CSS3 动画卡顿性能优化解决方案--摘抄
最近在开发小程序,与vue类似,它们都有生命周期这回事。
onLoad 监听页面加载
onReady 监听页面初次渲染完成
onShow 监听页面显示
到底是什么意思?
所以这又触碰到了我的知识盲区,不过项目在磕磕绊绊中完成的差不多了,但是遇到了CSS3动画渲染的性能问题,所以我也是被逼的,再回过头来从浏览器渲染网页的流程出发,去找动画卡顿的症结。
浏览器渲染网页的流程如下:
使用 HTML 创建文档对象模型(DOM)
使用 CSS 创建 CSS 对象模型(CSSOM)
基于 DOM 和 CSSOM 执行脚本(Scripts)
合并 DOM 和 CSSOM 形成渲染树(Render Tree)
使用渲染树布局(Layout)所有元素
渲染(Paint)所有元素
可以结合Alon的这篇前端性能优化和安卓开发者选项的显示页面布局。
安卓开发者选项的显示页面布局
如何判断手机app是native,webview还是hybird?
简单说下,app中的一大块是白色的没有红线标记出来的,但是上面有按钮,图片等时,就是webview,也就是通过一个伪浏览器去请求到的数据,断网时打开app没有任何东西显示在上面
onLoad 监听页面加载
在渲染完界面之后,也就是通过.json中的配置项生成native界面后,开始渲染webview的部分,一个页面只会调用一次。
onReady 监听页面初次渲染完成
一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
onShow 监听页面显示
每次打开页面都会去调用其中的函数。
我们的动画应该放在哪里?
应该放在onShow里,因为这样我每次打开都能看到动画。
为什么会卡顿?
有一个前提必须要提,前端开发者们都知道,浏览器是单线程运行的。但是我们要明确以下几个概念:单线程,主线程和合成线程。
虽然说浏览器执行js是单线程执行(注意,是执行,并不是说浏览器只有1个线程,而是运行时,runing),但实际上浏览器的2个重要的执行线程,这 2 个线程协同工作来渲染一个网页:主线程和合成线程。
一般情况下,主线程负责:运行 JavaScript;计算 HTML 元素的 CSS 样式;页面的布局;将元素绘制到一个或多个位图中;将这些位图交给合成线程。
相应地,合成线程负责:通过 GPU 将位图绘制到屏幕上;通知主线程更新页面中可见或即将变成可见的部分的位图;计算出页面中哪部分是可见的;计算出当你在滚动页面时哪部分是即将变成可见的;当你滚动页面时将相应位置的元素移动到可视区域。
那么为什么会造成动画卡顿呢?
原因就是主线程和合成线程的调度不合理。
下面来详细说一下调度不合理的原因。
在使用height,width,margin,padding作为transition的值时,会造成浏览器主线程的工作量较重,例如从margin-left:-20px渲染到margin-left:0,主线程需要计算样式margin-left:-19px,margin-left:-18px,一直到margin-left:0,而且每一次主线程计算样式后,合成进程都需要绘制到GPU然后再渲染到屏幕上,前后总共进行20次主线程渲染,20次合成线程渲染,20+20次,总计40次计算。
主线程的渲染流程,可以参考浏览器渲染网页的流程:
使用 HTML 创建文档对象模型(DOM)
使用 CSS 创建 CSS 对象模型(CSSOM)
基于 DOM 和 CSSOM 执行脚本(Scripts)
合并 DOM 和 CSSOM 形成渲染树(Render Tree)
使用渲染树布局(Layout)所有元素
渲染(Paint)所有元素**
也就是说,主线程每次都需要执行Scripts,Render Tree ,Layout和Paint这四个阶段的计算。
而如果使用transform的话,例如tranform:translate(-20px,0)到transform:translate(0,0),主线程只需要进行一次tranform:translate(-20px,0)到transform:translate(0,0),然后合成线程去一次将-20px转换到0px,这样的话,总计1+20计算。
可能会有人说,这才提升了19次,有什么好性能提升的?
假设一次10ms。
那么就减少了约190ms的耗时。
会有人说,辣鸡,才190ms,无所谓。
那么如果margin-left是从-200px到0呢,一次10ms,10ms*199≈2s。
还会有人说,辣鸡,也就2s,无所谓。
你忘了单线程这回事了吗?
如果网页有3个动画,3*2s=6s,就是6s的性能提升。
由于数据是猜测的,所以暂时不考虑其真实性,文章后面我使用chrome devtools的performance做了一个实验。
要知道,在"客户至上"的今天,好的用户体验是所有产品的必须遵守的一条规则,无论是对于开发者还是产品经理,追求极致的性能都是我们打造一个好的产品所必备的品质。
可能看了我的略不专业的分析后,大家对主线程,合成线程以及它们在2种性能不同动画方案上的工作流程还不是很了解,可以去看一篇翻译过来的博客(英文原版链接已经失效了):深入浏览器理解CSS animations 和 transitions的性能问题
这篇文章完美讲述了浏览器主线程和合成线程的区别,并且举了一个高度从100px变化到200px的2种动画方案的对比,对主线程和合成线程的整个工作流程做了很详尽的讲解,真心建议认真阅读一遍。
回过头来总结下,css3动画卡顿的解决方案:
在使用css3 transtion做动画效果时,优先选择transform,尽量不要使用height,width,margin和padding。
transform为我们提供了丰富的api,例如scale,translate,rotate等等,但是在使用时需要考虑兼容性。但其实对于大多数css3来说,mobile端支持性较好,desktop端支持性需要格外注意。
补充:为了增强本文的说服力,特地回家做了一个实验,代码如下。
<!DOCTYPE html><html><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Page Title</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>.margin-transition{/* margin-left: 0; */background: rgba(0,0,255,0.3);transition: margin-left 1s;}.transform-transition{/* transform: translate(0,0); */background: rgba(0,255,0,0.3);transition: transform 1s;}.common{height: 300px;width: 300px;}</style></head><body><div class="margin-transition common" id="marginTransition"><p>transition:margin-left 1s</p></div><div class="transform-transition common" id="transformTransition"><p>transition:tranform 1s</p></div><button id="control">见证奇迹</button><script>var btn = document.getElementById('control');var marginTransition = document.getElementById('marginTransition');var transformTransition = document.getElementById('transformTransition');btn.addEventListener("click",function(){console.log(marginTransition.style,transformTransition.style)marginTransition.style.marginLeft = "500px";transformTransition.style.transform = "translate(500px,0)"})</script></body></html>
我将主要借助chrome devtools的performance工具对比二者的性能差异。
先来看margin动画,动态修改DOM节点的margin-left值从0到500px;。
transition: margin-left 1s;
再来看下transform动画,动态修改DOM节点的transform值从translate(0,0)到translate(500px,0)。
transition: transform 1s;
可能图片不是很好地能说明性能差异,那么我们来列一张耗时对比表,方便我们计算。
| 耗时 | margin | transform | 
|---|---|---|
| Summery | 3518ms | 2286ms | 
| Scripting | 1.8ms | 2.9ms | 
| Rendering | 22.5ms | 6.9ms | 
| Painting | 9.7ms | 1.6ms | 
| Other | 39.3ms | 25.2ms | 
| Idle( browser is waiting on the CPU or GPU to do some processing) | 3444.4ms | 2249.8ms | 
| GPU使用率 | 4.1MB | 1.7MB | 
通过上表我们可以计算出明margin,transform与transition组合实现CSS3动画效果时的性能差异参数。
| 关键性能参数 | margin | transform | 
|---|---|---|
| 实际动画耗时(总时间 减去 空闲时间) | 73.6ms | 36.2ms | 
计算得出,transform动画耗时约等于margin动画耗时的0.49倍,性能优化50%。
由于我对Other的所做的具体事情不是很清楚,所以这里的实际动画时间也有可能还要减掉Other中的时间,下表是我们减掉后的数据。
| 关键性能参数 | margin | transform | 
|---|---|---|
| 实际动画耗时(总时间 减去 其他时间和空闲时间) | 34.3ms | 11ms | 
计算得出,transform动画耗时约等于margin动画耗时的0.32倍,性能优化接近70%。
也就是说,无论我们减去还是不减去Other的时间,我们采用transform实现动画的方式都比margin动画快。
不精确的得出一个小结论:transform比margin性能好50%~70%。
虽然会有50%~70%的性能提升,但是需要注意硬件差异,硬件好的情况下可能不能发现卡顿或者其他的一些性能上的问题。
例如在开发小程序的过程中,模拟器是位于desktop端的,因此它的硬件性能性能更好,例如CPU,GPU。但是一旦在mobile端运行,例如ios或者android上运行时,就可能会出现性能问题,这就是因为移动端的硬件条件逊于PC端导致的。
所以说,性能问题是一直存在的,只不过硬件差异会导致性能影响的程度不同。
所以我们再次回过头来,总结出css3动画卡顿的解决方案:
在使用css3 transtion做动画效果时,优先选择transform,尽量不要使用height,width,margin和padding。
CSS3 动画卡顿性能优化解决方案--摘抄的更多相关文章
- javascript 手势(swipeLeft,swipeRight)滑动中使用css3动画卡顿,开启硬件加速
		
