Description
自从有了前端开发这个概念以来,其实这个岗位所做的事情都是围绕着人机交互来开展的,主要包括展示信息给用户看,然后获取用户的意图并做出响应。一个高质量的动画在很多情况下都能大大提升用户的视觉体验。
实现动画的方法
- 视频或GIF图片
- Flash
- 通过JS改变DOM节点的CSS属性
- Canvas
- WebGL
动画渲染技术的趋势
Canvas + WebGL
前端动画分类
- JavaScript控制的动画
- CSS控制的动画
JS动画
JS动画的原理是通过setTimeout
setInterval
或requestAnimationFrame
方法绘制动画帧(render),从而动态地改变网页中图形的显示属性(如DOM样式,canvas位图数据,SVG对象属性等),进而达到动画的目的。
多数情况下,应 首先选用 requestAnimationFrame
方法(RAF),因为RAF的原理是会在浏览器下一次重绘之前更新动画,即它的刷新频率和浏览器自身的刷新频率保持一致(一般为60Hz),从而确保了性能。另外RAF在浏览器切入后台时会暂停执行,也可以提升性能和电池寿命
CSS动画
css动画有以下特点:
优点
- CSS动画实现比较简单
- CSS动画执行与JS主线程无关,例如在Chromium里,css动画运行在compositor thread线程中,即使你js线程卡住,css动画照常执行
- 强制使用硬件加速,能有效利用GPU
缺点
- 只能操作DOM或XML对象的部分属性
- 动画控制能力薄弱,不能逐帧定义动画状态
- 支持的缓动函数有限(CSS3动画的贝塞尔曲线是一个标准3次方曲线)
- 滥用硬件加速也会导致性能问题
CSS动画渲染优化原理
如上所说,渲染树构建完成后;浏览器要做的步骤:
reflow——》repaint——》composite
reflow和repaint
reflow和repaint都是耗费浏览器性能的操作,这两者尤以reflow为甚;因为每次reflow,浏览器都要重新计算每个元素的形状和位置。
由于reflow和repaint都是非常消耗性能的,我们的浏览器为此做了一些优化。浏览器会将reflow和repaint的操作积攒一批,然后做一次reflow。但是有些时候,你的代码会强制浏览器做多次reflow。例如:
var content = document.getElementById('content');
content.style.width = 700px;
var contentWidth = content.offsetWidth;
content.style.backgound = 'red';
以上第三行代码,需要浏览器reflow后;再获取值,所以会导致浏览器多做一次reflow。
下面是一些针对reflow和repaint的最佳实践:
- 不要一条一条地修改dom的样式,尽量使用className一次修改。
- 将dom离线后修改
- 使用documentFragment对象在内存里操作dom。
- 先把dom节点display:none;(会触发一次reflow)。然后做大量的修改后,再把它显示出来。
- clone一个dom节点在内存里,修改之后;与在线的节点相替换。
- 不要使用table布局,一个小改动会造成整个table的重新布局。
- transform和opacity只会引起合成,不会引起布局和重绘。
从上述的最佳实践中你可能发现,动画优化一般都是尽可能地减少reflow、repaint的发生。
composite
composite在GPU中进行
为了仅发生composite,我们做动画的css property必须满足以下三个条件:
- 不影响文档流。
- 不依赖文档流。
- 不会造成重绘。
满足以上以上条件的css property只有transform和opacity。
但GPU加速会造成大量内存占用,使用不当会引起反效果。
GPU动画的优点和缺点
优点:
- 每秒60帧,动画平滑、流畅。
- 一个合适的动画工作在一个单独的线程,它不会被大量的js计算阻塞。
- 3D“变换”是便宜的。
缺点:
- 提升一个元素到复合层需要额外的重绘,有时这是慢的。(即我们得到的是一个全层重绘,而不是一个增量)
- 绘图层必须传输到GPU。取决于层的数量和传输可能会非常缓慢。这可能让一个元素在中低档设备上闪烁。
- 每个复合层都需要消耗额外的内存,过多的内存可能导致浏览器的崩溃。
- 如果你不考虑隐式合成,而使用重绘;会导致额外的内存占用,并且浏览器崩溃的概率是非常高的。
- 我们会有视觉假象,例如在Safari中的文本渲染,在某些情况下页面内容将消失或变形。
优化技巧
- 避免隐式合成
- 保持动画的对象的z-index尽可能的高。理想的,这些元素应该是body元素的直接子元素。当然,这不是总可能的。所以你可以克隆一个元素,把它放在body元素下仅仅是为了做动画。
- 将元素上设置will-change CSS属性,元素上有了这个属性,浏览器会提升这个元素成为一个复合层(不是总是)。这样动画就可以平滑的开始和结束。但是不要滥用这个属性,否则会大大增加内存消耗。
- 动画中只使用transform和opacity
- 减小复合层的尺寸