ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[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