建構分割文字動畫

瞭解如何建立分割字母和字詞動畫的基礎總覽。

在這篇文章中,我想分享一些網頁的分割文字動畫和互動問題,對於如何簡化、易於使用且跨瀏覽器運作的解決方式。試試示範

示範

如果你偏好使用影片,也可以觀看這篇 YouTube 文章:

總覽

分割文字動畫令人驚豔。本文中幾乎不會充滿動畫潛力,但提供了建構基礎的基礎。製作動畫的目標是循序漸進呈現動畫效果。由於動畫是以頂端為基礎,因此文字應預設為清晰可讀。分割文字動態效果可能會變得非常過雜,並可能造成乾擾,因此只有在使用者接受動態時,我們才會調整 HTML 或套用動態樣式。

以下是工作流程和結果的概要說明:

  1. 針對 CSS 和 JS ,準備縮減動態條件變數。
  2. 準備在 JavaScript 中使用分割文字公用程式。
  3. 在網頁載入時調度條件和公用程式。
  4. 使用字母和字詞 (雷得部分) 撰寫 CSS 轉場效果和動畫。

以下是我們將產生的條件式結果預覽畫面:

Chrome 開發人員工具的螢幕截圖,開啟「Elements」面板,並將動態效果設為「reduce」,而且 h1 顯示為未分割
使用者偏好較慢動作:文字清晰易讀 / 未分割

如果使用者偏好較慢的動態效果,我們只保留 HTML 文件,不添加動畫。如果動作安全,我們會直接切入片段。以下是 JavaScript 按字母分割文字後的 HTML 預覽畫面。

Chrome 開發人員工具的螢幕截圖,開啟「Elements」面板,並將動態效果設為「reduce」,而且 h1 顯示為未分割
使用者保持同意,可將文字分割為多個 <span> 元素

正在準備動作條件式

這項專案中的 CSS 和 JavaScript 將會使用方便可用@media (prefers-reduced-motion: reduce) 媒體查詢。這項媒體查詢是我們決定分割文字時的主要條件式。CSS 媒體查詢會用來保留轉換和動畫,而 JavaScript 媒體查詢則會用於保留 HTML 操作。

準備 CSS 條件式

我使用 PostCSS 來啟用「Media 查詢 Level 5」的語法,可以將媒體查詢布林值儲存至變數:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

準備 JS 條件式

在 JavaScript 中,瀏覽器提供了檢查媒體查詢的方式,我利用銷毀功能,從媒體查詢檢查中擷取並重新命名布林值結果:

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

然後,我可以測試 motionOK,並且只在使用者未要求減少動作時變更文件。

if (motionOK) {
  // document split manipulations
}

我可以使用 PostCSS 啟用 Nesting Draft 1@nest 語法,藉此查看相同的值。這樣就能將動畫的所有邏輯,以及父項和子項的樣式要求儲存在單一位置:

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

只要使用 PostCSS 自訂屬性和 JavaScript 布林值,即可有條件升級效果。這帶我們進入下一節,也就是我詳細討論 JavaScript 以將字串轉換為元素。

正在分割文字

文字字母、字詞、行等不能使用 CSS 或 JS 個別製作動畫。 為了達到這個效果,我們需要裝上盒。如果想為每個字母加上動畫效果 每個字母都必須是元素此外,如果想為每個字詞建立動畫 每個字詞都必須是元素

  1. 建立 JavaScript 公用程式函式,將字串分割成元素
  2. 自動化調度管理這些公用程式的用法

分割字母公用程式函式

一個有趣的起始位置是函式,這個函式會接收字串並傳回陣列中的每個字母。

export const byLetter = text =>
  [...text].map(span)

ES6 的分散語法確實有助於完成這項作業。

分割字詞公用程式函式

這個函式與分割字母類似,會以字串的形式傳回陣列中的每個字詞。

export const byWord = text =>
  text.split(' ').map(span)

JavaScript 字串上的 split() 方法可讓我們指定要切割的字元。我傳送了空白處,表示字詞間分割。

設定方塊公用程式函式

這個效果需要每個字母的方塊,而我們在這些函式中發現 map() 是使用 span() 函式呼叫。以下是 span() 函式。

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

請務必注意,您正在設定名為 --index 的自訂屬性與陣列位置。保留字母動畫的方塊固然很棒,但如果在 CSS 中使用索引,新增的索引其實不大,影響也很大。這種大型影響最值得注意的是交錯。我們能夠使用 --index,以使動畫變換的外觀。

公用程式結論

完成的 splitting.js 模組:

const span = (text, index) => {
  const node = document.createElement('span')

  node.textContent = text
  node.style.setProperty('--index', index)

  return node
}

export const byLetter = text =>
  [...text].map(span)

export const byWord = text =>
  text.split(' ').map(span)

接下來,請匯入及使用以下 byLetter()byWord() 函式。

分割自動化調度管理

已可使用分割公用程式後,請將所有工具彙整在一起,以便:

  1. 尋找要分割的元素
  2. 分割這些內容並以 HTML 取代文字

接著,CSS 就會接管元素,並以動畫形式顯示元素 / 方塊。

尋找元素

我選擇使用屬性和值來儲存所需動畫的資訊,以及如何分割文字的資訊。我喜歡在 HTML 中加入這些宣告式選項JavaScript 屬性 split-by 可用於尋找元素,以及建立字母或字詞的方塊。在 CSS 中使用 letter-animationword-animation 屬性來指定元素子項,並套用轉換和動畫。

以下 HTML 範例示範了這兩個屬性:

<h1 split-by="letter" letter-animation="breath">animated letters</h1>
<h1 split-by="word" word-animation="trampoline">hover the words</h1>

從 JavaScript 尋找元素

我使用 CSS 選取器語法收集屬性,收集要分割文字的元素清單:

const splitTargets = document.querySelectorAll('[split-by]')

尋找 CSS 中的元素

我也使用 CSS 中的屬性狀態選取器,為所有字母動畫提供相同的基本樣式。稍後,我們會使用屬性值新增更多具體樣式,達成效果。

letter-animation {
  @media (--motionOK) {
    /* animation styles */
  }
}

正在分割現有文字

我們會針對在 JavaScript 中找到的每個分割目標,根據屬性的值分割文字,並將每個字串對應至 <span>。接著,我們就能將元素文字替換成我們製作的方塊:

splitTargets.forEach(node => {
  const type = node.getAttribute('split-by')
  let nodes = null

  if (type === 'letter') {
    nodes = byLetter(node.innerText)
  }
  else if (type === 'word') {
    nodes = byWord(node.innerText)
  }

  if (nodes) {
    node.firstChild.replaceWith(...nodes)
  }
})

協調結論

已完成的 index.js

import {byLetter, byWord} from './splitting.js'

const {matches:motionOK} = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

if (motionOK) {
  const splitTargets = document.querySelectorAll('[split-by]')

  splitTargets.forEach(node => {
    const type = node.getAttribute('split-by')
    let nodes = null

    if (type === 'letter')
      nodes = byLetter(node.innerText)
    else if (type === 'word')
      nodes = byWord(node.innerText)

    if (nodes)
      node.firstChild.replaceWith(...nodes)
  })
}

JavaScript 能以下列英文判讀:

  1. 匯入一些輔助公用程式函式。
  2. 如果不採取任何行動,檢查能否對此使用者執行動作。
  3. 針對要分割的每個元素建立範本。
    1. 根據想要的分割方式進行分割。
    2. 將文字替換成元素。

分割動畫和轉場效果

上述分割文件操縱方式剛剛成功運用 CSS 或 JavaScript 實現了各種可能的動畫和效果。本文下方提供幾個連結,有助於激發您拆分想法的靈感。

是時候展現成果了!我將分享 4 種 CSS 驅動的動畫和轉場效果🤓

分割字母

我發現以下 CSS 能派上用場,成為分割字母效果的基礎。我會將所有轉場效果和動畫放在動態媒體查詢後方,然後為每個新的子字母 span 提供顯示屬性,以及一個使用空白字元的樣式:

[letter-animation] > span {
  display: inline-block;
  white-space: break-spaces;
}

空白聊天室樣式十分重要,因此版面配置引擎不會收合僅為空格的跨距。現在來看看有狀態有趣的東西。

轉場效果的分割字母範例

這個範例使用 CSS 轉換為分割文字效果。使用轉場效果時,必須要顯示不同狀態,引擎才能建立動畫效果,而我選擇了三個狀態:沒有懸停、懸停在語句上,然後懸停在字母上。

當使用者將滑鼠遊標懸停在句子 (即容器) 時,我回頭將整個子項向上移,視同使用者將其移遠離去。接著,當使用者將滑鼠遊標懸停在字母上

@media (--motionOK) {
  [letter-animation="hover"] {
    &:hover > span {
      transform: scale(.75);
    }

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:hover {
        transform: scale(1.25);
      }
    }
  }
}

分割字母的動畫範例

這個範例使用預先定義的 @keyframe 動畫,為每個字母無限次加上動畫效果,並利用內嵌自訂屬性索引建立交錯效果。

@media (--motionOK) {
  [letter-animation="breath"] > span {
    animation:
      breath 1200ms ease
      calc(var(--index) * 100 * 1ms)
      infinite alternate;
  }
}

@keyframes breath {
  from {
    animation-timing-function: ease-out;
  }
  to {
    transform: translateY(-5px) scale(1.25);
    text-shadow: 0 0 25px var(--glow-color);
    animation-timing-function: ease-in-out;
  }
}

分割字詞

在這些範例中,Flexbox 把 Flexbox 當成容器類型,順利利用 ch 單位做為健康的間隔長度。

word-animation {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 1ch;
}
顯示字詞之間差距的 Flexbox 開發人員工具

轉換分割字詞範例

在此轉換範例中,我再次使用懸停功能。由於效果一開始會隱藏內容,直到遊標懸停為止,因此我確定只有在裝置具有懸停功能時,才會套用互動和樣式。

@media (hover) {
  [word-animation="hover"] {
    overflow: hidden;
    overflow: clip;

    & > span {
      transition: transform .3s ease;
      cursor: pointer;

      &:not(:hover) {
        transform: translateY(50%);
      }
    }
  }
}

以動畫呈現分段字詞範例

在這個動畫範例中,我再次使用 CSS @keyframes,為一般的文字段落建立交錯式的無限動畫。

[word-animation="trampoline"] > span {
  display: inline-block;
  transform: translateY(100%);
  animation:
    trampoline 3s ease
    calc(var(--index) * 150 * 1ms)
    infinite alternate;
}

@keyframes trampoline {
  0% {
    transform: translateY(100%);
    animation-timing-function: ease-out;
  }
  50% {
    transform: translateY(0);
    animation-timing-function: ease-in;
  }
}

結語

現在你知道我怎麼做,你會怎麼做?🙂

讓我們帶您更多元的方法,並瞭解運用網路打造網站的所有方式。 建立 Codepen 或代管自己的示範影片,透過 Twitter 推文附上,我會將程式碼加到下方的「社群重混」部分。

來源

更多示範和靈感

社群重混作品