如絲般順滑:使用 CSS3 實現 60 幀的動畫
在移動端上實現動畫很簡單。
在移動端上正確地實現動畫也比較簡單...如果你采納我們的建議的話。
雖然現在每個人都會使用 CSS3 實現動畫,但許多人用的都不夠恰當。很多應加以考慮的最佳實踐常常被忽略,因為仍然有人不明白這些最佳實踐的真正意義。
如今有這么多的設備規范,如果還不有針對性地優化你的代碼,糟糕的用戶體驗將讓你死無葬身之地。
記住:雖然市場上始終有一些高端的旗艦機在挑戰性能極限,但你面對的仍將是和這些性能怪獸相比只是玩具一樣的低端設備。
我們想幫助你正確地駕馭 CSS3。首先先要了解幾件事。
了解時間軸
當渲染和處理 HTML 元素時,瀏覽器做了什么?這個時間軸叫做關鍵渲染路徑。
想達到流暢的動畫效果我們需要關注修改屬性對合成(Composite)階段的影響,而不是其它階段。
1. 樣式
瀏覽器開始計算應用于元素的樣式 —— 重計算樣式。
2. 布局
下一步,瀏覽器開始為每個元素生成形狀和位置 —— 布局。這是瀏覽器設置頁面屬性的地方,如 width 和 height,以及 margin 或者 left、top、right、bottom。
3. 渲染
瀏覽器開始向層中填充像素。要使用的屬性有 box-shadow,border-radius,color,background-color 等等。
4. 合成
這就是你動手腳的地方了,因為瀏覽器開始把所有層畫到屏幕上。
現代瀏覽器能夠通過使用 transform 和 opacity 完美運行 4 種樣式。
- 位置?—?transform: translateX(n) translateY(n) translateZ(n);
- 縮放?—?transform: scale(n);
- 旋轉?—?transform: rotate(n deg);
- 透明?—?opacity: n;
如何達到 60 幀每秒
想法有了,讓我們擼起袖子開始干活吧。
我們從 HTML 開始,創建一個非常簡單的結構,并把類名 app-menu 的元素放入類名 layout 的元素中。
<div class="layout">
<div class=”app-menu”></div>
<div class=”header”></div>
</div>
錯誤做法
.app-menu {
left: -300px;
transition: left 300ms linear;
}
.app-menu-open .app-menu {
left: 0px;
transition: left 300ms linear;
}
看到我們更改的這些屬性了嗎?你應該避免在 transitions 中使用 left、top、right、bottom 屬性。它們的更新讓瀏覽器每次都要創建布局,影響所有它們的子元素,進而難以實現流暢的動畫。
結果是這樣的:
這個動畫一點也不流暢。我們通過開發者工具檢查背后到底發生了什么,請看:
能夠清晰地看到 FPS 非常不規則,性能也就比較糟糕。
使用 Transform
.app-menu {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
transition: transform 300ms linear;
}
.app-menu-open .app-menu {
-webkit-transform: none;
transform: none;
transition: transform 300ms linear;
}
transform 屬性會影響合成階段。在這里瀏覽器被告知層馬上要被渲染,做好準備執行動畫,因此動畫渲染時卡頓更少。
時間軸:
開始有效果了,FPS 變得有規律了,此外,動畫變得更流暢了。
FPS 開始優化,并且更加穩定,動畫也更流暢。
使用 GPU 執行動畫
百尺竿頭更進一步。為了讓動畫“如絲般順滑”,我們接下來使用 GPU 來渲染。
.app-menu {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
transition: transform 300ms linear;
will-change: transform;
}
雖然一些瀏覽器仍然需要 translateZ() 或 translate3d() 作為備選方案,will-change 被廣泛支持已經是勢不可擋了。它的功能是把元素提升到另一個層中,這樣瀏覽器就不必關心布局渲染或者繪制了。
這樣動畫能流暢到什么程度?看時間軸:
動畫的 FPS 更穩定了,渲染也更快了。但是有一幀仍然渲染得很久。在開始處還有一點點瓶頸。
記住剛開始創建的 HTML 結構嗎?我們用 JavaScript 控制 app-menu div。
function toggleClassMenu() {
var layout = document.querySelector(".layout");
if(!layout.classList.contains("app-menu-open")) {
layout.classList.add("app-menu-open");
} else {
layout.classList.remove("app-menu-open");
}
}
var oppMenu = document.querySelector(".menu-icon");
oppMenu.addEventListener("click", toggleClassMenu, false);
問題出在對 layout 元素增加類名上,我們使瀏覽器計算了不止一次樣式 —— 這影響了渲染效果。
如絲般順滑的 60 幀 FPS
如果我們在視窗之外創建菜單會如何?這種分離化的區域能夠保證只有需要做動畫的元素才會被影響。
因此,我們改進下面的 HTML 結構:
<div class="menu">
<div class="app-menu"></div>
</div>
<div class="layout">
<div class="header"></div>
</div>
此外你也可以通過一種略有不同的方式去控制菜單的狀態。我們通過 JavaScript 的 transitionend 方法,在監控到動畫結束時移除這個控制動畫的 CSS 類。
function toggleClassMenu() {
myMenu.classList.add("menu--animatable");
myMenu.classList.add("menu--visible");
myMenu.addEventListener("transitionend", OnTransitionEnd, false);
}
function OnTransitionEnd() {
myMenu.classList.remove("menu--animatable");
}
var myMenu = document.querySelector(".menu");
var oppMenu = document.querySelector(".menu-icon");
oppMenu.addEventListener("click", toggleClassMenu, false);
現在把它們放到一起,然后看效果。
下面是完全正確的使用 CSS3 實現動畫的例子:
.menu {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
z-index: 150;
}
.menu—visible {
pointer-events: auto;
}
.app-menu {
background-color: #fff;
color: #fff;
position: relative;
max-width: 400px;
width: 90%;
height: 100%;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
-webkit-transform: translateX(-103%);
transform: translateX(-103%);
display: flex;
flex-direction: column;
will-change: transform;
z-index: 160;
pointer-events: auto;
}
.menu—-visible.app-menu {
-webkit-transform: none;
transform: none;
}
.menu-—animatable.app-menu {
transition: all 130ms ease-in;
}
.menu--visible.menu—-animatable.app-menu {
transition: all 330ms ease-out;
}
.menu:after {
content: ‘’;
display: block;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.4);
opacity: 0;
will-change: opacity;
pointer-events: none;
transition: opacity 0.3s cubic-bezier(0,0,0.3,1);
}
.menu.menu--visible:after{
opacity: 1;
pointer-events: auto;
}
時間軸是怎么樣的?
如絲般地順滑,是吧?
來自:http://www.zcfy.cc/article/smooth-as-butter-achieving-60-fps-animations-with-css3-1054.html
Save