How Javascript works (Javascript工作原理) (十三) CSS 和 JS 動畫底層原理及如何最佳化其效能

來源:互聯網
上載者:User

標籤:dde   狀態   and   自己的   主要畫面格   eve   要求   block   簡單方法   

個人總結:讀完這篇文章需要20分鐘。

 

這是 JavaScript 工作原理的第十三章。

概述

正如你所知,動畫在建立令人歎服的網路應用中扮演著一個關鍵角色。由於使用者越來越注重使用者體驗,商戶開始意識到完美,令人愉悅的使用者體驗的重要性,結果網路應用變得越來越重並且擁有更多動態互動的功能。這就要求網路應用提供更加複雜的動畫來實現平滑的狀態過渡貫穿於使用者的使用過程當中。現在,這已經司空見慣。使用者變得越來越挑剔,他們潛意識期許可以獲得快速響應和良好互動的使用者介面。

然而,讓介面具有動畫效果不一定是件簡單的事情。動畫的時機,方面及採用何種動畫效果都是很模糊的概念。

 

JavaScript 和 CSS 動畫比較

JavaScript 和 CSS 是建立網頁動畫的兩條主要途徑。兩種不分好賴,看情況用吧。

 

CSS 動畫

使用 CSS 動畫是讓元素在螢幕上移動的最簡單方法。

我們將會以如何讓元素在 X 和 X 座標上移動元素 50 像素作為小樣本開始。通過持續 1 秒的 CSS 過渡來移動元素。

.box {  -webkit-transform: translate(0, 0);  -webkit-transition: -webkit-transform 1000ms;  transform: translate(0, 0);  transition: transform 1000ms;}.box.move {  -webkit-transform: translate(50px, 50px);  transform: translate(50px, 50px);}

當為元素添加 move 類的時候,改變 transform 的值然後開發發生過渡效果。

除了過渡期間,還有 easing 參數,它主要負責動畫體驗。該參數會在之後詳細介紹。

如果通過以上的程式碼片段可以建立單獨的樣式類來操作動畫,那麼也可以使用 JavaScript 來切換每個動畫。

如下元素:

<div class="box">  Sample content.</div>

然後,使用 JavaScript 來切換每個動畫。

var boxElements = document.getElementsByClassName(‘box‘),    boxElementsLength = boxElements.length,    i;for (i = 0; i < boxElementsLength; i++) {  boxElements[i].classList.add(‘move‘);}

以上程式碼片段為每個包含 box 類的元素添加 move 類來觸發動畫。

這樣做可以很好為你的網路應用提供很好的平衡。你就可以專註於使用 JavaScript 來操作應用狀態,然後只需為目標元素設定合適的類,讓瀏覽器來處理動畫。如若你選擇這麼處理,就可以監聽元素的 transitionend 事件,除了處理IE 老版本瀏覽器安全色問題之外。

 

如下監聽 transitioned 事件,該事件會在動畫結束時觸發。

var boxElement = document.querySelector(‘.box‘); // 擷取第一個包含 box 類的元素boxElement.addEventListener(‘transitionend‘, onTransitionEnd, false);function onTransitionEnd() {  // Handle the transition finishing.}

除了使用 CSS 過渡,還可以使用 CSS 動畫,CSS 動畫可以讓你更好地控制單獨的動畫主要畫面格,期間以及迴圈次數。

主要畫面格是用來通知瀏覽器在規定的時間點上應有的 CSS 屬性值然後填充空白。

看下例子:

/** * 該樣本是沒有包含瀏覽器首碼的精簡版。加上以後會更加準確些。 * */.box {  /* 選擇動畫名稱 */  animation-name: movingBox;  /* 動畫時間長度 */  animation-duration: 2300ms;  /* 動畫迴圈次數 */  animation-iteration-count: infinite;  /* 每次奇數次迴圈時反轉動畫 */  animation-direction: alternate;}@keyframes movingBox {  0% {    transform: translate(0, 0);    opacity: 0.4;  }  25% {    opacity: 0.9;  }  50% {    transform: translate(150px, 200px);    opacity: 0.2;  }  100% {    transform: translate(40px, 30px);    opacity: 0.8;  }}

效果樣本-https://sessionstack.github.io/blog/demos/keyframes/

通過使用 CSS 動畫定義獨立於目標元素的動畫本身,然後設定元素的 animation-name 屬性來使用想要的動畫效果。

CSS 動畫仍然是需要加瀏覽器首碼的,在 Safari, Safari 行動瀏覽器和 Android 端添加 -webkit- 首碼。Chrome, Opera, Internet Explorer, and Firefox 端全部不需要添加首碼。有很多工具可以用來建立包含任意首碼的樣式,這樣就不需要在源檔案中帶樣式首碼。

可以使用 autoprefixer 或者 cssnext 來自動為樣式添加首碼。

 

JavaScript 動畫

和 CSS 過渡或者 CSS 動畫相比,使用 JavaScript 來建立動畫要更加複雜些,但是一般而言,它會為開發進行提供強大的功能。

一般情況下,可以內聯 JavaScript 動畫作為代碼的一部分。也可以把它們封裝在其它對象之中。以下為複現之前描述的 CSS 過渡的 JavaScript 代碼:

var boxElement = document.querySelector(‘.box‘);var animation = boxElement.animate([  {transform: ‘translate(0)‘},  {transform: ‘translate(150px, 200px)‘}], 500);animation.addEventListener(‘finish‘, function() {  boxElement.style.transform = ‘translate(150px, 200px)‘;});

預設情況下,網頁動畫只是修改了元素的展示效果。如果想要讓元素停留在其移動到的目標位置,那麼就得在動畫結束的時候修改其底層樣式。這也是為什麼在以上的樣本中監聽 finish 事件然後設定box.style.transform 屬性為 translate(150px, 200px) 的原因,該屬性值和 CSS 動畫執行的第二個樣式轉換是一樣的。

通過使用 JavaScript 動畫,可以完全控制每一步元素的樣式。這意味著可以隨心所欲地減速,暫停,停止或者翻轉動畫進而操作目標元素。由於可以適當地封裝動畫行為,所以當在構建複雜麵向對象的應用程式的時候會特別有用。

 

Easing 定義

自然平滑地移動會讓網路應用擁有更好的使用者互動體驗。

自然條件下,沒有事物可以直線地從一個點運動到另一個點。現實生活中,在我們周圍的物理世界中物體在移動的時候會加速或減速,因為我們並不生活在真空狀態下且有不同的因素來影響事物的運行狀態。人類的大腦會期望感受這樣的移動,所以當為網路應用製作動畫的時候,利用此類知識會對自己會有好處。

這是你所應該理解的術語:

  • 『ease in』-開始移動緩慢而後加速
  • 『ease out』-開始移動迅速而後減速

可以合并兩個動畫,比如 『ease in out』。

Easing 可以使得動畫更加自然平滑。

Easing 關鍵字

可以為 CSS 過渡和動畫選擇任意的 easing 方法。不同的關鍵字會影響動畫的 easing。你也可以完全自訂 easing 方法。

以下為可以選擇用來控制 easing 的 CSS 關鍵字:

  • linear
  • ease-in
  • ease-out
  • ease-in-out

讓我們深入瞭解並查看他們的效果。

Linear 動畫

不使用任何的 easing 方法的動畫即為 linear。

以下為 linear 過渡效果的圖示:

 

值隨著時間流逝,值等比增加。使用 linear 動效,會讓動畫不自然。一般來說,避免使用 linear 動效。

使用如下代碼實現一個簡單的線性動畫:

transition: transform 500ms linear;

Ease-out 動畫

正如前所述,和 linear 對比,easing out 讓動畫快速啟動,結束時會減速。以下為圖示:

 

總之,easing out 是最適合做介面體驗的,因為快速地啟動會給人以快速響應的動畫的感覺,而結束時讓人感覺很平滑這得歸功於不一致的移動速度。

打個比喻,比如那些跑車,首先啟動速度相當的快,這就給人以愉悅的感覺。這個就比較符合人類對於動畫的感知。

有很多的方法來實現 ease out 動畫效果,而最簡單的即為 CSS 中的 ease-out 關鍵字。

transition: transform 500ms ease-out;

Ease-in 動畫

和 ease-out 動畫相反-其啟動慢然後結束時變快。圖示如下:

和 ease-out 動畫比較,由於他們啟動緩慢給人以反應卡頓的感覺,所以 ease-in 讓人感覺動畫不自然。動畫結束時很快給人一種奇怪的感覺,因為整個動畫一直在加速,而現實世界中當事物突然停止運動的時候會減速而不是加速。

和 ease-out 和 linear 動畫類似,使用 CSS 關鍵字來實現 ease-in 動畫:

transition: transform 500ms ease-in;

Ease-in-out 動畫

該動畫為 ease-in 和 ease-out 的合集。圖示如下:

不要設定動畫期間過長,否則會給人一種介面不響應的感覺。

使用 ease-in-out CSS 關鍵字來實現 ease-in-out 動畫:

transition: transform 500ms ease-in-out;

自訂 easing

你可以自訂自己的 easing 曲線,這樣就更有效地控制項目中的動畫。

實際上, ease-inlinear 及 ease 關鍵字映射到預定義貝茲路徑 ,可以在 CSS transitions specification 和 Web Animations specification 中尋找更多關於貝茲路徑的內容。

貝茲路徑

