(转载)CSS动画性能——重绘与重排(回流)

文章原链接
身为一个前端,只考虑动画怎样实现就够了么?也许后续的动画性能优化才是你最大的敌人。

为什么会有这篇博文,说来惭愧。虽然用过CSS3制作过大量的动画效果,但在PC端和移动端,动画表现时佳时不佳,会卡顿会掉帧,有大量动画的页面更是会使移动设备的耗电和发热状态达到跟玩高FPS大型手游一样。小动画的卡顿掉帧问题也够让人抓耳挠腮一段时日。这篇博客并不会给出解决方案(因为我也没找到解决方案),因为导致动画卡顿的原因数不胜数,比如低端安卓设备,纵使用transform,动画还是有可能从直接从一边运行一段时间…然后“瞬移”到另一边…,未知因素过多,故只是在此记录一下之前制作动画时未考虑到的知识。

一、浏览器渲染流程

说到动画性能,就不得不提到页面的渲染流程

  • 解析HTML,创建DOM树
  • 解析CSS,生成CSS规则树
  • 将DOM树与CSS规则树合并,构建渲染树(RenderingObject树)
  • 布局和绘制,重绘(repaint)和重排(reflow) (重排也称回流)
二、重绘和重排

对动画性能影响最大的,就是重绘和重排。且重排的代价比重绘要大。重排的花销跟render tree有多少节点需要重新构建有关系,假如在body最前面插入一个元素,会导致整个render tree回流,但如果是指body后面插入一个元素,则不会影响前面的元素重排。

  1. 当页面布局和几何属性改变时就需要重排。下述情况会发生浏览器重排:
  • 添加或者删除可见的DOM元素
  • 元素位置改变
  • 元素尺寸改变(包括:内外边距、边框厚度、宽度和高度等属性的改变)
  • 内容改变,例如:文本改变或者图片被另一个不同尺寸的图片替代
  • 页面渲染器初始化
  • 浏览器窗口尺寸改变
  • 对可见元素 display:none,或者对不可见元素 display:block 时
  • 激活伪类(:hover)
  • transition对宽高的处理,在整个transition的每一帧中,浏览器都要去重新布局,绘制页面(参考)
     根据改变的范围和程度,渲染树中或大或小的对应的部分也需要重新计算。有些改变会触发整个页面的重排:例如,当滚动条出现时。

浏览器重排必定导致重绘,但重绘不一定导致重排。

  1. 重绘何时发生:

当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观、风格,而不会影响布局的,比如 background-color,则称之为重绘。

  • 改变字体
  • 增加或者移除样式表
  • 内容变化,比如用户在input框中输入文字
  • 激活CSS伪类(:hover)
  • 脚本操作DOM (也有可能造成回流)
  • 计算 offsetWidth 和 offsetHeight 的属性
  • 设置style属性的值
3.渲染树变化的排队与刷新

由于每次重排都会产生计算损耗,大多数浏览器通过队列化修改并批量执行来优化重排过程。然而你可能会(经常不知不觉)强制刷新队列并要求计划任务立即执行。获取布局信息的操作会导致队列刷新,比如以下方法:

  • offsetTop,offsetLeft,offsetWidth,offsetHeight

  • scrollTop,scrollLeft,scrollWidth,scrollHeight

  • clientTop,clientLeft,clientWidth,clientHeight

  • width,height

  • getComputedStyle() (currentStyle in IE)

  • JS更改元素style

    以上属性和方法需要返回最新的布局信息,因此浏览器不得不执行渲染队列中的“待处理”变化并触发重排以返回正确的值。

在修改样式的过程中,最好避免使用上面列出的属性。它们都会刷新渲染队列,即使你是在获取最近未发生改变的或者与最新变化无关的布局信息。

  1. 最小化重绘和重排

最小化DOM访问次数,尽可能在JavaScript端处理
如果需要多次访问某个DOM节点,请使用局部变量存储它的引用
小心处理HTML集合,因为它实时联系着底层文档。把集合长度缓存到一个变量中,并在迭代中使用它
如果可能的话,使用速度更快的API,比如 querySelectorAll() 和 firstElementChild
要留意重绘和重排:批量修改样式时,“离线”操作DOM树,使用缓存,并减少访问布局信息的次数
动画中使用绝对定位
使用事件委托来减少事件处理器的数量
尽量避免用 transition 过渡会更改布局的属性,如果有位移之类的,考虑用transform + transition
制作动画时,尽量使用 CSS3 的 transform,因为 transform 属性不会改变元素的布局(更详细的知识可以参考:详谈层合成composite )
三、小动画卡顿解决方案

之前做过一个小动画,是一个元素的宽度由 0px 变到 40px,所经历时长是7s。我是怎么写的呢,常规思路:

.block{
    animation: change 7s linear;
}

@keyframes change{
    from{ margin-left: 0px; }
    to{ margin-left: -40px; }
}结果动画看起来很“卡”,在我看了上述重绘、重排的知识后,以为定是自己写的动画性能忒差导致的,遂按照上述规则改进了一下,将.block设置为了绝对定位,使其脱离文档流,margin-left 改成 left,无果,依然卡。

仔细一想,不对劲,虽然这个小动画性能不佳,但是整个页面只有这一个动画。仔细看了下自己定义的动画规则,7s变化40px…是否是间隔太长的缘故…遂将时长设置为4s,卡顿缓和了许多,设置时长越短,卡顿越不明显。

但关键是,要求制作的效果就是要7s变化40px,遂改用transform:translateX()。终于,在7s改变40px的情况下,也能做到丝滑流畅了。

但为什么transform在7s使就可以做到丝滑流畅,这里有两点猜想:有可能是因为transform开启了GPU加速,有可能动画对transform属性和非transform属性的渲染帧数不一样。但到底是为何,还有待求证。

但不管是因为什么,请记住这句话,动画配合transform食用更佳。

*参考:

《高性能的JavaScript》——Nicholas C.Zakas

css重排与重绘

页面重绘和回流以及优化

详解浏览器渲染页面过程

高性能JavaScript重排与重绘

回流与重绘:CSS性能让JavaScript变慢

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值