[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 的动画按照下面的流程图来执行各种操作
![](https://box.kancloud.cn/9efbff6b4086e4a7b092d5b09aaed5dc_640x1441.png)
图中有那么多的橙色方框,浏览器会做大量的计算,动画就会卡顿。
因为每一帧的变化浏览器都在进行布局、绘制、把新的位图交给 GPU 内存(这恰好是我们上面提到的GPU的短板)
虽然只改变元素高度但是很可能要同步改变他的子元素的大小,那浏览器就要重新计算布局,计算完后主线程再来重新生成该元素的位图。
## 使用 transform 属性的动画
~~~
div {
transform: scale(0.5);
transition: transform 1s linear;
}
div:hover {
transform: scale(1.0);
}
~~~
流程图如下:
![](https://box.kancloud.cn/0fedba58ae64cd88075c281d401dd020_640x1186.png)
很明显,这么少的橙色方框,动画肯定会流畅。
因为 transform 属性不会改变自己和他周围元素的布局,他会对元素的整体产生影响。
因此,浏览器只需要一次生成这个元素的位图,然后动画开始时候交给 GPU 来处理他最擅长的位移、旋转、缩放等操作。这就解放了浏览器不再去做各种的布局、绘制等操作。
## chrome中执行对比
把上面的demo代码在浏览器中执行下看下效果,[demo地址](http://ccforward.github.io/demos/css/animation.html)。
**`transition: height 1s linear`**
![](https://box.kancloud.cn/ef623e0c67e5932bb50a6770a74db930_640x353.png)
* * * * *
**`transform: scale(1.0)`**
同样是改变大小的 scale 动画
![](https://box.kancloud.cn/11ad7f8e683b516e950f9e836160cfb4_640x328.png)
> [前端观察-高性能 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