想像一下:你加入一個新項目,深入研究程式碼庫,在最初的幾個小時內,你發現了一些令人沮喪的熟悉的東西。您會發現相同基本動畫的多個 @keyframes 定義分散在樣式表中。三種不同的淡入效果,兩種或三種幻燈片變化,一些縮放動畫,以及至少兩種不同的旋轉動畫,因為,好吧,為什麼不呢? @關鍵幀脈衝{ 從{ 規模:1; } 到{ 規模:1.1; } }
@keyframes 更大的脈衝 { 0%, 20%, 100% { 規模:1; } 10%, 40% { 規模:1.2; } }
如果這種情況聽起來很熟悉,那麼您並不孤單。根據我在各個專案中的經驗,我能提供的最一致的快速勝利之一是整合和標準化關鍵影格。它已經成為一種非常可靠的模式,我現在期待將這種清理作為我在任何新程式碼庫上的首要任務之一。 混亂背後的邏輯 仔細想想,這種冗餘是完全合理的。我們在日常工作中都使用相同的基本動畫:淡入淡出、幻燈片、縮放、旋轉和其他常見效果。這些動畫非常簡單,而且很容易快速建立 @keyframes 定義來完成工作。 如果沒有集中式動畫系統,開發人員自然會從頭開始編寫這些關鍵幀,而不會意識到程式碼庫中的其他地方已經存在類似的動畫。在基於組件的架構(現在我們大多數人都這樣做)中工作時,這種情況尤其常見,因為團隊經常在應用程式的不同部分並行工作。 結果呢?動畫混亂。 小問題 關鍵影格重複最明顯的問題是浪費開發時間和不必要的程式碼膨脹。多個關鍵影格定義意味著當需求改變時需要更新多個位置。需要調整淡入淡出動畫的時間嗎?您需要尋找程式碼庫中的每個實例。想要標準化緩動函數嗎?祝你好運找到所有的變化。維護點的增加使得即使是簡單的動畫更新也成為一項耗時的任務。 更大的問題 這種關鍵影格重複造成了一個潛伏在表面之下的更陰險的問題:全域範圍陷阱。即使使用基於元件的架構,CSS 關鍵影格也始終在全域範圍內定義。這意味著所有關鍵影格都適用於所有元件。總是。是的,您的動畫不一定使用您在組件中定義的關鍵影格。它使用與載入到全域範圍中的名稱完全相同的最後一個關鍵影格。 只要所有關鍵幀都相同,這似乎是一個小問題。但是,當您想要為特定用例自訂動畫時,您就會遇到麻煩,或者更糟的是,您將成為導致這些問題的人。 要么你的動畫無法工作,因為另一個組件在你的組件之後加載,覆蓋了你的關鍵幀,要么你的組件最後加載並意外地更改了使用該關鍵幀名稱的每個其他組件的動畫行為,而你甚至可能沒有意識到這一點。 這是一個演示該問題的簡單範例: . 組件一 { /* 組件樣式 */ 動畫:脈衝 1s 緩入緩出無限交替; }
/* 這個@keyframes 定義不起作用 */ @關鍵幀脈衝{ 從{ 規模:1; } 到{ 規模:1.1; } }
/* 後面的程式碼... */
. 組件二 { /* 組件樣式 */ 動畫:脈衝 1s 緩入緩出無限; }
/* 此關鍵影格將應用於兩個組件 */ @關鍵幀脈衝{ 0%, 20%, 100% { 規模:1; } 10%, 40% { 規模:1.2; } }
兩個元件使用相同的動畫名稱,但第二個 @keyframes 定義會覆寫第一個。現在,組件一和組件二都將使用第二個關鍵幀,無論哪個組件定義了哪個關鍵幀。 請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 1 [forked]。 最糟糕的部分?這通常在本地開發中完美運行,但在生產中當建置過程更改樣式表的載入順序時會神秘地中斷。您最終會得到表現不同的動畫,具體取決於加載的組件以及加載的順序。 解決方案:統一關鍵幀 這種混亂的答案出乎意料地簡單:預先定義的動態關鍵影格儲存在共享樣式表中。我們不是讓每個元件定義自己的動畫,而是創建有據可查、易於使用的集中式關鍵幀使用、可維護並根據您的專案的特定需求進行客製化。 將其視為關鍵幀標記。正如我們使用標記來表示顏色和間距,我們中的許多人已經使用標記來表示動畫屬性(例如持續時間和緩動函數),為什麼不也使用標記來表示關鍵影格呢? 這種方法可以自然地與您正在使用的任何當前設計令牌工作流程集成,同時解決小問題(程式碼重複)和更大的問題(全局範圍衝突)。 這個想法很簡單:為我們所有常見的動畫創建單一的事實來源。這個共享樣式表包含精心製作的關鍵幀,涵蓋了我們專案實際使用的動畫模式。不再猜測我們的程式碼庫中是否已經存在淡入淡出動畫。不再意外覆蓋其他組件的動畫。 但關鍵是:這些不僅僅是靜態的複製貼上動畫。它們被設計為動態的,並且可以透過 CSS 自訂屬性進行自訂,使我們能夠保持一致性,同時仍然可以靈活地使動畫適應特定的用例,例如,如果您需要在一個地方稍微更大的「脈衝」動畫。 建構第一個關鍵幀令牌 我們應該解決的第一個容易實現的目標之一是「淡入」動畫。在我最近的一個專案中,我發現了十多個單獨的淡入定義,是的,它們都只是將不透明度從 0 動畫到 1。 因此,讓我們建立一個新的樣式表,將其命名為 kf-tokens.css,將其匯入到我們的專案中,並在其中放置帶有適當註釋的關鍵影格。 /* 關鍵影格-tokens.css */
/* * Fade In - 淡入淡出動畫 * 用法:動畫:kf-fade-in 0.3s ease-out; */ @keyframes kf-淡入 { 從{ 不透明度:0; } 到{ 不透明度:1; } }
這個單一的 @keyframes 聲明取代了我們程式碼庫中所有分散的淡入動畫。乾淨、簡單且全球適用。現在我們已經定義了這個令牌,我們可以在整個專案的任何元件中使用它: .模態{ 動畫:kf-淡入 0.3 秒緩出; }
.工具提示{ 動畫:kf-fade-in 0.2s ease-in-out; }
.通知{ 動畫:kf-淡入 0.5 秒緩出; }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 2 [forked]。 注意:我們在所有 @keyframes 名稱中使用 kf- 前綴。此前綴用作命名空間,可防止與專案中現有動畫發生命名衝突,並立即清楚地表明這些關鍵影格來自我們的關鍵影格標記檔案。 製作動態投影片 kf 淡入關鍵影格效果很好,因為它很簡單,而且幾乎沒有空間搞亂事情。然而,在其他動畫中,我們需要更加動態,在這裡我們可以利用 CSS 自訂屬性的巨大威力。與分散的靜態動畫相比,這就是關鍵影格標記真正發揮作用的地方。 讓我們來看一個常見的場景:「滑入」動畫。但要從哪裡滑進去呢?距右側 100 像素?左邊50%?應該從螢幕頂部進入嗎?還是從底部漂進去?可能性有很多,但我們可以建立一個適應所有場景的靈活令牌,而不是為每個方向和每個變化創建單獨的關鍵影格: /* * Slide In - 定向滑動動畫 * 使用--kf-slide-from控制方向 * 預設:從左側滑入 (-100%) * 用法: * 動畫:kf-slide-in 0.3s ease-out; * --kf-slide-from:-100px 0; // 從左邊滑動 * --kf-slide-from: 100px 0; // 從右邊滑動 * --kf-slide-from: 0 -50px; // 從頂部滑動 */
@keyframes kf-slide-in { 從{ 翻譯:var(--kf-slide-from,-100% 0); } 到{ 翻譯:0 0; } }
現在,我們只需更改 --kf-slide-from 自訂屬性即可將這個單一 @keyframes 標記用於任何滑動方向: .側邊欄{ 動畫:kf-slide-in 0.3s 緩出; /* 使用預設值:從左側滑動 */ }
.通知{ 動畫:kf-slide-in 0.4s 緩出; --kf-slide-from: 0 -50px; /* 從頂部滑動 */ }
.模態{ 動畫: kf-淡入0.5秒, kf-滑入 0.5s 三次貝塞爾曲線(0.34, 1.56, 0.64, 1); --kf-slide-from: 50px 50px; /* 從右下角滑動 */ }
這種方法為我們提供了令人難以置信的靈活性,同時保持一致性。一個關鍵幀聲明,無限可能。 請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 3 [forked]。 如果我們想讓我們的動畫更靈活,也允許「滑出」效果,我們可以只需新增 --kf-slide-to 自訂屬性,類似於我們將在下一節中看到的內容。 雙向縮放關鍵幀 跨項目重複的另一個常見動畫是「縮放」效果。無論是 Toast 訊息的微妙放大、模態的戲劇性放大,還是標題的溫和縮小效果,縮放動畫無處不在。 讓我們建立一組靈活的 kf-zoom 關鍵幀,而不是為每個比例值創建單獨的關鍵幀:
/* * 縮放動畫 * 使用 --kf-zoom-from 和 --kf-zoom-to 控制比例值 * 預設:從 80% 縮放到 100%(0.8 到 1) * 用法: * 動畫:kf-zoom 0.2s 緩出; * --kf-zoom-from: 0.5; --kf-縮放至:1; // 從 50% 縮放到 100% * --kf-zoom-from: 1; --kf-縮放至:0; // 從 100% 縮放到 0% * --kf-zoom-from: 1; --kf-縮放至:1.1; // 從 100% 縮放到 110% */
@keyframes kf-zoom { 從{ 比例尺:var(--kf-zoom-from,0.8); } 到{ 比例:var(--kf-zoom-to, 1); } }
透過一個定義,我們可以實現我們需要的任何縮放變更: .吐司{ 動畫: kf-滑入0.2s, kf-zoom 0.4s 緩出; --kf-幻燈片-來自:0 100%; /* 從頂部滑動 */ /* 使用預設縮放:從 80% 縮放到 100% */ }
.模態{ 動畫:kf-zoom 0.3s 三次貝塞爾曲線(0.34, 1.56, 0.64, 1); --kf-縮放-來自:0; /* 從 0% 到 100% 的戲劇性縮放 */ }
.標題{ 動畫: kf-淡入 2 秒, kf-zoom 2s 緩入; --kf-zoom-來自:1.2; --kf-縮放至:0.8; /* 溫和縮小 */ }
預設值 0.8 (80%) 非常適合大多數 UI 元素,例如 Toast 訊息和卡片,同時仍可輕鬆針對特殊情況進行自訂。 請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 4 [forked]。 您可能已經注意到最近範例中的一些有趣的事情:我們一直在組合動畫。使用 @keyframes 令牌的主要優勢之一是它們被設計為彼此無縫整合。這種流暢的構圖是有意為之,而非偶然。 稍後我們將更詳細地討論動畫合成,包括它們可能出現問題的地方,但大多數組合都是簡單且易於實現的。 注意:在寫這篇文章時,也許是因為寫它,我發現自己重新思考了入口動畫的整個想法。隨著 CSS 的最新進展,我們還需要它們嗎?幸運的是,亞當·阿蓋爾探討了同樣的問題,並在他的部落格中精彩地表達了這些問題。這與此處所寫的內容並不矛盾,但它確實提供了一種值得考慮的方法,特別是如果您的專案嚴重依賴入口動畫。 連續動畫 雖然入口動畫(如“淡入淡出”、“滑動”和“縮放”)發生一次然後停止,但連續動畫會無限循環以吸引註意力或指示正在進行的活動。我遇到的兩個最常見的連續動畫是“旋轉”(用於加載指示器)和“脈衝”(用於突出顯示重要元素)。 這些動畫在創建關鍵影格標記時提出了獨特的挑戰。與通常從一種狀態進入另一種狀態的入口動畫不同,連續動畫的行為模式需要高度可自訂。 旋轉醫生 每個項目似乎都使用多個旋轉動畫。有些順時針旋轉,有些逆時針旋轉。有些會進行一次 360 度旋轉,有些會進行多次旋轉以獲得更快的效果。讓我們建立一個可以處理所有場景的靈活旋轉,而不是為每個變體創建單獨的關鍵影格:
/* * Spin - 旋轉動畫 * 使用--kf-spin-from和--kf-spin-to控制旋轉範圍 * 使用--kf-spin-turns控制旋轉量 * 預設:從 0 度旋轉到 360 度(1 個完整旋轉) * 用法: * 動畫:kf-spin 1s線性無限; * --kf-自旋轉彎:2; // 2 次完整旋轉 * --kf-spin-from: 0deg; --kf-旋轉:180°; // 半旋轉 * --kf-spin-from: 0deg; --kf-旋轉至:-360°; // 逆時針方向 */
@keyframes kf-spin { 從{ 旋轉:var(--kf-spin-from, 0deg); } 到{ 旋轉: calc(var(--kf-spin-from, 0deg) + var(--kf-spin-to, 360deg) * var(--kf-spin-turns, 1)); } }
現在我們可以創建任何我們喜歡的旋轉變化:
.loading-spinner { 動畫:kf-spin 1s線性無限; /* 使用預設值:從 0deg 旋轉到 360deg */ }
.fast-loader { 動畫:kf-spin 1.2s 緩入無限交替; --kf-旋轉匝數:3; /* 每個週期每個方向 3 次完整旋轉*/ }
.steped-reverse { 動畫:kf-spin 1.5s steps(8) 無限; --kf-旋轉至:-360°; /* 逆時針 */ }
.微妙的擺動{ 動畫:kf-spin 2s 緩入出無限交替; --kf-spin-from:-16deg; --kf-旋轉至:32°; /* 擺動 36 度:-18 度和 +18 度之間 */ }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 5 [forked]。 這種方法的優點在於,相同的關鍵影格可用於載入旋轉器、旋轉圖示、擺動效果,甚至複雜的多回合動畫。 脈衝悖論 脈衝動畫比較棘手,因為它們可以「脈衝」不同的屬性。有些脈衝比例,其他脈衝不透明度,還有一些脈衝顏色屬性,如亮度或飽和度。我們可以建立適用於任何 CSS 屬性的關鍵幀,而不是為每個屬性建立單獨的關鍵幀。 以下是具有比例和不透明度選項的脈衝關鍵影格範例:
/* * 脈衝 - 脈衝動畫 * 使用 --kf-pulse-scale-from 和 --kf-pulse-scale-to 控制比例範圍 * 使用 --kf-pulse-opacity-from 和 --kf-pulse-opacity-to 控制不透明度範圍 * 預設值:無脈衝(所有值均為 1) * 用法: * 動畫:kf-pulse 2s 緩緩出入無限交替; * --kf-脈衝比例-來自:0.95; --kf-脈衝比例-至:1.05; // 縮放脈衝 * --kf-脈衝不透明度-來自:0.7; --kf-脈衝不透明度-to:1; // 不透明度脈衝 */
@keyframes kf-脈衝 { 從{ 比例:var(--kf-pulse-scale-from, 1); 不透明度:var(--kf-pulse-opacity-from, 1); } 到{ 比例:var(--kf-pulse-scale-to, 1); 不透明度:var(--kf-pulse-opacity-to, 1); } }
這將創建一個靈活的脈衝,可以為多個屬性設定動畫: .號召性用語{ 動畫:kf-pulse 0.6s無限交替; --kf-脈衝不透明度-來自:0.5; /* 不透明度脈衝 */ }
.通知點{ 動畫:kf-pulse 0.6s 緩緩緩出無限交替; --kf-脈衝比例-來自:0.9; --kf-脈衝比例-至:1.1; /* 縮放脈衝 */ }
.文字標示{ 動畫:kf-pulse 1.5s 無限緩出; --kf-脈衝比例-來自:0.8; --kf-脈衝不透明度-來自:0.2; /* 縮放和不透明度脈衝 */ }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 6 [forked]。 這個單一的 kf-pulse 關鍵影格可以處理從微妙的注意力吸引到引人注目的亮點的一切,同時易於自訂。 高級緩動 使用關鍵幀令牌的好處之一是可以輕鬆擴展我們的動畫庫並提供大多數開發人員不會費心從頭開始編寫的效果,例如彈性或彈跳。 下面是一個簡單的「彈跳」關鍵影格標記範例,它使用 --kf-bounce-from 自訂屬性來控制跳躍高度。 /* * Bounce - 彈跳入口動畫 * 使用--kf-bounce-from控制跳躍高度 * 預設:從 100vh 跳轉(螢幕外) * 用法: * 動畫:kf-bounce 3s 緩入; * --kf-bounce-from: 200px; // 從200px高度跳轉 */
@keyframes kf-bounce { 0% { 譯:0 calc(var(--kf-bounce-from, 100vh) * -1); }
34% { 譯:0 calc(var(--kf-bounce-from, 100vh) * -0.4); }
55% { 譯:0 calc(var(--kf-bounce-from, 100vh) * -0.2); }
72% { 譯:0 calc(var(--kf-bounce-from, 100vh) * -0.1); }
85% { 譯:0 calc(var(--kf-bounce-from, 100vh) * -0.05); }
94% { 譯:0 calc(var(--kf-bounce-from, 100vh) * -0.025); }
99% { 譯:0 calc(var(--kf-bounce-from, 100vh) * -0.0125); }
22%、45%、64%、79%、90%、97%、100% { 翻譯:0 0; 動畫計時功能:緩出; } }
由於關鍵影格內的計算,像「彈性」這樣的動畫有點棘手。我們需要分別定義 --kf-elastic-from-X 和 --kf-elastic-from-Y (兩者都是可選的),它們一起讓我們從螢幕上的任何點創建一個彈性入口。
/* * Elastic In - 彈性入口動畫 * 使用 --kf-elastic-from-X 和 --kf-elastic-from-Y 控制起始位置 * 預設:從頂部中心進入(0,-100vh) * 用法: * 動畫:kf-elastic-in 2s 緩緩出出兩者; * --kf-elastic-from-X:-50px; * --kf-elastic-from-Y: -200px; // 從(-50px,-200px)輸入 */
@keyframes kf-elastic-in { 0% { 譯: calc(var(--kf-elastic-from-X, -50vw) * 1) calc(var(--kf-elastic-from-Y, 0px) * 1); }
16% { 譯: calc(var(--kf-elastic-from-X, -50vw) * -0.3227) calc(var(--kf-elastic-from-Y, 0px) * -0.3227); }
28% { 譯: calc(var(--kf-elastic-from-X, -50vw) * 0.1312)計算(var(--kf-elastic-from-Y,0px)* 0.1312); }
44% { 譯: calc(var(--kf-elastic-from-X, -50vw) * -0.0463) calc(var(--kf-elastic-from-Y, 0px) * -0.0463); }
59% { 譯: calc(var(--kf-elastic-from-X, -50vw) * 0.0164) calc(var(--kf-elastic-from-Y, 0px) * 0.0164); }
73% { 譯: calc(var(--kf-elastic-from-X, -50vw) * -0.0058) calc(var(--kf-elastic-from-Y, 0px) * -0.0058); }
88% { 譯: calc(var(--kf-elastic-from-X, -50vw) * 0.0020) calc(var(--kf-elastic-from-Y, 0px) * 0.0020); }
100% { 翻譯:0 0; } }
這種方法使我們可以輕鬆地在整個專案中重複使用和自訂高級關鍵幀,只需更改單個自訂屬性即可。
.bounce-and-zoom { 動畫: kf-bounce 3s 緩入, kf-zoom 3s 線性; --kf-縮放-來自:0; }
.bounce-and-slide { 動畫合成:新增; /* 兩個動畫都使用翻譯 */ 動畫: kf-bounce 3s 緩入, kf-滑入 3 秒緩出; --kf-幻燈片-來自:-200px; }
.elastic-in { 動畫:kf-elastic-in 2s 緩緩出出兩者; }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 7 [forked]。 到目前為止,我們已經了解瞭如何以智慧且高效的方式整合關鍵影格。當然,您可能想要調整一些內容以更好地滿足您的專案需求,但我們已經介紹了幾種常見動畫和日常用例的範例。有了這些關鍵幀令牌,我們現在就擁有了強大的構建塊,可以在整個專案中創建一致、可維護的動畫。不再有重複的關鍵幀,不再有全域範圍衝突。這是一種乾淨、方便的方式來處理我們所有的動畫需求。 但真正的問題是:我們如何將這些構建塊組合在一起? 把它們放在一起 我們已經看到,組合基本關鍵影格標記很簡單。我們不需要任何特殊的東西,只是定義第一個動畫,定義第二個動畫,根據需要設定變量,就是這樣。 /* 淡入+滑入 */ .吐司{ 動畫: kf-淡入0.4秒, kf-滑入 0.4s 三次貝塞爾曲線(0.34, 1.56, 0.64, 1); --kf-slide-from: 0 40px; }
/* 放大+淡入 */ .模態{ 動畫: kf-淡入0.3秒, kf-zoom 0.3s 三次貝塞爾曲線(0.34, 1.56, 0.64, 1); --kf-zoom-from:0.7; --kf-縮放至:1; }
/* 滑入 + 脈衝 */ .通知{ 動畫: kf-滑入0.5s, kf-pulse 1.2s 緩入緩出無限交替; --kf-slide-from:-100px 0; --kf-脈衝比例-來自:0.95; --kf-脈衝比例-至:1.05; }
這些組合效果很好,因為每個動畫都針對不同的屬性:不透明度、變換(平移/縮放)等。但有時會出現衝突,我們需要知道為什麼以及如何處理它們。 當兩個動畫嘗試對相同屬性進行動畫處理時(例如,都對比例進行動畫處理或同時對不透明度進行動畫處理),結果將不是您所期望的。預設情況下,只有一個動畫實際應用於該屬性,即動畫清單中的最後一個。這是 CSS 如何處理同一屬性上的多個動畫的限制。 例如,這不會按預期工作,因為僅套用 kf 脈衝動畫。 .壞組合{ 動畫: kf-向前變焦0.5秒, kf-脈衝1.2s無限交替; --kf-zoom-from:0.5; --kf-縮放至:1.2; --kf-脈衝比例-來自:0.8; --kf-脈衝比例-至:1.1; }
動畫新增 處理影響相同屬性的多個動畫的最簡單、最直接的方法是使用animation-composition 屬性。在上面的最後一個範例中,kf-pulse 動畫取代了 kf-zoom 動畫,因此我們不會看到初始縮放,也不會獲得預期的 1.2 比例。 透過設定要新增的動畫組合,我們告訴瀏覽器組合兩個動畫。這給了我們我們想要的結果。 . 組件二 { 動畫合成:新增; }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 8 [forked]。 對於我們想要組合對相同屬性的效果的大多數情況,此方法效果很好。當我們需要將動畫與靜態屬性值結合時,它也很有用。 例如,如果我們有一個元素使用 translate 屬性將其精確定位在我們想要的位置,然後我們想使用 kf-slide-in 關鍵幀對其進行動畫處理,那麼我們會在沒有動畫合成的情況下得到令人討厭的可見跳躍。 請參閱 Amit Sheen 的筆關鍵影格標記 - 示範 9 [forked]。 透過添加動畫合成設置,動畫可以與現有的平滑結合變換,因此元素保持在原位並按預期進行動畫處理。 動畫交錯 處理多個動畫的另一種方法是「交錯」它們——也就是說,在第一個動畫完成後稍微開始第二個動畫。這並不是一個適用於所有情況的解決方案,但是當我們有一個入口動畫後面跟著一個連續動畫時,它很有用。 /* 淡入 + 不透明度脈衝 */ .通知{ 動畫: kf-淡入 2 秒緩出, kf-pulse 0.5s 2s 緩緩緩出無限交替; --kf-脈衝不透明度-to:0.5; }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 10 [forked]。 訂單事宜 我們使用的大部分動畫都使用變換屬性。在大多數情況下,這更加方便。它還具有性能優勢,因為變換動畫可以透過 GPU 加速。但如果我們使用變換,我們需要接受執行變換的順序很重要。很多。 到目前為止,在我們的關鍵影格中,我們使用了單獨的變換。根據規範,這些總是以固定的順序應用:首先,元素進行平移,然後旋轉,然後縮放。這是有道理的,也是我們大多數人所期望的。 但是,如果我們使用變換屬性,則函數的編寫順序就是它們的應用順序。在這種情況下,如果我們在 X 軸上移動某個物體 100 像素,然後將其旋轉 45 度,這與先將其旋轉 45 度然後移動 100 像素不同。 /* 粉紅方塊:先平移,再旋轉 */ .example-one { 變換:translateX(100px) 旋轉(45deg); }
/* 綠色方塊:先旋轉,再平移 */ .example-2 { 變換:旋轉(45度)平移X(100像素); }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 11 [forked]。 但根據變換順序,所有單獨的變換(我們用於關鍵影格標記的所有內容)都發生在變換函數之前。這意味著您在變換屬性中設定的任何內容都將在動畫之後發生。但是,例如,如果您將平移與 kf-spin 關鍵影格一起設置,則平移將在動畫之前發生。還困惑嗎? 這會導致靜態值可能導致相同動畫產生不同結果的情況,如下例所示:
/* 兩個旋轉器的通用動畫 */ .spinner { 動畫:kf-spin 1s線性無限; }
/* 粉紅色旋轉器:旋轉前平移(單獨變換)*/ .spinner-粉紅色{ 譯:100% 50%; }
/* 綠色旋轉器:旋轉然後平移(函數順序)*/ .spinner-green { 變換:翻譯(100%,50%); }
請參閱 Amit Sheen 的筆關鍵影格令牌 - 示範 12 [forked]。 您可以看到第一個旋轉器(粉紅色)在 kf-spin 旋轉之前發生平移,因此它首先移動到其位置,然後旋轉。第二個旋轉器(綠色)獲得一個在單獨變換之後發生的 translate() 函數,因此元素首先旋轉,然後相對於其當前角度移動,我們得到了寬軌道效果。 不,這不是一個錯誤。這只是我們在處理多個動畫或多個變換時需要了解 CSS 並牢記的事情之一。如果需要,您也可以建立一組附加的 kf-spin-alt 關鍵幀,使用rotate() 函數旋轉元素。 減少運動 當我們談論替代關鍵影格時,我們不能忽視「無動畫」選項。使用關鍵幀令牌的最大優點之一是可以嵌入可訪問性,而且實際上很容易做到。透過在設計關鍵影格時考慮到可訪問性,我們可以確保喜歡減少運動的使用者獲得更流暢、更少干擾的體驗,而無需額外的工作或程式碼重複。 「簡化運動」的確切含義可能會因一個動畫和另一個動畫以及不同項目而略有不同,但以下是需要記住的一些重要要點: 靜音關鍵幀 雖然某些動畫可以軟化或減慢,但有些動畫在要求減少運動時應該會完全消失。脈衝動畫就是一個很好的例子。為了確保這些動畫不會在簡化運動模式下運行,我們可以簡單地將它們包裝在適當的媒體查詢中。
@media(更喜歡減少運動:無偏好){ @keyfrmaes kf-脈衝 { 從{ 比例:var(--kf-pulse-scale-from, 1); 不透明度:var(--kf-pulse-opacity-from, 1); } 到{ 比例:var(--kf-pulse-scale-to, 1); 不透明度:var(--kf-脈衝不透明度-to, 1); } } }
這可以確保將prefers-reduced-motion 設為reduce 的使用者不會看到動畫,並且會獲得符合其偏好的體驗。 即時進入 有一些關鍵影格我們不能簡單地刪除,例如入口動畫。價值必須改變,必須有活力;否則,該元素將不會具有正確的值。但在減速運動中,從初始值的這種轉變應該是即時的。 為了實現這一點,我們將定義一組額外的關鍵幀,其中值立即跳到最終狀態。這些成為我們的預設關鍵影格。然後,我們將在媒體查詢中添加常規關鍵幀,將偏好減少運動設定為無偏好,就像前面的範例一樣。 /* 立即彈出以減少運動 */ @keyframes kf-zoom { 從,到{ 比例:var(--kf-zoom-to, 1); } }
@media(更喜歡減少運動:無偏好){ /* 原始縮放關鍵影格 */ @keyframes kf-zoom { 從{ 比例尺:var(--kf-zoom-from,0.8); } 到{ 比例:var(--kf-zoom-to, 1); } } }
這樣,喜歡減少運動的用戶將看到元素立即以其最終狀態出現,而其他人則獲得動畫過渡。 軟方法 在某些情況下,我們確實希望保持一些運動,但比原始動畫更柔和、更平靜。例如,我們可以用柔和的淡入來代替反彈入口。
@keyframes kf-bounce { /* 軟淡入可減少運動 */ }
@media(更喜歡減少運動:無偏好){ @keyframes kf-bounce { /* 原始彈跳關鍵影格 */ } }
現在,啟用減少運動的用戶仍然可以獲得外觀感,但沒有彈跳或彈性動畫的強烈運動。 構建塊就位後,下一個問題是如何使它們成為實際工作流程的一部分。編寫靈活的關鍵影格是一回事,但要讓它們在大型專案中可靠,需要一些我必須艱難學習的策略。 實施策略和最佳實踐 一旦我們擁有了可靠的關鍵幀標記庫,真正的挑戰是如何將它們帶入日常工作中。
人們很想立即刪除所有關鍵影格並宣布問題已解決,但在實踐中我發現最好的結果來自逐步採用。從最常見的動畫開始,例如淡入淡出或幻燈片。這些都是輕鬆的勝利,無需進行大量重寫即可顯示出立竿見影的價值。 命名是另一個值得關注的點。一致的前綴或命名空間可以清楚地表明哪些動畫是令牌,哪些是本地一次性動畫。它還可以防止意外碰撞,並幫助新團隊成員一目了然地識別共享系統。 文件與程式碼本身一樣重要。即使每個關鍵影格標記上方的簡短註釋也可以節省以後數小時的猜測時間。開發人員應該能夠打開令牌文件,掃描他們需要的效果,並將使用模式直接複製到他們的元件中。 靈活性使得這種方法值得付出努力。透過公開合理的自訂屬性,我們為團隊提供了在不破壞系統的情況下調整動畫的空間。同時,盡量不要過於複雜化。提供重要的旋鈕,讓其餘的保持固執己見。 最後,記住可訪問性。並非每個動畫都需要減少運動替代方案,但許多動畫都需要。儘早進行這些調整意味著我們以後不必再對其進行改造,並且它顯示出我們的用戶即使從未提及也會注意到的謹慎程度。
根據我的經驗,將關鍵幀標記視為我們設計標記工作流程的一部分是它們堅持下去的原因。一旦它們就位,它們就不再感覺像是特效,而是成為設計語言的一部分,是產品移動和回應方式的自然延伸。 總結 動畫可能是建立介面中最有趣的部分之一,但如果沒有結構,它們也可能成為最大的挫折感來源之一。將關鍵影格視為令牌,您可以將通常混亂且難以管理的內容轉變為清晰、可預測的系統。 真正的價值不僅在於節省幾行程式碼。相信當您使用淡入淡出、滑動、縮放或旋轉時,您可以準確地知道它在整個專案中的表現。這是來自自訂屬性的靈活性,而不是無休止的變化所帶來的混亂。它是內建在基礎中的可訪問性,而不是添加為事後的想法。 我已經看到這些想法在不同的團隊和不同的程式碼庫中發揮作用,而且模式總是相同的。 一旦標記就位,關鍵影格就不再是分散的技巧集合,而是成為設計語言的一部分。它們讓產品感覺更有意、更一致、更有活力。 如果您從本文中學到一件事,那就是:動畫值得我們對顏色、版面和間距給予相同的關注和結構。每次介面移動時,關鍵幀令牌的少量投資都會得到回報。