Android 姬与 CSS3 动画不得不说的故事

最近公司做一个H5抽奖活动,要求将中奖名单(??不存在的)显示在页面中,效果类似直播平台的弹幕一样飘过 。心想简单啊,直接定位用animate动画就解决了,然而…

此处输入图片的描述

问题来了,问题来了,问题来了!在测试的时候,发现部分安卓机型出现卡卡卡卡顿的现象,实际动画效果引起强烈不适,有一种撸多了的感觉…Android 姬妮怎么老是出问题呀(╯‵□′)╯︵┻━┻ 。

经过一番周折,两遁空门,叁叁来迟,似李狗贼终于让我找见了解决卡顿的方法 。

方案:

  1. 尽量使用 transform 当成动画处理,避免使用 height,width,margin,padding 等;

  2. 要求较高时,可以开启浏览器开启GPU硬件加速
    webkit-transform: translate3d( 0,0,0 );
    -moz-transform: translate3d( 0,0,0 );
    -ms-transform: translate3d( 0,0,0 );
    -o-transform: translate3d( 0,0,0 );
    transform: translate3d( 0,0,0 );

那到底是为什么 Android 姬会出现卡顿的现象呢?为什么亲儿子 IOS 没有受到遗传呢?这后面究竟是道德的伦桑还是人性的扭曲?欢迎收看《 走进科学 - Android 下的C3动画》。

令人吃鲸的真相

从浏览器的引擎工作原理方面

深入浏览器理解 CSS animations 和 transitions 的性能问题 一文中提到,现代的浏览器通常会有两个重要的执行线程,这2个线程协同工作来渲染一个网页:主线程和合成线程 。

一般情况下,主线程负责:

  • 运行 JavaScript
  • 计算 HTML 元素的 CSS 样式。
  • 页面的布局
  • 将元素绘制到一个或多个位图中
  • 将这些位图交给合成线程

相应地,合成线程负责:

  • 通过 GPU 将位图绘制到屏幕上
  • 通知主线程更新页面中可见或即将变成可见的部分的位图
  • 计算出页面中哪部分是可见的
  • 计算出当你在滚动页面时哪部分是即将变成可见的
  • 当你滚动页面时将相应位置的元素移动到可视区域

使用 height,width,margin,padding,top,left 占用主线程比较耗时。而使用 transform 浏览器只需要一次生成这个元素的位图,并在动画开始的时候将它提交给GPU去处理 。

通过DevTools从文档对象模型到像素级别的观察

下图是 Chrome 浏览器中动画的 timeline,瀑布流越高,浏览器为了计算每个像素,就做的越多。

timeline

由此也能验证以上得出的结论 。

文章 High Performance Animations 提到:

We’re going to cut straight to the chase. Modern browsers can animate four things really cheaply: position, scale, rotation and opacity. If you animate anything else, it’s at your own risk, and the chances are you’re not going to hit a silky smooth 60fps.

现代浏览器在完成 position,scale,rotation,opacity 四种属性的动画时,消耗成本较低 。

捉急的项目证实

为了验证以上的理论,我将两种写法做了比对 。

下图是我之前的方法,使用定位改变 right 的值使元素从右到左移动,但是可以看到这种写法的 FPS 平均只有 30 。我们知道每秒 60 帧是最适合人眼交互的,FPS 小于 60 人眼能明显感觉的到,这就是为什么卡顿的原因 。

c3-animate

下图使用了 transform 方法改变 translateX 的值,FPS 明显高过 60 帧 ,惊不惊喜? 意不意外?

c3-transform

总结

为了做到兼容性好,流畅度高的动画,一定要对自己的代码做好优化,尽可能将动画元素absolute化以避免影响文档树,造成大面积重新计算layout 。动画处理使用浏览器渲染快的 transform 属性,尽量避免改变 height,width,margin,padding 等,要求较高时还可以开启浏览器 GPU 硬件加速( transform: translate3d( 0,0,0 ) ) 。

PS:

暗中观察