幀的定義:1幅畫就叫作「1幀」,每秒幀數指的就是「每秒播放的畫面數」。每一幀都是靜止的圖象,快速連續地顯示幀便造成了運動的假象。高的幀率能夠獲得更流暢、更逼真的動畫。每秒鐘幀數 (fps) 愈多,所顯示的動做就會愈流暢。css
理論上說,FPS 越高,動畫會越流暢,目前大多數設備的屏幕刷新率爲 60 次/秒,因此一般來說 FPS 爲 60 frame/s 時動畫效果最好,也就是每幀的消耗時間爲 16.67ms。css3
直觀感覺,不一樣幀率的體驗:git
計算幀率的方法:github
setTimeout出現的問題:web
例子:算法
var FPS = 60;
setTimeout(draw, 1000/FPS);
複製代碼
上述代碼,若是draw帶有大量邏輯計算,致使計算時間超過幀等待時間時,將會出現丟幀。除外,若是FPS過高,超過了當時瀏覽器的重繪頻率,將會形成計算浪費,例如瀏覽器實際才重繪2幀,但卻計算了3幀,那麼有1幀的計算就浪費了。canvas
引入requestAnimationFrame,這個方法是用來在頁面重繪以前,通知瀏覽器調用一個指定的函數,以知足開發者操做動畫的需求。segmentfault
var fps = 30;
var now;
var then = Date.now();
var interval = 1000/fps;
var delta;
function tick() {
requestAnimationFrame(tick);
now = Date.now();
delta = now - then;
if (delta > interval) {
// 這裏不能簡單then=now,不然還會出現上邊簡單作法的細微時間差問題。例如fps=10,每幀100ms,而如今每16ms(60fps)執行一次draw。16*7=112>100,須要7次才實際繪製一次。這個狀況下,實際10幀須要112*10=1120ms>1000ms才繪製完成。
then = now - (delta % interval);
draw(); // ... Code for Drawing the Frame ...
}
}
tick();
複製代碼
transition:通常用來作過渡的, 沒時間軸的概念, 經過事件觸發(一次),沒中間狀態(只有開始和結束)
animate:作動效,有時間軸的概念(幀可控),能夠重複觸發和有中間狀態;
過渡的開銷比動效小,前者通常用於交互居多,後者用於活動頁居多;瀏覽器
大部分網站性能優化能夠對動畫有必定的優化做用,在這裏不累贅地細講了,詳情請看 -》網站性能優化實戰:juejin.im/post/5b0b7d…性能優化
本文主要講動畫的爲何會出現動畫卡頓問題,針對這種問題該如何解決。
Blink 內核早期架構
以 Chrome 瀏覽器內核 Blink 渲染頁面爲例。對早期的 Chrome 瀏覽器而言,每一個頁面 Tab 對應一個獨立的 renderer 進程,Renderer 進程中包含了主線程和合成線程。早期 Chrome 內核架構:
其中,主線程主要負責:
合成線程則主要負責:
主線程和合成線程的調度不合理會形成渲染出現卡頓等問題。
FPS(JS) = Time(主線程) + Time(合成線程)
FPS(CSS) = Time(合成線程)(有時候,例如opacity, transform)
在不頻繁觸發主線程時,css 動畫比 js 動畫節省性能。
若是任何動畫觸發了繪製,佈局,或者二者,那麼「主線程」會來完成該工做。這個對基於 CSS 仍是 JavaScript 實現的動畫都同樣,佈局或者繪製的開銷巨大,讓與之關聯的 CSS 或 JavaScript 執行工做、渲染都變得毫無心義。
例子:
transition:margin 2s;
複製代碼
在使用height,width,margin,padding做爲transition的值時,會形成瀏覽器主線程的工做量較重,例如從margin-left:-20px渲染到margin-left:0,主線程須要計算樣式margin-left:-19px,margin-left:-18px,一直到margin-left:0,並且每一次主線程計算樣式後,合成進程都須要繪製到GPU而後再渲染到屏幕上,先後總共進行20次主線程渲染,20次合成線程渲染,20+20次,總計40次計算。
主線程的渲染流程,能夠參考瀏覽器渲染網頁的流程:
也就是說,主線程每次都須要執行Scripts,Render Tree ,Layout和Paint這四個階段的計算。
transition:transform 2s;
複製代碼
而若是使用transform的話,例如tranform:translate(-20px,0)到transform:translate(0,0),主線程只須要進行一次tranform:translate(-20px,0)到transform:translate(0,0),而後合成線程去一次將-20px轉換到0px,這樣的話,總計1+20計算。
css 動畫卡頓的解決方案: