我们常用css3的Animations或者Transitions来做动画,有些动画会很流畅,有些却不连贯。为什么?
首先让我们先来了解一下浏览器是怎样工作的。
浏览器内部
通过main thread和compositor thread来渲染页面
main thread:
- 执行javascript
- 计算HTML元素的节点样式
- 为每个节点生成图形和位置
- 将每个节点绘制填充到位图中
- 将这些位图交给排序compositor thread
compositor thread:
- 通过GPU将位图绘制到屏幕
- 叫main thread更新可见的位图或者即页面上将可见的部分。
- 计算出页面的哪些部分是可见的
- 计算哪些部分是即将可见的当你滚动
- 移动部分页面当你滚动的时候
GPU
really fast at:
- 绘制到屏幕
- 重复绘制相同的位图
- 绘制在不同位置,旋转,放大的位图。
relatively slow at:
- 加载位图到内存
浏览器fast to animate
- CSS transform
- position:
transform: translate(?px, ?px)
- scale:
transform: scale(?)
- rotate:
transform: rotate(?deg)
- position:
- opacity:
opacity: 0...1
举个栗子
有了上面的粗略认识,让我们来看看浏览器的main thread和compositor thread是怎么处理css动画的。
transition: height
1 | div { |
main thread和compositor thread的Timeline 图如下,橙色代表耗时操作,蓝色代表快的操作
我们可以看到有很多黄色耗时的操作。transition的每一帧操作,浏览器都要layout, painting, and uploading new bitmaps to the GPU.
浏览器之所以每一帧都这么生硬是因为元素的内容不断的变化,改变一个元素的高度可能会导致其子元素的大小也会改变,所以浏览器必须执行布局。布局结束后,main thread必须重新生成元素的位图。
transition: transform
1 | div { |
Timeline 图如下:
如我们所见,CSS transform不改变元素或元素周围的布局。
只需要生成该元素的位图并上传到GPU在动画的开始。之后,浏览器不需要做更多的布局,绘画,或位图上传。反而,浏览器会借助GPU的特殊能力来让元素改变位置、放大、旋转。
触发重新布局的属性
有些节点,当你改变他时,会需要重新布局(这也意味着需要重新计算其他被影响的节点的位置和大小)。这种情况下,被影响的DOM树越大(可见节点),重绘所需要的时间就会越长,而渲染一帧动画的时间也相应变长。
width | height | padding | margin |
display | border-width | border | top |
position | font-size | float | text-align |
overflow-y | font-weight | overflow | left |
font-family | line-height | vertical-align | right |
clear | white-space | bottom | min-height |
更多请见:http://goo.gl/lPVJY6
触发重绘的属性
重绘需要重新上传到GPU,在移动设备上,这是特别昂贵,因为CPU没有PC强大,这意味着绘画的工作需要更长的时间;以及CPU和GPU之间的带宽是有限的,所以纹理上传需要很长的时间。
color | border-style | visibility | background |
text-decoration | background-image | background-position | background-repeat |
outline-color | outline | outline-style | border-radius |
outline-width | box-shadow | background-size |
更多请见:http://goo.gl/lPVJY6
总结
避免使用会触发重布局和重绘的属性,尽量使用浏览器绘制效率较高transform 和 opacity(GPU在绘画时只是简单的降低之前已经画好的纹理的alpha值来达到效果,并不需要整体的重绘)。
一些问题
- 解决Chrome动画”卡顿”的办法:
1
2
3-webkit-transform:transition3d(0,0,0);
或
-webkit-transform:translateZ(0);
开启GPU硬件加速模式,从而让浏览器在渲染动画时从CPU转向GPU,我们设置值为0后,并没有真正使用3D效果,但浏览器却因此开启了GPU硬件加速模式。需谨慎,使用GPU可能会导致严重的性能问题,因为它增加了内存的使用,而且它会减少移动端设备的电池寿命。
- 使用了CSS3的Transition属性Chrome可能会导致浏览器频繁闪烁或抖动,可以使用下面的解决方案
1
2-webkit-backface-visibility: hidden;(设置进行转换的元素的背面在面对用户时是否可见:隐藏)
-webkit-transform-style: preserve-3d; (设置内嵌的元素在 3D 空间如何呈现:保留 3D )
参考文献:
【1】 High Performance Animations
【2】 CSS animations and transitions performance:
looking inside the browser