如絲般順滑:使用 CSS3 實現 60 幀的動畫

hubuke 8年前發布 | 26K 次閱讀 CSS 前端技術

在移動端上實現動畫很簡單。

在移動端上正確地實現動畫也比較簡單...如果你采納我們的建議的話。

雖然現在每個人都會使用 CSS3 實現動畫,但許多人用的都不夠恰當。很多應加以考慮的最佳實踐常常被忽略,因為仍然有人不明白這些最佳實踐的真正意義。

如今有這么多的設備規范,如果還不有針對性地優化你的代碼,糟糕的用戶體驗將讓你死無葬身之地。

記住:雖然市場上始終有一些高端的旗艦機在挑戰性能極限,但你面對的仍將是和這些性能怪獸相比只是玩具一樣的低端設備。

我們想幫助你正確地駕馭 CSS3。首先先要了解幾件事。

了解時間軸

當渲染和處理 HTML 元素時,瀏覽器做了什么?這個時間軸叫做關鍵渲染路徑

如絲般順滑:使用 CSS3 實現 60 幀的動畫

想達到流暢的動畫效果我們需要關注修改屬性對合成(Composite)階段的影響,而不是其它階段。

1. 樣式

如絲般順滑:使用 CSS3 實現 60 幀的動畫

瀏覽器開始計算應用于元素的樣式 —— 重計算樣式

2. 布局

如絲般順滑:使用 CSS3 實現 60 幀的動畫

下一步,瀏覽器開始為每個元素生成形狀和位置 —— 布局。這是瀏覽器設置頁面屬性的地方,如 widthheight,以及 margin 或者 lefttoprightbottom

3. 渲染

如絲般順滑:使用 CSS3 實現 60 幀的動畫

瀏覽器開始向層中填充像素。要使用的屬性有 box-shadowborder-radiuscolorbackground-color 等等。

4. 合成

這就是你動手腳的地方了,因為瀏覽器開始把所有層畫到屏幕上。

如絲般順滑:使用 CSS3 實現 60 幀的動畫

現代瀏覽器能夠通過使用 transformopacity 完美運行 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>

如絲般順滑:使用 CSS3 實現 60 幀的動畫

錯誤做法

.app-menu {
  left: -300px;
  transition: left 300ms linear;
}

.app-menu-open .app-menu {
  left: 0px;
  transition: left 300ms linear;
}

看到我們更改的這些屬性了嗎?你應該避免在 transitions 中使用 lefttoprightbottom 屬性。它們的更新讓瀏覽器每次都要創建布局,影響所有它們的子元素,進而難以實現流暢的動畫。

結果是這樣的:

如絲般順滑:使用 CSS3 實現 60 幀的動畫

這個動畫一點也不流暢。我們通過開發者工具檢查背后到底發生了什么,請看:

如絲般順滑:使用 CSS3 實現 60 幀的動畫

能夠清晰地看到 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 屬性會影響合成階段。在這里瀏覽器被告知層馬上要被渲染,做好準備執行動畫,因此動畫渲染時卡頓更少。

如絲般順滑:使用 CSS3 實現 60 幀的動畫

時間軸:

如絲般順滑:使用 CSS3 實現 60 幀的動畫

開始有效果了,FPS 變得有規律了,此外,動畫變得更流暢了。

FPS 開始優化,并且更加穩定,動畫也更流暢。

使用 GPU 執行動畫

百尺竿頭更進一步。為了讓動畫“如絲般順滑”,我們接下來使用 GPU 來渲染。

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

雖然一些瀏覽器仍然需要 translateZ()translate3d() 作為備選方案,will-change 被廣泛支持已經是勢不可擋了。它的功能是把元素提升到另一個層中,這樣瀏覽器就不必關心布局渲染或者繪制了。

如絲般順滑:使用 CSS3 實現 60 幀的動畫

這樣動畫能流暢到什么程度?看時間軸:

如絲般順滑:使用 CSS3 實現 60 幀的動畫

動畫的 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;
}

如絲般順滑:使用 CSS3 實現 60 幀的動畫

時間軸是怎么樣的?

如絲般順滑:使用 CSS3 實現 60 幀的動畫

如絲般地順滑,是吧?

 

來自:http://www.zcfy.cc/article/smooth-as-butter-achieving-60-fps-animations-with-css3-1054.html

Save

 本文由用戶 hubuke 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!