這是專門探索 JavaScript 及其構建元件系列的第 13 篇文章。在識別和描述核心元素的過程中,我們還分享了構建 SessionStack 時的一些經驗法則,SessionStack 是一個足夠強大且高效能的 JavaScript 應用程式,用來幫助使用者實時檢視和重現其 Web 應用程式的缺陷。
如果你錯過了前面的章節,你可以在這裡找到它們:
- [譯] JavaScript 是如何工作的:對引擎、執行時、呼叫堆疊的概述
- [譯] JavaScript 是如何工作的:在 V8 引擎裡 5 個優化程式碼的技巧
- [譯] JavaScript 是如何工作的:記憶體管理 + 處理常見的4種記憶體洩漏
- [譯] JavaScript 是如何工作的: 事件迴圈和非同步程式設計的崛起 + 5個如何更好的使用 async/await 編碼的技巧
- [譯] JavaScript 是如何工作的:深入剖析 WebSockets 和擁有 SSE 技術 的 HTTP/2,以及如何在二者中做出正確的選擇
- [譯] JavaScript 是如何工作的:與 WebAssembly 一較高下 + 為何 WebAssembly 在某些情況下比 JavaScript 更為適用
- [譯] JavaScript 是如何工作的:Web Worker 的內部構造以及 5 種你應當使用它的場景
- [譯] JavaScript 是如何工作的:Web Worker 生命週期及用例
- [譯] JavaScript 是如何工作的:Web 推送通知的機制
- [譯] JavaScript 是如何工作的:用 MutationObserver 追蹤 DOM 的變化
- [譯] JavaScript 是如何工作的:渲染引擎和效能優化技巧
- [譯] JavaScript 是如何工作的:網路層內部 + 如何優化其效能和安全性
概覽
你也知道,動畫在創造吸引人的 web app 中扮演著重要的角色。隨著使用者越來越多地將注意力轉移到使用者體驗上,商家也開始意識到完美、愉悅的使用者體驗的重要性,web app 變得更加重要,並且 UI 更趨於動效。這一切都需要更復雜的動畫,以便在使用者使用中實現更平滑的狀態轉換。今天,這甚至不被認為是特別的。使用者正在變得越來越挑剔,預設期望高效響應和互動的使用者介面。
但是,動效化你的介面可沒那麼簡單。將什麼做成動畫,什麼時候,做成什麼樣的動畫,都是棘手的問題。
JavaScript 和 CSS 動畫
建立網頁動畫的兩種主要方式是使用 JavaScript 和 CSS。沒有絕對優劣; 一切都取決於你的目標。
CSS 動畫
用 CSS 實現動畫是讓螢幕上的內容移動的最簡單方法。
我們將以一個快速示例說明如何在 X 軸和 Y 軸上移動 50 畫素的元素。通過設定耗時 1000 ms 的 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
的值會改變,過渡開始。
除了過渡的時長,還有其它的用來緩動的選項,這些就是你看到的動畫的本質。稍後我們將在本文中更詳細地討論緩動。
如果像上面的程式碼片段那樣建立單獨的 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
類以觸發動畫。
這樣做可以為你的 app 提供很好的平衡。你可以專注於使用 JavaScript 管理狀態,並簡單地在目標元素上設定適當的類,讓瀏覽器處理動畫。如果沿著這條路線走下去,你可以監聽 transitionend
元素上的事件,但前提是你能夠放棄對舊版 Internet Explorer 的支援:
監聽 transitioned
過渡結束時觸發的事件,如下所示:
var boxElement = document.querySelector('.box'); // 獲取有 box 類的第一個元素。
boxElement.addEventListener('transitionend', onTransitionEnd, false);
function onTransitionEnd() {
// 處理過渡完成。
}
複製程式碼
除了 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;
}
}
複製程式碼
它的效果是這樣的(快速演示) — sessionstack.github.io/blog/demos/…
使用 CSS 動畫,你可以獨立於目標元素來定義動畫本身,並使用 animation-name
屬性選擇所需的動畫。
CSS 動畫有時仍然是需要瀏覽器屬性字首的,-webkit-
用於 Safari,Safari Mobile 和 Android。Chrome,Opera,Internet Explorer 和 Firefox 都會在沒有字首的情況下起作用。許多工具可以幫助你建立所需 CSS 的瀏覽器屬性字首,從而允許你在原始檔中編寫無字首的版本。
JavaScript 動畫
與使用 CSS 過渡或動畫相比,使用 JavaScript 建立動畫更復雜,但它通常為開發人員提供了更強大的功能。
JavaScript 動畫是作為程式碼的一部分內聯編寫的。你也可以將它們封裝在其他物件中。下面是你需要用 JavaScript 來編寫的重新建立前面描述的 CSS 過渡:
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)';
});
複製程式碼
預設情況下,Web 動畫僅修改元素的顯示。如果你想讓你的物件留在它被移動到的位置,那麼當動畫完成時你應該修改它的底層樣式。這就是為什麼我們要監聽 finish
事件,並將 box.style.transform
屬性設定為 translate(150px, 200px)
,這與我們動畫的第二個變換相同。
使用 JavaScript 動畫,你可以在每一步完全控制元素的樣式。這意味著你可以放慢動畫,暫停動畫,停止動畫,反轉動畫,並根據需要操作元素。如果你構建複雜的物件導向的 app,這一點尤其有用,因為你可以適當地封裝你的行為。
什麼是緩動?
自然動作讓你的使用者對你的 web app 感到更加舒適,從而帶來更好的使用者體驗。
自然情況下,沒有什麼東西是從一個點到另一個點做線性移動的。事實上,隨著它們在我們周圍的物質世界中移動,事物往往會加速或減速,因為我們並非處於真空狀態,並且存在影響這個因素的不同因素。人類的大腦受制於此會期望這種運動,所以當你為 app 製作動畫時,你應該利用這些知識為你帶來好處。
有一些術語需要了解一下:
- 「ease-in」 - 開始慢,然後加速。
- 「ease out」 - 開始快,然後減速。
兩個可以合併,比如「ease in out」。
緩動可以讓動畫感覺起來更自然。
緩動關鍵詞
CSS 過渡和動畫允許你選擇想要使用的緩動型別。有不同的會影響動畫緩動的關鍵詞。當然你完全可以使用自定義的緩動。
以下是你可以在 CSS 中用來控制緩動的一些關鍵詞:
linear
ease-in
ease-out
ease-in-out
我們逐一研究,看看究竟是什麼意思。
線性(linear
)動畫
沒有任何緩動的動畫稱為線性動畫。
以下是線性過渡的圖示:
隨著時間的推移,值會等量增加。使用線性運動時,總會感覺不自然。一般來說,你應該避免線性運動。
這是一個簡單的實現線性動畫的方式:
transition: transform 500ms linear;
緩出(ease-out
)動畫
前面已經說過,緩出動畫與線性動畫相比更快地開始,而後變慢。這就是它的圖示:
一般來講,緩出是使用者介面工作的最好選擇,因為快速開始給你一種快速響應的感覺,而因為不一致運動在結束時慢下來感覺比較自然。
有很多實現緩出效果的方法,但最簡單的就是使用 CSS 關鍵詞:
transition: transform 500ms ease-out;
複製程式碼
緩入(ease-in
)動畫
它與緩出相反 —— 開始慢,結束快。圖示如下:
與緩出相比,緩入感覺不太自然,因為它開始慢給人一種無響應的感覺。快速結束也很奇怪,因為整個動畫是在加速,而在現實世界中,物體在忽然停止時往往會減速。
要使用緩入動畫,類似於緩出或者線性動畫,使用關鍵詞:
transition: transform 500ms ease-in;
複製程式碼
緩入緩出(ease-in-out
)動畫
它是緩入和緩出的結合,圖示如下:
不要用於持續時間過長的動畫,這會讓人覺得你的使用者介面無響應。
使用 CSS 關鍵詞 ease-in-out
實現緩入緩出動畫:
transition: transform 500ms ease-in-out;
複製程式碼
自定義緩動
你可以定義自己的緩動曲線,從而更好地控制專案動畫的形成的感受。
實際上,ease-in
,ease-out
,linear
,ease
關鍵詞可以對應到預定義的貝塞爾曲線裡,這在CSS 過渡規範和網路動畫規範裡有詳細說明。
貝塞爾曲線
讓我們看一下貝塞爾曲線的工作原理。貝塞爾曲線有四個值,或者更確切地說,它需要兩對數字。每對描述三次貝塞爾曲線控制點的 X 和 Y 座標。貝塞爾曲線的起點座標是 (0, 0),終點座標是 (1, 1)。你可以設定這兩組數。兩個控制點的 X 值必須在 [0, 1] 範圍內,並且每個控制點的 Y 值可以超過 [0, 1] 限制,儘管規範沒有明確說超過多少。
即使每個控制點的 X 和 Y 值發生輕微變化,都會給你一個完全不同的曲線。我們來看看兩個貝塞爾曲線圖,點的座標相近但不同。
和
如你所見,兩個圖區別比較大。兩條曲線的第一個控制點的向量差為 (0.045, 0.183),第二個控制點差 (-0.427, -0.054)。
第二條曲線的 CSS 寫法如下:
transition: transform 500ms cubic-bezier(0.465, 0.183, 0.153, 0.946);
複製程式碼
前兩個數字是第一個控制點的 X 和 Y 座標,後兩個數字是第二個控制點的 X 和 Y 座標。
效能優化
無論何時動畫,你都應該保持 60 fps,否則會對使用者的體驗產生負面影響。
與世界上其他所有的東西一樣,動畫也是有代價的。動畫一些屬性比其他屬性更便宜。例如,動畫修改一個元素的 width
和 height
會改變它的形狀,而且可能引起頁面上其它元素的移動和形狀改變。這個過程稱為佈局。我們在之前的一篇文章中已經詳細討論過佈局和渲染。
一般來說,你應該避免使用觸釋出局或繪製的屬性動畫。對於大多數現代瀏覽器,這意味著將動畫(修改的屬性)限制為 opacity
和 transform
.
will-change
你可以使用 [will-change](https://dev.w3.org/csswg/css-will-change/)
通知瀏覽器你打算更改元素的屬性。瀏覽器會在你進行更改之前做最合適的優化。但不要過度使用 will-change
,因為這樣做會浪費瀏覽器資源,從而導致更多的效能問題。
可以這樣為變換和不透明度新增 will-change
:
.box { will-change: transform, opacity;}
複製程式碼
Chrome,Firefox 和 Opera 的瀏覽器支援非常好。
選 JavaScript 還是 CSS?
你可能知道了 —— 這個問題沒有正確或錯誤的答案。你只需要記住以下幾點:
- 基於 CSS 的動畫和原生支援的 Web 動畫通常在稱為「合成器執行緒」的執行緒上處理。它與瀏覽器的「主執行緒」不同,在該主執行緒中執行樣式,佈局,繪製和 JavaScript。這意味著如果瀏覽器在主執行緒上執行一些耗時的任務,這些動畫可以繼續執行而不會中斷。
- 在許多情況下,
transforms
和opacity
都可以在合成器執行緒中處理。 - 如果任何動畫出發了繪製,佈局,或者兩者,那麼「主執行緒」會來完成該工作。這個對基於 CSS 還是 JavaScript 實現的動畫都一樣,佈局或者繪製的開銷巨大,讓與之關聯的 CSS 或 JavaScript 執行工作、渲染都變得毫無意義。
選擇合適的物件來做動畫
優秀的動畫能讓使用者對你的專案的享受和參與感更添一層。無論你是喜歡寬度,高度,位置,顏色還是背景,你可以製作任何你喜歡的任何動畫,但你需要了解潛在的效能瓶頸。選擇不當的動畫會對使用者體驗產生負面影響,因此動畫需要兼具效能和適當性。動畫越少越好。動畫只是為了讓你的使用者體驗感覺自然,但不要過度使用動畫。
用動畫來增強互動
不要只是因為你能就做動畫。相反,使用策略性放置的動畫來增強使用者互動。避免不必要的中斷或阻礙使用者活動的動畫。
避免高代價動畫屬性
唯一比放置得不好的動畫還糟糕的是那些導致頁面卡頓的動畫。這種型別的動畫讓使用者感到沮喪和不快樂。
在 SessionStack 中使用動畫非常簡單。總的來說,我們遵循上述做法,但由於 UI 的複雜性,我們還有更多利用動畫的場景。SessionStack 必須像視訊一樣重新建立使用者在瀏覽 web app 時遇到問題時發生的所有內容。為此,SessionStack 僅利用會話期間我們的庫收集的資料:使用者事件,DOM 更改,網路請求,異常,除錯訊息等。我們的播放器經過高度優化,可以正確呈現和使用所有收集的內容資料,以便從視覺和技術角度出發,為終端使用者的瀏覽器及其中發生的所有事情提供畫素級的模擬。
為了確保複製得自然,尤其是在長時間和繁重的使用者會話中,我們使用動畫正確指示載入/緩衝,並遵循關於如何實現它們的最佳實踐,以便我們不佔用太多 CPU 時間並讓事件輪詢自由地渲染會話。
如果你想試試 SessionStack,有免費方案哦。
資源
- developers.google.com/web/fundame…
- developers.google.com/web/fundame…
- developers.google.com/web/fundame…
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。