JavaScript是如何工做的: CSS 和 JS 動畫底層原理及如何優化它們的性能

阿里雲最近在作活動,低至2折,有興趣能夠看看:
https://promotion.aliyun.com/...

爲了保證的可讀性,本文采用意譯而非直譯。javascript

這是專門探索 JavaScript 及其所構建的組件的系列文章的第 13 篇。css

若是你錯過了前面的章節,能夠在這裏找到它們:html

  1. JavaScript 是如何工做的:引擎,運行時和調用堆棧的概述!
  2. JavaScript 是如何工做的:深刻V8引擎&編寫優化代碼的5個技巧!
  3. JavaScript 是如何工做的:內存管理+如何處理4個常見的內存泄漏 !
  4. JavaScript 是如何工做的:事件循環和異步編程的崛起+ 5種使用 async/await 更好地編碼方式!
  5. JavaScript 是如何工做的:深刻探索 websocket 和HTTP/2與SSE +如何選擇正確的路徑!
  6. JavaScript 是如何工做的:與 WebAssembly比較 及其使用場景 !
  7. JavaScript 是如何工做的:Web Workers的構建塊+ 5個使用他們的場景!
  8. JavaScript 是如何工做的:Service Worker 的生命週期及使用場景!
  9. JavaScript 是如何工做的:Web 推送通知的機制!
  10. JavaScript是如何工做的:使用 MutationObserver 跟蹤 DOM 的變化!
  11. JavaScript是如何工做的:渲染引擎和優化其性能的技巧!
  12. JavaScript是如何工做的:深刻網絡層 + 如何優化性能和安全!

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!前端

概述

你確定知道,動畫在建立引人注目的 Web 應用程序中扮演着重要的角色。隨着用戶愈來愈多地將注意力轉移到用戶體驗上,商戶開始意識到完美、愉快的用戶體驗的重要性,結果 Web 應用程序變得愈來愈重,並具備更動態交互的 UI。這一切都須要更復雜的動畫,以便用戶在整個過程當中更平穩地進行狀態轉換。今天,這甚至不被認爲是什麼特別的事情。用戶正變得愈來愈挑剔,默認狀況下,他們指望的是具備高響應性和交互性的用戶界面。java

然而,界面的動畫化並不必定是簡單的。什麼是動畫,何時該用動畫,動畫應該有什麼樣的視頻效果,這些都是棘手的問題。git

JavaScript 和 CSS 動畫比較

建立 Web 動畫的兩種主要方法是使用JavaScript和 CSS。選擇哪一種沒有對或錯,這徹底取決於你想要達到的效果。github

CSS 動畫

用CSS製做動畫是讓元素在屏幕上移動的最簡單方法。web

這裏將從如何讓元素在 X 和 Y 軸上移動 50px 簡單示例開始,經過持續 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 的值而後開發發生過渡效果。segmentfault

除了轉換持續時間外,還有 easing 屬性,這實際上就是動畫的運動速度方式,該參數會在以後詳細介紹。

若是像上面的代碼片斷同樣,建立單獨的 CSS 類來實現動畫,固然也可使用 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 事件,但前提是放棄舊版 Internet Explorer 的支持:

圖片描述

監聽 transitionend 觸發的事件以下所示:

var boxElement = document.querySelector('.box');
boxElement.addEventListener('transitionend', onTransitionEnd, false);

function onTransitionEnd() {
  // Handle the transition finishing.
}

除了使用 CSS 過渡以外,你還可使用 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.i...

使用CSS動畫,你能夠獨立於目標元素定義動畫自己,並使用 animation-name 屬性來選擇所需的動畫。

CSS 動畫在某種程度仍然須要加瀏覽器前綴的,在 Safari、Safari Mobile 和 Android 中都使用了 -webkit。 Chrome、 Opera、Internet Explorer 和 Firefox 都不須要添加前綴。許多工具能夠幫助你建立所需 CSS 的前綴,這樣就不須要在源文件中帶樣式前綴。

JavaScript 動畫

和 CSS 過渡或者 CSS 動畫相比,使用 JavaScript 建立動畫更加複雜,但它一般爲開發人員提供了更強大的功能。

JavaScript 動畫是做爲代碼的一部份內聯編寫的。你還能夠將它們封裝在其餘對象中。如下爲用 JavaScript 來實現最開始的 CSS 過渡的代碼:

