CSS3 Animation
27 Aug 2017使用 CSS3 Transition 和 Animation 製作動畫、效能優化。
CSS3 製作動畫的方式有 2 種
以下分別述之。
Transition 漸變
Transition 讓物件能在特定時間內流暢地執行一連串動作,而非生硬地突然改變。
語法
使用 Transition 最重要的就是指出想要改變的屬性(Property)和時間(Duration)。
transition: property duration timing-function delay;
- Property:物件屬性,例如:長、寬、位置、字體大小、背景顏色。
- Duration:動畫時間。預設為 0,因此不指定就不產生效果。
- Timing Function:動畫進行的速度曲線,例如:ease(預設值)、linear、ease-in、ease-out、ease-in-out 或
cubic-bezier(n,n,n,n)
(自定義函數,範例)。關於自訂曲線可參考工具。 - Delay:延遲動畫開始的時間。
Browser Support
Chrome 26+、Firefox 16+、IE 10+、Safari 6.1+、Opera 12.1+,基本上所有主流瀏覽器皆支援。
範例 1
小方塊的寬度在一秒內由 100px 變成 200px,看起來就像是向右延伸了 100px。
程式碼。
.cube {
width: 100px;
height: 100px;
background: #41d2f2;
transition: width 1s;
&.move {
width: 200px;
}
}
範例 2
小方塊向右移動 200px,並且旋轉 360 度。
程式碼。
.cube {
width: 100px;
height: 100px;
background: #41d2f2;
transition: transform 1s;
&.move {
transform: translate3d(200px, 0, 0) rotate(360deg);
}
}
範例:translate3d
同上,利用一個藍色的小方塊 cube 作範例,這個小方塊被放在一個虛線包圍的 container 裡面。
transform-style
:設定其子元素的呈現方式,3D 或 平面(flat),預設是 flat。在這裡為 container 設定,使得 cube 能以 3D 呈現。rotate3d(x,y,z,angle)
:設定 3D 旋轉。perspective
:設定視角。
Animation 動畫
Animation 讓 HTML 元件在不使用 JavaScript 或 Flash 的情況下有動畫效果。
語法
animation: name duration timing-function delay iteration-count direction fill-mode play-state;
- Name:
@keyframes
動畫名稱。 - Duration:動畫時間。
- Timing Function:動畫進行的速度曲線。
- Delay:延遲動畫開始的時間。
- Iteration Count:動畫重複次數。
- Direction:動畫播放方向。
- Fill Mode:動畫開始前後是否保持動畫設定。
- Play State:控制動畫播放狀態。
@keyframes animationName {
keyframes-selector {
/* css-styles */
}
}
- Animation Name: 動畫名稱。
- Keyframes Selector: 物件在動畫的某個時間點的狀態,例如:指定 0% 至 100% 時的狀態。
Browser Support
Chrome 43+、Firefox 16+、IE 10+、Safari 9+、Opera 30+,也是所有主流瀏覽器皆支援。
範例 3
改寫上面的範例 2。指定執行動畫 move,總時間為 2 秒,執行無數次。
程式碼。
.cube {
width: 100px;
height: 100px;
background: #41d2f2;
animation: move 2s infinite;
}
@keyframes move {
0% {
transform: translate3d(0, 0, 0) rotate(0deg);
}
50% {
transform: translate3d(200px, 0, 0) rotate(360deg);
}
100% {
transform: translate3d(0, 0, 0) rotate(0deg);
}
}
Transition vs Animation
- Transition 設定簡單
- 複雜動畫建議使用 Animation
效能優化
目前大多數的裝置都是以每秒 60 次的頻率重新整理螢幕,因此瀏覽器需要儘量滿足裝置的重新整理頻率來重組畫面,讓使用者能順暢地與畫面互動。而每秒 60 次的重整意味著每次重整只有 1 / 60 秒 ~ 16.67 ms 的時間,扣除例行任務,每次重整可能只有 10 ms 的時間。若超過這個時間,就會發生閃避。為了達成這個效果,就必須優化關鍵轉譯路徑(Critical Rendering Path)。
關鍵轉譯路徑(Critical Rendering Path)
瀏覽器在經過一連串的工作後會呈現一個初始畫面,而這一連串的工作歷程就是指 Critical Rendering Path。因此,優化 Critical Rendering Path 就是優化網頁效能的關鍵。這歷程會有以下的步驟:
圖片來源:Rendering Performance。
- JavaScript:使用 JavaScript 觸發樣式變更,無論是 jQuery 的 animate 功能、排序資料集,或新增 DOM 元素至網頁。觸發的原因也可是 CSS Animation、Web Animation API。
- Styles:找出哪些 CSS 規則適用於哪些元素,並計算每個元素的最終樣式。注意,CSS 樣式的符合比對方向是由右往左的。
- Layout:計算版面配置,例如:width、height、margin、position(left、top、right、bottom)。
- Paint:繪製物件像素圖層,例如:box-shadow、border-radius、color、background-color。這階段耗時最長、所花成本最高,應儘量避免這個動作。
- Composite:將圖層依序繪製到畫面上,例如:transform、opacity。
畫面的變更會透過 Layout、Paint 和 Composite 三階段完成,但並非每個階段都需要經歷,需視使用的指令而定。因此可能是
- JavaScript/Styles -> Layout -> Paint -> Composite
- JavaScript/Styles -> Paint -> Composite
- JavaScript/Styles -> Composite
由以上可知,一旦變更某個步驟,便會從這個步驟開始重繪。所以,樣式的變更儘量位於後面的階段,這樣花費的成本可降至最低-某些功能就可利用只影響「Composite」這一層的指令來完成,這樣只要重繪最後一個階段就好了。例如
- Position — transform:
translateX(n) translateY(n) translateZ(n)
- Scale — transform:
scale(n)
- Rotation — transform:
rotate(ndeg)
- Opacity —
opacity: n
點此看實際範例。
這在動畫處理或捲動畫面時尤其重要。想知道 CSS 指令會影響的層面,可參考-CSS Triggers。
[備註] Web Engine
- Chrome: Blink(CSS)(WebKit on iOS), V8(JavaScript)
- Firefox:Gecko
- IE:Trident
- Edge:EdgeHTML
優化動畫效能,達到 60fps!
以下修改方式出自-Smooth as Butter: Achieving 60 FPS Animations with CSS3。在這裡使用一個簡單的範例,類似手機版網站,點擊 icon 後由左滑出選單。
範例 4:修改前
如下所示,由於改變的是 left 屬性,因此會重繪 layout,使得動畫不夠流暢。
程式碼。
.app-menu {
left: -300px;
transition: left 300ms;
}
.app-menu-open {
left: 0;
}
Chrome DevTool Timeline 如下,綠色的部份即 FPS,FPS 愈低表示能以 60 FPS 渲染畫面,或使用 FPS meter 檢視畫面更新頻率。
FPS 不夠規律,效能有待改善。
範例 5:改用 transform 位移
改用只影響 Composite 這階段的指令 transform: translateX(n)
來完成。
程式碼。
.app-menu {
transform: translateX(-100%);
transition: transform 300ms;
}
.app-menu-open {
transform: none;
}
Chrome DevTool Timeline。
FPS 規律,改善些許效能,動畫較流暢。關於 Composite 這階段的指令對效能的影響,可參考-Stick to Compositor-Only Properties and Manage Layer Count。
範例 6:觸發 GPU 繪製
利用特定 CSS 樣式強制觸發瀏覽器使用 GPU 繪製畫面。
程式碼。
.app-menu {
transform: translate3d(-100%, 0, 0);
transition: transform 300ms;
}
.app-menu-open {
transform: none;
}
Chrome DevTool Timeline。
FPS 規律,較範例 4 改善了一點點效能。
或使用 will-change
,推薦閱讀-使用 CSS3 will-change 提高頁面滾動、動畫等渲染性能。
範例 7:改變 layout
改變版面配置,試圖讓影響範圍變小。
程式碼。
<div class="menu">
<div class="app-menu"></div>
</div>
<div class="layout">
<div class="header">
<div class="menu-icon"></div>
</div>
</div>
Chrome DevTool Timeline。
FPS 規律,較範例 4 改善了一點點效能。
與原作者的實驗結果比較,大概都會比未修改前的效能改善一些,但並未如原作者的範例那樣大幅改進,可能是因為現今使用的電腦設備都十分地好吧。關於版面配置對效能的影響,可參考- Avoid Large, Complex Layouts and Layout Thrashing。
動畫設定注意事項
- 過場動畫時間不需太長,建議 300ms ~ 700ms。
- 網頁功能正常為優先考量,特效不是必要的。
- 注意瀏覽器的支援度。
References
- CSS Animation:露天讀書會的投影片。
- Smooth as Butter: Achieving 60 FPS Animations with CSS3
- Rendering Performance
- Analyze Runtime Performance
- 使用 CSS3 will-change 提高頁面滾動、動畫等渲染性能
後記
(2018/07/15 更新)
本文對於效能改善方法與範例和如何使用工具檢視並無深入的描述(甚至有誤,因而稍做修改與更正,歡迎指教),因此我又整理了一篇文章-關鍵轉譯路徑 Critical Rendering Path,除了對 Browser Rendering Pipeline 的每個步驟有更詳細的說明外,也包含使用 Chrome DevTools 檢測效能、實際優化範例。