面试官问:“你做过哪些优化?”
答:“使用高性能的css属性,使动画流畅,让用户有更好的动画体验”
面试官:“你是怎么做的,为什么这么做可以优化?”
答:“。。。”
对于有些性能做的不是很好的网站,我们通常会看到它们页面上的动画效果卡顿。
我们知道通常情况下帧速率达到60FPS,人眼就会感觉动画流畅,也就是前一帧画面到后一帧画面的这中间的绘制时间不超过16.6ms,才能让人感觉不到卡顿。
要完成一次完整的渲染需要走过一个完整的流水线,我们称之为关键渲染路径。
构架dom,cssom,合成样式树,构建布局树,分层,绘制,分块,光栅化,画
动画是有很多帧的,如果每帧页面上的元素都经过这样一个流程,这也太慢了。
于是我们想,可不可以页面元素只经过部分步骤,毕竟加载完页面他们已经走过完整流程了,动画只是页面更新的操作。
还有是不是不用所有元素都经过这个步骤,仅仅只是需要动起来的元素才去绘制呢
这还真是可以的。
当我们在动画中改动了布局上的变化,比如宽高,内外边距等,这些都会影响其子节点或者兄弟节点,这就导致这一帧的变动需要从layout布局树这一步开始改动,以及后续的分层,绘制等等,这是非常低效的。
所以我们在做动画时,尽量不要去频繁变动这些布局属性,我甚至看到过有同学用定时器定时改动某个元素的宽高达到变大的动画效果。
有些动画也会涉及背景或字体颜色的变化,这种变化不会影响布局变化,它会从绘制这一步开始改动,但仍然不是最有效的。
css中有两个低成本做动画的属性:opacity和transform。因为在最后一步画阶段才会去查看元素的透明度,旋转位移等属性,所以动画使用这两个元素去改动,只影响画这个阶段。
并且他们还是发生在合成线程,不阻塞主渲染线程,就算主线程被js卡死,他仍然能正常进行动画。
接下来说说,如何只让需要动起来的元素才去绘制呢,毕竟其他元素我也不需要他动。
浏览器已经对此做了一些优化,它预想到你可能会对某些元素进行改动属性,浏览器通过复杂的逻辑将页面进行了分层,把它认为将来可能会改动的元素与不太可能改动的元素分到不同层,这样他在改动时,就可能不去动那些不太容易会变动的层。
那么我们可以根据浏览器这个策略,促使它将我们知道需要动画的元素分到单独一层。
css有一个属性will-change,他可以告知浏览器将来我们可能会对某个属性进行变更。
那么浏览器就会提前对他分一个层,但是如果没用好他有可能还不是优化。
因为分层是有内存开销的,分了过多的层,占用过多的内存,甚至会出现反向效果,使得页面卡顿。
所以我们只对那些确定会变动的动画,甚至于这个动画并没有很卡顿,我们无需对他分层。
还得注意,will-change属性最好是用js动态添加和删除,因为提前设置可能会让浏览器提前去消耗内存做准备,
这在动画开始前是没有必要的,动画结束后我们也应该用js手动给它释放掉。
参考:
https://web.dev/articles/animations-overview?hl=zh-cn