今天,在做一个移动端项目,遇到了css3动画卡顿的现象. 例图: 在手势滑动中(swipeLeft,swipeRight)遇到了动画卡顿的现象,最后使用了css3动画-webkit-transform ...
 - ViewPager -- Fragment 切换卡顿 性能优化
		
当ViewPager切换到当前的Fragment时,Fragment会加载布局并显示内容,如果用户这时快速切换ViewPager,即 Fragment需要加载UI内容,而又频繁地切换Fragment, ...
 - Chrome 下动画卡顿问题的另一种可能
		
[现象] 动画出现了明显的卡顿,且仅仅出现在 chrome 中. [原因排查] 一开始使用了css动画的时候已经出现了卡顿.找到如下的文章:CSS3 动画卡顿解决方案.深入浏览器理解CSS anima ...
 - performance面板使用,以及解决动画卡顿
		
https://googlechrome.github.io/devtools-samples/jank// 官方案例 https://juejin.im/post/5b65105f518825 ...
 - Android 界面滑动卡顿分析与解决方案(入门)
		
Android 界面滑动卡顿分析与解决方案(入门) 导致Android界面滑动卡顿主要有两个原因: 1.UI线程(main)有耗时操作 2.视图渲染时间过长,导致卡顿 目前只讲第1点,第二点相对比较复 ...
 - React-Native Navigator 过渡动画卡顿的解决方案
		
