使用CSS3實現60FPS動畫

使用動畫在移動應用程序中很容易的。如果您遵循我們的建議,移動應用程序中使用動畫變得很簡單。

雖然現在很多人在移動應用程序中使用CSS3 Animation來製作動畫,當然很多人也不這麼做。很多最佳實踐,還是不斷的被忽視。出現這種情況主要是因爲仍有許多人並不真正瞭解這些最佳實踐存在的真正原因,因此沒有大力的支持。

面對這麼多的終端設備,如果不考慮優化你的代碼,你最終將會交付一個水平一般的效果,從而失去一份最高份額的市場佔有率。

這篇文章我們想給你提供一個正確利用和使用CSS3的特性,這樣做我們首先需要了解一些事情。

理解時間軸

什麼是瀏覽器的渲染?這個非常簡單的時間稱爲關鍵渲染路徑(Critical Rendering Path)。


實現平滑的動畫,我們需要關注於改變屬性影響合成的步驟,而不是在前一層添加這個壓力。

Style


瀏覽器開始計算應用中的元素樣式——重新計算樣式風格。

Layout


接下來的一層,瀏覽器會爲這些元素生成形狀和位置——佈局。瀏覽器會給頁面設置屬性,比如width、height以及margin、left、top、right和bottom等。

Paint


瀏覽器將開始給每個元素填充像素。它們使用屬性有:box-shadow、border-radius、color、background-color等。

Composite

瀏覽器開始在屏幕中繪製這些圖層(Layout)。


現代瀏覽器可以利用transform和opacity繪製很好的動畫,其中共有四個讓動畫更好的屬性:

  • 位置(Position):transform: translateX(n) translateY(n) translateZ(n)
  • 縮放(Scale): transform: scale(n)
  • 旋轉(Rotation): transform: rotate(ndeg)
  • 透明度(Opacity): opacity: n

如何實現每秒60FPS

考慮到這一點,是時候擼起袖子開始工作了。

讓我們先從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;
}

看到這些屬性改變了嗎?你應該避免在動畫中使用left、top、right和bottom這四個屬性。這些屬性並不能幫我們創建一個流體動畫,因爲瀏覽器每次都會創建佈局(重排),而且還會影響到他們的後代元素。

最終效果像這樣:


動畫效果並不光滑。我們通過DevTools Timeline工具來查看發生了什麼,檢查後的結果如下所示:


清楚顯示了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屬性效果的合成(Composite)。這裏告訴瀏覽器將會繪製(Paint),儘快準備好動畫開始,所以有更少的時間打斷動畫的渲染。


Timeline顯示的結果如下:


結果開始有所好轉,FPS更普通,因此動畫是平滑的。

FPS開始更好的結果,更多的常數,動畫也順暢。

在GPU上運行動畫

那麼我們再提高一層,真正的使動畫像黃油一樣平滑,這樣一來我們就要使用GPU來渲染動畫。

.app-menu {
    -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
    transition: transform 300ms linear;
    will-change: transform;
}

儘管transformZ()或者translate3d()仍將需要在一些瀏覽器中回退,will-change具有這方面的特性。什麼中以讓它們促進元素提高到另一層,因此瀏覽器不需要考慮容器佈局的渲染或繪製。


看看是平滑的嗎?Timeline效果如下:


動畫的FPS越是常數動畫呈現的將會越快。但仍有很多片段(Frame)需要運行。

還記得一開始創建的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);

這裏的問題是,通過JavaScript給.layout的div添加類名,瀏覽器將要重新計算樣式風格——這將直接影響渲染性能。

像黃油一樣平滑的60FPS的解決方案

如果已經創建窗口外區域的菜單,創建一個獨立(isolated)區域將確保隻影響真正想要動畫的元素。

因此,前面的HTML結構需要做一下調整:

<div class="menu">
    <div class="app-menu"></div>
</div>
<div class="layout">
    <div class="header"></div>
</div>

現在控制菜單的狀態可以有一個稍微不同的方式。在動畫過渡結束時刪除類名,實現這樣的效果,可以使用JavaScript的transitionend函數:

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;
}

.menuvisible {
    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;
}


時間軸的結果告訴我們:


像黃油一樣平滑,看到了?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章