css3动画屏前幕后

我们常用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

  1. CSS transform
    • position: transform: translate(?px, ?px)
    • scale: transform: scale(?)
    • rotate: transform: rotate(?deg)
  2. opacity: opacity: 0...1

举个栗子

有了上面的粗略认识,让我们来看看浏览器的main thread和compositor thread是怎么处理css动画的。

transition: height

1
2
3
4
5
6
7
div {
height: 100px;
transition: height 1s linear;
}
div:hover {
height: 200px;
}

main thread和compositor thread的Timeline 图如下,橙色代表耗时操作,蓝色代表快的操作
animate-height-2x

我们可以看到有很多黄色耗时的操作。transition的每一帧操作,浏览器都要layout, painting, and uploading new bitmaps to the GPU.
浏览器之所以每一帧都这么生硬是因为元素的内容不断的变化,改变一个元素的高度可能会导致其子元素的大小也会改变,所以浏览器必须执行布局。布局结束后,main thread必须重新生成元素的位图。

transition: transform

1
2
3
4
5
6
7
div {
transform: scale(0.5);
transition: transform 1s linear;
}
div:hover {
transform: scale(1.0);
}

Timeline 图如下:
animate-transform-2x

如我们所见,CSS transform不改变元素或元素周围的布局。
只需要生成该元素的位图并上传到GPU在动画的开始。之后,浏览器不需要做更多的布局,绘画,或位图上传。反而,浏览器会借助GPU的特殊能力来让元素改变位置、放大、旋转。

触发重新布局的属性

有些节点,当你改变他时,会需要重新布局(这也意味着需要重新计算其他被影响的节点的位置和大小)。这种情况下,被影响的DOM树越大(可见节点),重绘所需要的时间就会越长,而渲染一帧动画的时间也相应变长。








widthheightpaddingmargin
displayborder-widthbordertop
positionfont-sizefloattext-align
overflow-yfont-weightoverflowleft
font-familyline-heightvertical-alignright
clearwhite-spacebottommin-height

更多请见:http://goo.gl/lPVJY6

触发重绘的属性

重绘需要重新上传到GPU,在移动设备上,这是特别昂贵,因为CPU没有PC强大,这意味着绘画的工作需要更长的时间;以及CPU和GPU之间的带宽是有限的,所以纹理上传需要很长的时间。






colorborder-stylevisibilitybackground
text-decorationbackground-imagebackground-positionbackground-repeat
outline-coloroutlineoutline-styleborder-radius
outline-widthbox-shadowbackground-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