在RN0.44版本之前,路由导航跳转几乎是使用的是Navigator组件,在0.44版本以后就不推荐使用了,官方推荐的是react-navigation,当然还是可以在废弃的库中找到: import ...
 - CSS3动画的使用以及优化
		
CSS3 动画 目录 1. 定义动画2. animation 属性3. animation 属性的兼容4. animation与transition 属性的取别5. animate.css 动画库6. ...
 - 解决Chrome动画”卡顿”的办法
		
为动画DOM元素添加CSS3样式-webkit-transform:transition3d(0,0,0)或-webkit-transform:translateZ(0);,这两个属性都会开启GPU硬 ...
 - Spark记录-Spark性能优化解决方案
		
Spark性能优化的10大问题及其解决方案 问题1:reduce task数目不合适解决方式:需根据实际情况调节默认配置,调整方式是修改参数spark.default.parallelism.通常,r ...
 
随机推荐
- cocos2dx 3.x lua 网络加载并且保存资源(unix、linux)
			
#ifndef __DazzleParkour__TextLoader__ #define __DazzleParkour__TextLoader__ #include <stdio.h> ...
 - VS2013连接SQL Server 2008 R2测试
			
第一步,打开SQL Server 08,这里要说明一下,一定要开启服务,很多时候我们重启电脑以后,SQL Server的保留进程会被类似电脑管家之类的保护程序关闭,于是乎连接了半天的数据库都连不上. ...
 - Git - revert详解
			
git revert 撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销作为一次最新的提交 * git revert HEAD ...
 - k8s的高级调度方式
			
默认的scheduler的调度过程:1.预选策略:从所有节点当中选择基本符合选择条件的节点.2.优选函数:在众多符合基本条件的节点中使用优选函数,计算节点各自的得分,通过比较进行排序.3.从最高得分的 ...
 - C#基础-数组-ArrayList
			
数组ArrayList using System.Collections; //表示引入集合的命名空间 数组ArrayList容量本身是不固定的,根据存储的数据动态变化 // 声明一个ArrayLis ...
 - ipvsadm分发MySQL读请求
			
在MySQL的部署场景中,经常使用HAproxy和ipvs来作为读请求转发的网关.ipvs的好处在于本身不需要daemon的方式来运行,而是直接作为kernel的服务来提供:当ipvs和应用程序服务器 ...
 - destoon模块自定义字段的添加并让其支持搜索的方法
			
今天看了看模块设置里的自定义字段功能的用法,试着加了个新字段glry,设置了值,然后去数据库moduleid的article表看,字段成功加上了. 于是去template下article文件夹的lis ...
 - UVa - 1593 Unix ls(STL)
			
给你一堆文件名,排序后按列优先的方式左对齐输出. 假设最长文件名长度是M,那么每一列都要有M+2字符,最后一列有M字符. inmanip真NB..orz #include <iostream&g ...
 - C语言指针分析
			
/*************1*************/ int p; //p是一个普通的整型变量. /*************2*************/ int *p; //p与*结合,说明 ...
 - PAT Basic 1075
			
1075 链表元素分类 给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而 [0, K] 区间内的元素都排在大于 K 的元素前面.但每一类内部元素的顺序是不能 ...