[TOC]
# requestAnimationFrame 與動畫幀算法
在 **requestAnimationFrame** 這方法出現前,做動畫的時候,經常會看到有人去用 JS 定時器去做動畫,這其實很簡單,大家都寫過前端代碼,你只要一個 `setTimeInterval` 就可以了,你寫一個定時器,然后移動位置,不停的設置對象的 X 和 Y 值,比如說是 Top 和 left 這樣兩個值,不斷的去把它 + 1,這個對象就在屏幕上面動了。例如,我給他一個 16,假裝這個地方能做到 **60FPS**,實際上是達不到的,這實際上是有問題的,JS 腳本動畫,首先它有性能比較差的毛病,有兩個原因導致的。
```
setTimeInterval(()=> {
obj.x+=1;
obj.y+=1;
},16);
```
1. 就是定時器 Timer 實際上精度是不夠的,因為 Timer 本身是由瀏覽器去出發的,那實際上瀏覽器有可能達不到這精度,或者它有一個偏差值,并不能達到 16 毫秒精度,有時候多一點,有時候少一點,那就會造成一定不穩定,這是它第 1 個 Timer 的精度是不夠的。
2. 如果你是用這種定時器動畫去操作 DOM 的話,實際上效率就更差了,大家都知道 DOM 操作是很貴的,會引起 repaint 和 refollow,就是重繪和重排,那這樣性能就更差了,所以基本上現在沒有人會去用 JS 定時器去做動畫,再怎么差也得用一個 CSS3 提供的路子去做動畫。
```
function setp(){
obj.x+=1;
obj.y+=1;
requestAnimationFrame(step); // 注意這里
}
```
在做一幀的時候,比如說 `step`這個方法,往前走一步,那到最后再來調 `requestAnimationFrame`再把我們本身代碼存進去,相當于形成了看起來好像一個遞歸的樣子。
那么這個方法本身是由瀏覽器內核來調度的,瀏覽器內核會根據它的運行的情況來調度你的方法,也就說會確保你的方法在下一次瀏覽器渲染頁面之前會被得到調用,它只確保,但是時間并不是固定的。
請注意 `requestAnimationFrame` 并不是時間固定的一個長度,也就是說并不一定能達到 16.7 毫秒,雖然在 W3C 的官方規范里面,說能夠支持到 60fps,也就是 16.7 毫秒這個方法會被調用一次,但真正運行時會發現肯定不行的,調不了這么多。比如說你在運行一些比較耗時的這種腳本,或者在做一些很復雜的操作,那基本上有可能達到 100 毫秒甚至 200 毫秒都有。
# 兩張圖解釋CSS動畫的性能
## 簡介
它們的問題不在瀏覽器,而在設備,瀏覽器對兩者的支持都很好。但是為了達到非常平滑的效果,瀏覽器必須借助設備GPU的計算能力。在高端智能手機上不成問題,但對于早期和廉價的手機來說,它們可能根本沒有響應的硬件和系統API。這就導致最終的動畫效果很生硬。所以,只要你使用了過度和動畫,就在你能找到的最老、最差的設備上進行測試。
在手機上使用CSS動畫時很多時候會感到卡頓,然后網上很多教程說開啟GPU加速 transform: translate3d(0,0,0); 可解決,但是為什么開啟GPU加速就能讓動畫順滑呢?
## 從瀏覽器內部去理解下
JS是單線程的,但是瀏覽器可以開啟多個線程,渲染一個網頁需要兩個重要的線程來共同完成:
1. **Main Thread** 主線程
2. **Compositor Thread** 繪制線程(我自己翻譯的)
* 主線程的工作:
1. 運行JS
2. 計算 HTML 元素的 CSS 樣式
3. 布局頁面
4. 將元素繪制到一個或多個位圖中
5. 把這些位圖交給 Compositor Thread 來處理
* 繪制線程的工作:
1. 通過 GPU 將位圖繪制到屏幕上
2. 通知主線程去更新頁面中可見或即將可見的部分的位圖
3. 計算出頁面中那些部分是可見的
4. 計算出在滾動頁面時候,頁面中哪些部分是即將可見的
5. 滾動頁面時將相應位置的元素移動到可視區
我們知道如果長時間的執行 JS 會阻塞主線程,頁面就會出現各種的卡頓。
而繪制線程會盡量的去響應用戶的交互,頁面發生變化時,繪制線程會以每秒60幀(60fps是最適合人眼的交互,30fps以下的動畫,讓人感覺到明顯的卡頓)的間隔不斷重繪頁面。
* GPU 在如下方面很快:
1. 繪制位圖到屏幕上
2. 可不斷的繪制相同的位圖
3. 將同一位圖進行位移、旋轉、縮放 (就是動畫)
但是在將位圖加載到GPU內存中有點慢
關于兩張圖的正題來了
## 瀏覽器重新計算布局
> PS: 橙色方框的操作比較耗時,綠色方框的操作比較快速
~~~
div {
height: 100px;
transition: height 1s linear;
}
div:hover {
height: 200px;
}
~~~
一個從 height: 100px 到 height: 200px 的動畫按照下面的流程圖來執行各種操作