var boxElement = document.querySelector('.box');
var animation = boxElement.animate([
  {transform: 'translate(0)'},
  {transform: 'translate(150px, 200px)'}
])

animation.addEventListener('finish', function() {
  boxElement.style.transform = 'translate(150px, 200px)';
})

默認狀況下,Web 動畫僅修改元素的展現效果。 若是要將對象停留在移動後的位置,則應在動畫完成時修改其基礎樣式。 這就是爲何在上面的例子中監聽 finish 事件,並將 box.style.transform 屬性設置爲 translate(150px, 200px),該屬性值和 CSS 動畫執行的第二個樣式轉換是同樣的。

使用 JavaScript 動畫,你能夠在每一步徹底控制元素的樣式。 這意味着你能夠放慢動畫速度,暫停動畫,中止它們,翻轉它們,並根據須要操縱元素。 若是你正在構建複雜的面向對象的應用程序,這尤爲有用,由於你能夠正確地封裝你想要的動畫行爲。

Easing 定義

天然過渡效果會讓你的用戶對你的 Web 應用程序感受更舒服,從而帶來更好的用戶體驗。

固然,沒有任何東西從一個點到另外一個點線性移動。 實際上,當事物在咱們周圍的物理世界中移動時,事物每每會加速或減速,由於咱們不是在真空中,而且有不一樣的因素會影響這一點。 人類的大腦會指望感覺這樣的移動,因此當爲網絡應用製做動畫的時候,利用此類知識會對本身會有好處。

如下是一些術語須要瞭解一下:

  • ease in —  相對於勻速,開始的時候慢,以後快
  • ease out — 相對於勻速,開始時快,結束時候間慢
  • ease-in-out — 相對於勻速,開始和結束都慢)兩頭慢

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 動畫

如前所述,與線性動畫相比,easing out 動畫開始時快,結束時候間慢,過渡效果的圖示以下:

圖片描述

通常來講,easing out過渡效果是最適合作界面體驗的,由於快速地啓動會給人以快速響應的動畫的感受,而結束時讓人感受很平滑這得歸功於不一致的移動速度。

有不少方法能夠實現 ease-out 效果,但最簡單的是 CSS 中的 ease-out 關鍵字:

transition: transform 500ms ease-out;

Ease-in 動畫

ease-out 動畫相反-開始時快,結束時候間慢,過渡效果圖以下:

圖片描述

ease-out 動畫相比, ease-in 可能會讓人感到不尋常,因爲啓動緩慢給人以反應卡頓的感受,所以會產生一種無反應的感受。 動畫結束很快也會產生一種奇怪的感受,由於整個動畫正在加速,而現實世界中的物體在忽然中止時每每會減速。

ease-outlinear 動畫相似,使用 CSS 關鍵字來實現 ease-in 動畫:

transition: transform 500ms ease-in;

Ease-in-out 動畫

該動畫爲 ease-in 和 ease-out 的合集,過渡效果圖以下:

圖片描述

不要使用太長的動畫持續時間,由於它們會讓你的 UI 感受沒有響應。

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

transition: transform 500ms ease-in-out;

自定義 easing

你也能夠定義本身的 easing 曲線,這能夠更好地建立本身想要的動畫效果。

實際上, ease-inlinearease 關鍵字映射到預約義 貝塞爾曲線 ,能夠在 CSS transitions specificationWeb Animations specification 中查找更多關於貝塞爾曲線的內容。

貝塞爾曲線 (Bézier curves)

Bézier curve(貝塞爾曲線)是應用於二維圖形應用程序的數學曲線。 曲線定義:起始點、終止點(也稱錨點)、控制點。經過調整控制點,貝塞爾曲線的形狀會發生變化。 1962年,法國數學家Pierre Bézier第一個研究了這種矢量繪製曲線的方法,並給出了詳細的計算公式,所以按照這樣的公式繪製出來的曲線就用他的姓氏來命名,稱爲貝塞爾曲線

CSS3 transition-timing-function 屬性,其語法以下:

transition-timing-function: linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n);

總而言之能夠用cubic-bezier(n,n,n,n)的形式來表示所有的屬性值,這裏就涉及到貝塞爾曲線(Bézier curve)。

