CSS动画与GPU
写在前面
满世界的动画性能优化技巧,例如:
只允许改变
transform
、opacity
,其它属性不要动,避免重新计算布局(reflow)对动画元素应用
transform: translate3d(0, 0, 0)
、will-change: transform
等,开启硬件加速动画元素尽量用
fixed
、absolute
定位方式,避免reflow对动画元素应用高一点的
z-index
,减少复合层数量。。。其它可能有用的规则
那么问题是:已经小心遵守这些规则了,为什么动画还会卡顿、跳帧?还能优化吗?要从哪里入手?
一.硬件加速是非规范的
The most important thing I’d like to tell you before we dive deep into GPU compositing is this: It’s a giant hack. You won’t find anything (at least for now) in the W3C‘s specifications about how compositing works, about how to explicitly put an element on a compositing layer or even about compositing itself. It’s just an optimization that the browser applies to perform certain tasks and that each browser vendor implements in its own way.
很多情况下,开启硬件加速确实能带来明显的性能提升,但是,这部分内容是非规范的,W3C并没有相关规范说明其中细节,所以通过一些技巧(例如transform: translate3d(0, 0, 0)
)开启硬件加速是规范之外的行为,可能得到性能提升,也可能带来严重的性能问题
也许在将来会形成规范,依照规范去做肯定能获得性能提升,但在这之前,除了必须遵从各项性能优化原则外,还要考虑实际渲染流程,从原理上解决性能问题
硬件加速(Hardware Acceleration)
硬件加速在CSS动画上是指GPU合成(GPU compositing),浏览器不直接通过CPU生成图像数据显示出来,而是把相关层数据发送给GPU,而GPU在图像数据运算方面有天生优势,所以算是加速
那么当硬件加速不可用时,浏览器怎样渲染页面?
在没有硬件加速的情况下,浏览器通常是依赖于CPU来渲染生成网页的内容,大致的做法是遍历这些层,然后按照顺序把这些层的内容依次绘制在一个内部存储空间上(例如bitmap),最后把这个内部表示显示出来,这种做法就是软件渲染(software rendering)
二.transform和opacity的特殊性
以前通过改变布局相关属性形成动画,例如:
@keyframes move {
from { left: 30px; }
to { left: 100px; }
}
对于动画的每一帧,浏览器都要重新计算元素的形状位置(reflow),把新状态渲染出来(repaint),再显示到屏幕上
整页reflow和repaint想想就觉得很慢,那么如果把动画元素抽出来作为前景,每帧其它部分作为背景不变,只重新渲染动画元素,再把前景背景合成起来,是不是会更快?当然会,因为GPU能快速地进行亚像素级图层合成
但是这样做的前提是能够按照动的,不动的划分出前景背景层,如果动画元素或者受布局影响,或者动的过程中影响到了布局,就会打破前景背景的界限,这样简单分为2层就有问题。那么,应用position: fixed | absolute
是不是就能保证不会影响布局了?
不行,因为left
可以接受百分比值、相对单位(em、vw等等),浏览器不能百分百肯定该属性的变化与布局无关,所以不能简单的分出前景背景层,例如:
@keyframes move {
from { left: 30px; }
to { left: 100%; }
}
但浏览器能百分百肯定transform
和opacity
的变化与布局无关,不受布局影响,其变化也不会影响现有布局,所以这两个属性的特殊性是:
does not affect the document’s flow,
does not depend on the document’s flow,
does not cause a repaint.
如果不影响布局,且不受布局影响,其变化不会导致其它部分需要repaint,那么这个东西肯定可以抽出去单独作为一层,放心交给GPU去处理,享受硬件加速带来的好处;
细腻(GPU能做到亚像素级精度,且对GPU来说不费劲)
流畅(不受其它运算密集的JS任务影响,动画交给GPU了,与CPU无关)
三.GPU合成的代价
It might surprise you, but the GPU is a separate computer. That’s right: An essential part of every modern device is actually a standalone unit with its own processors and its own memory- and data-processing models. And the browser, like any other app or game, has to talk with the GPU as it would with an external device.
GPU是独立的一部分,有自己的处理器、内存核数据处理模型,那么意味着通过CPU在内存里创建的图像数据无法直接与GPU共享,需要打包发送给GPU,GPU收到后才能执行我们期望的一系列操作,这个过程需要时间,而打包数据需要内存
需要的内存取决于:
复合层的数量
复合层的大小
相对于数量,复合层的大小影响更大一些,例如:
.rect {
width: 320px;
height: 240px;
background: #f00;
}
这个红块如果要发送给GPU的话,需要的存储空间是:320 × 240 × 3 = 230400B = 225KB
(rgb需要3个字节),如果图像含有透明部分,就需要320 × 240 × 4 = 307200B = 300KB
这样一个不起眼的小红块就需要2、300KB,页面动辄几十上百个元素,占全屏半屏的元素也不少,如果都作为复合层,交给GPU,内存消耗可想而知,所以一些很极端的硬件加速场景性能非常差:
gpu compositing issue
对于1GB RAM的设备,去掉系统和后台进程的1/3
,再去掉浏览器和当前页面的1/3
,实际能用的只有200到300MB,如果复合层太多太大,内存会被迅速消耗,然后掉帧(卡顿、闪烁)现象,甚至浏览器/应用崩溃也就很合理了
P.S.详细见CSS3硬件加速也有坑!!!
四.创建复合层
浏览器在一些情况下会创建复合层,例如:
3D transforms: translate3d, translateZ and so on;
<video>, <canvas> and <iframe> elements;
animation of transform and opacity via Element.animate();
animation of transform and opacity via СSS transitions and animations;
position: fixed;
will-change;
filter;
。。。
还有很多,详细见CompositingReasons.h中定义的常量,分为几类:
这些大多是我们期望的,算是显式创建的复合层,而另一些情况也会创建复合层:
位于复合层之上的元素会被创建复合层(B的
z-index
大于A,对A做动画,B也会被**独立的复合层)
很容易理解,A在动画过程中可能会与B产生重叠,被B遮住,那么GPU需要每帧对A图层做动画,然后再与B图层合成,才能得到正确结果,所以B无论如何都要被**复合层,连同A一起交给GPU
隐式创建复合层主要出于重叠考虑,如果浏览器不确定会不会发生重叠,那么就要把不确定的东西都**复合层,所以,从这个角度看,高z-index
原则是有道理的
五.硬件加速的优缺点
优点
动画非常流畅,能达到60fps
动画执行过程在独立线程里,不受计算密集的JS任务影响
缺点
把元素**复合层时需要额外重绘,有时很慢(可能需要整页重绘)
复合层数据传递给GPU有额外时耗,取决于复合层的数量和大小,这在中低端设备可能会导致闪烁
每个复合层都要消耗一部分内存,移动设备上内存很贵,过多占用会导致浏览器/应用崩溃
存在隐式复合层的问题,不注意的话内存飙升
文字模糊,元素有时会变形
最主要的问题集中在内存消耗和repaint上,所以动画性能优化目标是降低内存消耗,减少repaint
六.性能优化技巧
1.尽量避免隐式复合层
复合层直接影响repaint、内存消耗:动画开始时创建复合层、结束时删除复合层,都会引起repaint,而动画开始时必须把图层数据发送给GPU,内存消耗集中在这里。两条建议:
给动画元素应用高
z-index
,最好直接作为body
的子元素,对于嵌套很深的动画元素,可以复制一个到body
下,仅用于实现动画效果给动画元素应用
will-change
,浏览器会提前把这些元素**复合层,可以让动画开始/结束时更流畅些,但不能滥用,在不需要的时候赶紧去掉,减少内存消耗
2.只改变transform和opacity
能用transform
、opacity
优先用,不能用的话想办法用,比如背景色渐变,可以用盖在上面的伪元素背景色opacity
动画模拟;box-shadow
动画可以用铺在下面的伪元素opacity
动画模拟,这些曲折的实现方式能带来显著性能提升
3.减少复合层的大小
小元素放大展示,减小width
、height
,减少传递给GPU的数据,由GPU做scale
放大展示,视觉效果无差异(多用于纯色背景元素,对不太重要的图片也可以进行5%
到10%
的宽高压缩),例如:
<div id="a"></div>
<div id="b"></div>
<style>
#a, #b {
will-change: transform;
background-color: #f00;
}
#a {
width: 100px;
height: 100px;
}
#b {
width: 10px;
height: 10px;
transform: scale(10);
}
</style>
最终显示的两个红色块在视觉上没有差异,但减小了90%
的内存消耗
4.考虑对子元素动画与容器动画
容器动画可能存在不必要的内存消耗,比如子元素之间的空隙,也会被当做有效数据发送给GPU,如果对各个子元素分别应用动画,就能避免这部分的内存消耗
例如12
道太阳光线旋转,转容器就把容器整张图都发送给GPU,单独转12
道光线就去掉了光线之间的11
条空隙,能够节省一半内存
5.早早关注复合层的数量和大小
从一开始就关注复合层,尤其是隐式创建的复合层,避免后期优化影响布局
复合层的大小比数量影响更大,但浏览器会做一些优化操作,把几个复合层整合成一个,叫Layer Squashing,但有时一个大复合层比几个小复合层消耗的内存更多,有必要的话可以手动去掉这种优化:
// 给每个元素应用不同的translateZ
translateZ(0.0001px), translateZ(0.0002px)
6.不要滥用硬件加速
没事不要乱加transform: translateZ(0)
、will-change: transform
等强制开启硬件加速的属性,GPU合成存在缺点和不足,而且是非标准的行为,最好情况能带来显著性能提升,最坏情况可能会让浏览器崩溃
参考资料
GPU Animation: Doing It Right:一篇文章看了一天
理解WebKit和Chromium: Chromium硬件加速合成
转自个人订阅号ayqy
CSS动画与GPU的更多相关文章
- CSS动画的性能分析和浏览器GPU加速
此文已由作者袁申授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 有数的数据大屏可以在一块屏幕上展示若干张不同的图表,以炫酷的方式展示各种业务数据.其中有些图表使用CSS实现了 ...
- 这样使用 GPU 渲染 CSS 动画(转)
大多数人知道现代网络浏览器使用GPU来渲染部分网页,特别是具有动画的部分. 例如,使用transform属性的CSS动画看起来比使用left和top属性的动画更平滑. 但是如果你问,“我如何从GPU获 ...
- 梅须逊雪三分白,雪却输梅一段香——CSS动画与JavaScript动画
CSS动画并不是绝对比JavaScript动画性能更优越,开源动画库Velocity.js等就展现了强劲的性能. 一.两者的主要区别 先开门见山的说说两者之间的区别. 1)CSS动画: 基于CSS的动 ...
- css动画属性性能
性能主要表现:流量.功耗与流畅度 在现有的前端动画体系中,通常有两种模式:JS动画与CSS3动画. JS动画是通过JS动态改写样式实现动画能力的一种方案,在PC端兼容低端浏览器中不失为一种推荐方案. ...
- css动画与js动画的区别
CSS动画 优点: (1)浏览器可以对动画进行优化. 1. 浏览器使用与 requestAnimationFrame 类似的机制,requestAnimationFrame比起setTimeout ...
- CSS动画属性性能详细介绍
CSS动画属性会触发整个页面的重排relayout.重绘repaint.重组recomposite Paint通常是其中最花费性能的,尽可能避免使用触发paint的CSS动画属性,这也是为什么我们推荐 ...
- 你所不知道的 CSS 动画技巧与细节
怕标题起的有点大,下述技巧如果你已经掌握了看看就好,欢迎斧正,本文希望通过介绍一些 CSS 不太常用的技巧,辅以一些实践,让读者可以更加深入的理解掌握 CSS 动画. 废话少说,直接进入正题,本文提到 ...
- 盒子端 CSS 动画性能提升研究
不同于传统的 PC Web 或者是移动 WEB,在腾讯视频客厅盒子端,接大屏显示器(电视)下,许多能流畅运行于 PC 端.移动端的 Web 动画,受限于硬件水平,在盒子端的表现的往往不尽如人意. 基于 ...
- 前端性能优化(css动画篇)
正巧看到在送书,于是乎找了找自己博客上记录过的一些东西来及其无耻的蹭书了~~~ 小广告:更多内容可以看我的博客 最近拜读了一下html5rocks上几位大神写的一篇关于CSS3动画性能优化的文章,学到 ...
随机推荐
- [Linux] 关于Centos6中ulimit nproc用户进程数的限制
一.缘由: 在启动mongodb的时候,有Warning提示soft rlimits too low,就是用户使用进程数过小,遂调高系统资源关于用户最大进程数的限制ulimit -u. 先暂时使设置生 ...
- jquery 实现重复点击一个元素时不重复执行效果
jquery 实现重复点击一个元素时不重复执行效果 这需要用到jquery的stop方法 实例 停止当前正在运行的动画: $("#stop").click(function(){ ...
- Integration Services创建ETL包
http://www.cnblogs.com/chiniao/archive/2009/12/23/1630595.html (转载) Microsoft Integration Services ...
- eclipse中 报出The type javax.servlet.http.HttpServlet cannot be resolved. It is indirect错误
在Myeclispe部署项目后 报错 The type javax.servlet.http.HttpServlet cannot be resolved. It is indirect错误 如果在M ...
- Framework4.5语法糖 异步Task
1.线程安全 在使用TaskRun的时候需要注意线程安全的问题. 线程安全通常是由全局变量及静态变量引起的,如果是值类型就不存在这样的隐患,如果是引用类型用不好就会导致线程不安全! 2.Task.Ta ...
- Ubuntu1404 (1)
0.初始设置 (1)开户root账号并重启系统: sudo gedit /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf, 添加greeter-show ...
- linux_oracle_healthcheck.sh
#!/bin/bash######################################################################################### ...
- selenium处理极验滑动验证码
要爬取一个网站遇到了极验的验证码,这周都在想着怎么破解这个,网上搜了好多知乎上看到有人问了这问题https://www.zhihu.com/question/28833985,我按照这思路去大概实现了 ...
- (转载)FT232RL通信中断问题解决办法总结
原文地址:http://cuiweidabing.blog.163.com/blog/static/66631928201101514021658/ FT232RL是FTDI(www.ftdichip ...
- 【HEVC】1、HM-16.7编码器的基本结构
编码器在整个HM解决方案中的工程名为TAppEncoder,入口点函数位于encmain.cpp文件中: int main(int argc, char* argv[]) { TAppEncTop c ...