圖中有那么多的橙色方框,瀏覽器會做大量的計算,動畫就會卡頓。
因為每一幀的變化瀏覽器都在進行布局、繪制、把新的位圖交給 GPU 內存(這恰好是我們上面提到的GPU的短板)
雖然只改變元素高度但是很可能要同步改變他的子元素的大小,那瀏覽器就要重新計算布局,計算完后主線程再來重新生成該元素的位圖。
## 使用 transform 屬性的動畫
~~~
div {
transform: scale(0.5);
transition: transform 1s linear;
}
div:hover {
transform: scale(1.0);
}
~~~
流程圖如下:

很明顯,這么少的橙色方框,動畫肯定會流暢。
因為 transform 屬性不會改變自己和他周圍元素的布局,他會對元素的整體產生影響。
因此,瀏覽器只需要一次生成這個元素的位圖,然后動畫開始時候交給 GPU 來處理他最擅長的位移、旋轉、縮放等操作。這就解放了瀏覽器不再去做各種的布局、繪制等操作。
## chrome中執行對比
把上面的demo代碼在瀏覽器中執行下看下效果,[demo地址](http://ccforward.github.io/demos/css/animation.html)。
**`transition: height 1s linear`**

* * * * *
**`transform: scale(1.0)`**
同樣是改變大小的 scale 動畫

> [前端觀察-高性能 CSS3 動畫](https://www.qianduan.net/high-performance-css3-animations/)
> [讓你的網頁更絲滑(全)](https://cn.vuejs.org/v2/guide/transitions.html)
# js
[騰訊移動Web前端知識庫](https://github.com/AlloyTeam/Mars)
[https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/](https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/)
http://www.w3cplus.com/animation/animations.html
http://www.ui.cn/detail/193639.html
Web動畫 https://github.com/amfe/article/issues/34
https://imququ.com/post/js-animation.html
[Javascript高性能動畫與頁面渲染](http://www.infoq.com/cn/articles/javascript-high-performance-animation-and-page-rendering)
消除疑問:CSS動畫 VS JavaScript https://github.com/classicemi/blog/issues/3
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=98012088_dg&wd=CSS%20%E5%BC%80%E5%90%AFGPU%E5%8A%A0%E9%80%9F&oq=%E5%BC%80%E5%90%AFGPU%E5%8A%A0%E9%80%9F&rsv_pq=e284902b00026a74&rsv_t=a9d3OmOBTm4n4WrsuTmYfp9xB3Q6HA1oafZ6I3MB34D1Pg%2BmWeXaCJOV3Ls%2B8ocpNAo&rsv_enter=1&inputT=1918&rsv_sug3=23&rsv_sug1=11&rsv_sug2=0&rsv_sug4=3064