讓咱們看看貝塞爾曲線的工做原理。 貝塞爾曲線須要四個值,或者更準確地說它須要兩對數字。 每對描述立方貝塞爾曲線控制點的 XY 座標。貝塞爾曲線的起點有一個座標 (0, 0) ,結束座標是 (1, 1)。 你能夠設置兩個對號,兩個控制點的 X 值必須在 [0,1] 範圍內,而且每一個控制點的 Y 值能夠超過 [0,1] 限制,儘管規定不清楚多少。

即便每一個控制點的 XY 值稍有變化,也會獲得徹底不一樣的曲線。讓咱們看兩張貝塞爾曲線的圖,兩張圖相近但座標的控制結點卻不一樣。

圖片描述

圖片描述

如您所見,兩張圖有很大的不一樣, 第一個控制點矢量差爲 (0.045,0.183) 矢量差,而第二控制點矢量差爲 (-0.427, -0.054)

第二條曲線的樣式爲:

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

前兩個數字是第一個控制點的 XY 座標,後兩個數字是第二個控制點的 XY 座標。

性能優化

當你在使用動畫的時候,你應該維持 60 幀每秒,不然會影響用戶體驗。

和世界上的其餘事物同樣,動畫也會有性能的開銷。一些屬性的動畫性能開銷相比其它屬性要小。例如,爲元素的 widthheight 作動畫會更改其幾何結構而且可能會形成頁面上的其它元素移動或者大小的改變,這個過程稱爲佈局。咱們在以前的一篇文章 中更詳細地討論了佈局和渲染。

一般,你應該避免動畫觸發佈局或重繪的屬性。 對於大多數現代瀏覽器,這意味着把動畫侷限於 opacitytransform 屬性。

Will-change

你可使用 will-change 知瀏覽器你打算更改元素的屬性,這容許瀏覽器在進行更改以前進行最適當的優化。可是,不要過分使用 will-change,由於這樣作會致使瀏覽器浪費資源,從而致使更多的性能問題。

will-change 用法以下:

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

該屬性在 Chrome, Firefox,Opera 獲得很好的兼容。

圖片描述

JavaScript 動畫和 CSS 動畫該若是抉擇

  • 根據 Google Developer,渲染線程分爲 主線程 (main thread)合成線程 (compositor thread)。若是 CSS 動畫只是改變 transformsopacity,這時整個 CSS 動畫得以在 合成線程 完成(而JS動畫則會在 主線程 執行,而後觸發合成線程進行下一步操做),在 JS 執行一些昂貴的任務時,主線程繁忙,CSS 動畫因爲使用了合成線程能夠保持流暢
  • 在許多狀況下,也能夠由合成線程來處理 transformsopacity 屬性值的更改。
  • 對於幀速表現很差的低版本瀏覽器,CSS3能夠作到天然降級,而JS則須要撰寫額外代碼。
  • CSS動畫有自然事件支持(TransitionEnd、AnimationEnd,可是它們都須要針對瀏覽器加前綴),JS則須要本身寫事件。
  • 若是有任何動畫觸發繪畫,佈局或二者,則須要 「主線程」 才能完成工做。 這對於基於 CSS 和 JavaScript 的動畫都是如此,佈局或繪製的開銷可能會使與 CSS 或 JavaScript 執行相關的任何工做相形見絀,這使得問題沒有實際意義。
  • CSS3有兼容性問題,而JS大多時候沒有兼容性問題。

總結

若是動畫只是簡單的狀態切換,不須要中間過程控制,在這種狀況下,css 動畫是優選方案。它可讓你將動畫邏輯放在樣式文件裏面,而不會讓你的頁面充斥 Javascript 庫。然而若是你在設計很複雜的富客戶端界面或者在開發一個有着複雜 UI 狀態的 APP。那麼你應該使用 js 動畫,這樣你的動畫能夠保持高效,而且你的工做流也更可控。因此,在實現一些小的交互動效的時候,就多考慮考慮 CSS 動畫。對於一些複雜控制的動畫,使用 javascript 比較可靠。


原文:

https://blog.sessionstack.com...

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

你的點贊是我持續分享好東西的動力,歡迎點贊!

交流

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

https://github.com/qq44924588...

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

clipboard.png

相關文章
相關標籤/搜索