瞭解如何建立分割字母和文字動畫的基礎總覽。
在這篇文章中,我想分享解決方案,讓網頁的文字動畫和互動效果分割,以便在各瀏覽器中運作,並且可供使用者存取。試用示範模式。
如果你偏好觀看影片,請參閱這篇文章的 YouTube 版本:
總覽
分割文字動畫效果非常出色。這篇文章僅會略微介紹動畫的潛力,但會提供基礎知識,讓您進一步發揮創意。目標是逐步加入動畫。文字應預設可供閱讀,動畫則會疊加在文字上方。分割文字動畫效果可能會變得過度誇張,甚至可能會造成干擾,因此我們只會在使用者同意的情況下,操控 HTML 或套用動畫樣式。
以下是工作流程和結果的概要說明:
以下是我們要取得的條件式結果預覽畫面:
如果使用者偏好減少動畫效果,我們會保留 HTML 文件,不加入任何動畫。如果動作正常,我們會繼續將其切成小塊。以下是 JavaScript 將文字以字母分割後的 HTML 預覽畫面。
準備動作條件
這個專案會使用方便的可用 @media
(prefers-reduced-motion: reduce)
媒體查詢,從 CSS 和 JavaScript 中使用。這個媒體查詢是我們決定是否要將文字分割的主要條件。CSS 媒體查詢會用於暫緩轉場效果和動畫,而 JavaScript 媒體查詢會用於暫緩 HTML 操作。
準備 CSS 條件
我使用 PostCSS 啟用 Media Queries 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 啟用巢狀草稿 1 的 @nest
語法,藉此檢查相同的值。這樣一來,我就能將所有動畫相關邏輯和父項與子項的樣式需求,集中儲存在同一個位置:
letter-animation {
@media (--motionOK) {
/* animation styles */
}
}
有了 PostCSS 自訂屬性和 JavaScript 布林值,我們就可以依條件升級效果。接下來,我們將進入下一節,我會分解用於將字串轉換為元素的 JavaScript。
分割文字
CSS 或 JS 不能分別使用文字字母、字詞和行等動畫。為了達到這個效果,我們需要使用方塊。如果我們想為每個字母製作動畫,則每個字母都必須是元素。如果我們想為每個字詞製作動畫,則每個字詞都必須是元素。
- 建立 JavaScript 公用程式函式,將字串分割成元素
- 協調這些公用程式的使用
分割字母公用函式
建議您從一個函式開始,這個函式會接收字串,並傳回陣列中的每個字母。
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()
函式。
分割自動化調度管理
分割公用程式可供使用後,如果將所有值放在一起,代表:
- 找出要分割的元素
- 分割文字,並替換文字為 HTML
CSS 隨即會接手,為元素 / 方塊建立動畫。
尋找元素
我選擇使用屬性和值,儲存所需動畫的相關資訊,以及如何分割文字。我喜歡將這些宣告式選項放入 HTML。屬性 split-by
是從 JavaScript 使用,用於尋找元素,並為字母或字詞建立方塊。屬性 letter-animation
或 word-animation
是從 CSS 使用,用於指定元素子項並套用轉換和動畫。
以下是兩個屬性的 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 的英文讀法:
- 匯入一些輔助公用程式函式。
- 檢查這個使用者是否允許動作偵測,如果不允許,則不採取任何行動。
- 針對每個要分割的元素。
- 並根據他們想要的分割方式進行分割。
- 使用元素取代文字。
分割動畫和轉場效果
上述的分割文件操控功能,利用 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 可做為容器類型,並妥善運用 ch
單位做為適當的間距長度。
word-animation {
display: inline-flex;
flex-wrap: wrap;
gap: 1ch;
}
轉場分割字詞範例
在這個轉場範例中,我再次使用懸停效果。由於效果會在滑鼠游標移至上方之前隱藏內容,因此我確保只有在裝置具有滑鼠游標功能時,才會套用互動和樣式。
@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 或自行發布示範內容,然後透過推文傳送給我,我會將其新增至下方的社群重混內容部分。
來源
更多示範和靈感
社群重混作品
<text-hover>
CodeSandbox 上的 gnehcwu 網頁元件