讓我們看一下貝茲路徑的運行原理。一條貝茲路徑包含四個點,或者準確地說是包含兩組數值。每一對數值內包含表示三次貝茲路徑控制點的 X 和 Y 座標。貝茲路徑的起點座標為 (0, 0) ,終點座標為 (1, 1)。可以設定兩組數值對。每個控制點的 X 軸值必須在 [0, 1] 之間,而 Y 軸值可以超過 [0, 1],雖然規範並沒有明確允許超過的數值。即使每個控制點的 X 和 Y 值的微小差異都會輸出完全不同的貝茲路徑。

查看維基百科關於貝茲路徑的說明,通俗一點講即,現在所說的即三次貝茲路徑,該曲線由四個點組成,P0, P1, P2, P3 組成,那麼,P0 和 P1 組成一對,P2 和 P3 組成一對,P1 和 P2 即為控制點,P0 和 P3 即為起始和結束節點。如所示:

看下兩張擁有相近但不同座標的控制結點的貝茲路徑圖。

如你所見,兩張圖有很大不同。第一個控制點向量差異為 (0.045, 0.183),而第二個控制點向量差異為 (-0.427, -0.054)。

第二條曲線的樣式為:

transition: transform 500ms cubic-bezier(0.465, 0.183, 0.153, 0.946);

第一組數值為起始控制點的 X 和 Y 座標而第二組數值為第二個控制點的 X 和 Y 座標。

 

效能最佳化

你得維持動畫幀數為 60 幀每秒,否則會影響到使用者體驗。

和世界上其它事物一樣,動畫會有效能開銷。一些屬性的動畫效能開銷相比其它屬性要小。比如,為元素的 width 和 height做動畫會更改其幾何結構並且可能會造成頁面上的其它元素移動或者大小的改變。這一過程被稱為布局。之前的文章中有詳細介紹過布局和渲染。

總之,應該盡量避免為會引起布局和繪製的屬性做動畫。對於大多數現代瀏覽器而言,即把動畫局限於 opacity 和 transform 屬性。

Will-change

可以使用 will-change 來通知瀏覽器將會更改某個元素的屬性。這會允許瀏覽器當更改某個元素屬性的時候,事先進行最恰當的最佳化。但不要濫用 will-change,因為這樣做會適得其反,使得瀏覽器浪費更多的資源,從而造成更多的效能問題。

為 transforms 和 opacity 添加 will-change 代碼如下:

.box {  will-change: transform, opacity;}

該屬性在 Chrome, Firefox,Opera 得到很好的相容。

 

 

如何選擇 JavaScript 和 CSS 來執行動畫

這個問題是無解的。只需謹記以下原則:

  • 基於 CSS 的動畫和原生支援的網頁動畫一般都是由被稱為『合成線程』的線程來處理的。這不同於瀏覽器的主線程,主線程是用來執行計算樣式,布局,繪製及 JavaScript 代碼的。這即意味著如果瀏覽器在主線程上運行耗時的任務,不會中斷動畫的運行。
  • 很多時候,也可以由合成線程來處理 transforms 和 opacity 屬性值的更改。
  • 如果有任何動畫觸發繪製,布局或者同時觸發兩者,『主線程』將不得不來進行處理。事實是基於 CSS 和 JavaScript 的動畫和布局或者繪製的效能開銷將很有可能會阻塞所有和 CSS 或者 JavaScript 運行相關的工作,從而使得渲染問題變得毫無意義。
正確使用動畫

良好的動畫為項目添加一層令人愉快和互動的使用者體驗。你可以隨意使用動畫,不管是寬度,調試,定位,顏色或背景色,但必須注意潛在的效能瓶頸。糟糕的動畫選擇會影響使用者體驗,所以動畫必須是高效和適當的。儘可能減少使用動畫。只使用動畫來讓使用者體驗流暢自然而不是濫用。

使用動畫進行互動

不要因為只是為了用而去使用動畫。相反,有策略性地使用動畫來加強使用者互動體驗。避免使用不必要的動畫來打斷或者阻礙使用者的使用。

避免為效能開銷大的屬性做動畫

比糟糕的動畫使用更糟的是那些會引起頁面卡頓的動畫。這類動畫讓使用者感到懊喪和不快。

引用資源
  • https://developers.google.com/web/fundamentals/design-and-ux/animations/css-vs-javascript
  • https://developers.google.com/web/fundamentals/design-and-ux/animations/
  • https://developers.google.com/web/fundamentals/design-and-ux/animations/animations-and-performance
疑問

可以看下網上的這篇介紹貝茲路徑的文章,那麼可以如何使用貝茲路徑來做出令人驚歎的動畫呢?

 

How Javascript works (Javascript工作原理) (十三) CSS 和 JS 動畫底層原理及如何最佳化其效能

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.