如何创建高性能 CSS 动画

本指南介绍了如何创建高性能 CSS 动画。

如需了解这些建议背后的理论,请参阅为什么某些动画运行缓慢?

浏览器兼容性

本指南建议的所有 CSS 属性均具有良好的跨浏览器支持。

transform

浏览器支持

  • Chrome:36.
  • Edge:12.
  • Firefox:16.
  • Safari:9.

来源

opacity

浏览器支持

  • Chrome:1.
  • Edge:12.
  • Firefox:1.
  • Safari:2.

来源

will-change

浏览器支持

  • Chrome:36.
  • Edge:79。
  • Firefox:36。
  • Safari:9.1.

来源

移动元素

如需移动元素,请使用 transform 属性的 translaterotation 关键字值。

例如,如需将项滑动到视图中,请使用 translate

.animate {
  animation: slide-in 0.7s both;
}

@keyframes slide-in {
  0% {
    transform: translateY(-1000px);
  }
  100% {
    transform: translateY(0);
  }
}

使用 rotate 旋转元素。以下示例会将元素旋转 360 度。

.animate {
  animation: rotate 0.7s ease-in-out both;
}

@keyframes rotate {
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(360deg);
  }
}

调整元素大小

如需调整元素的大小,请使用 transform 属性的 scale 关键字值。

.animate {
  animation: scale 1.5s both;
}

@keyframes scale {
  50% {
    transform: scale(0.5);
  }
  100% {
    transform: scale(1);
  }
}

更改元素的可见性

如需显示或隐藏某个元素,请使用 opacity

.animate {
  animation: opacity 2.5s both;
}

@keyframes opacity {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

避免使用会触发布局或绘制的属性

在将任何 CSS 属性(transformopacity 除外)用于动画之前,请先确定该属性对渲染流水线的影响。除非绝对必要,否则请避免使用任何会触发布局或绘制的属性。

强制创建图层

为什么某些动画运行缓慢?中所述,将元素放在新层上可让浏览器重新绘制它们,而无需重新绘制布局的其余部分。

浏览器通常可以很好地决定应将哪些项放在新层上,但您也可以使用 will-change 属性手动强制创建层。顾名思义,此属性会告知浏览器此元素将以某种方式更改。

在 CSS 中,您可以将 will-change 应用于任何选择器:

body > .sidebar {
  will-change: transform;
}

不过,规范建议您仅对始终即将更改的元素执行此操作。例如,对于用户可以滑入和滑出的边栏可能就是如此。对于不经常更改的元素,我们建议在可能发生更改时使用 JavaScript 应用 will-change。请务必给浏览器足够的时间来执行必要的优化,并在更改停止后移除媒体资源。

如果您想在不支持 will-change 的浏览器(最有可能是 Internet Explorer)中强制创建图层,可以设置 transform: translateZ(0)

调试动画运行缓慢或出现故障的问题

Chrome DevTools 和 Firefox DevTools 提供了许多工具,可帮助您找出动画运行缓慢或出现故障的原因。

检查动画是否触发布局

如果动画使用 transform 以外的元素移动元素,则很可能会很慢。以下示例比较了使用 transform 的动画与使用 topleft 的动画。

错误做法
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     top: calc(90vh - 160px);
     left: calc(90vw - 200px);
  }
}
正确做法
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     transform: translate(calc(90vw - 200px), calc(90vh - 160px));
  }
}

您可以在以下两个 Glitch 示例中对此进行测试,并使用开发者工具探索性能。

Chrome DevTools

  1. 打开效果面板。
  2. 在动画播放期间记录运行时性能
  3. 检查摘要标签页。

如果您在摘要标签页中看到渲染的值不为零,则可能表示您的动画正在让浏览器执行布局工作。

“摘要”面板显示渲染时间为 37 毫秒,绘制时间为 79 毫秒。
animation-with-top-left 示例会导致呈现工作。
“摘要”面板会显示渲染和绘制操作的零值。
animation-with-transform 示例不会导致呈现工作。

Firefox 开发者工具

在 Firefox 开发者工具中,瀑布图可以帮助您了解浏览器在哪些方面花费了时间。

  1. 打开效果面板。
  2. 在动画播放期间开始录制性能数据。
  3. 停止录制并检查 Waterfall 标签页。

如果您看到 Recalculate Style 条目,则表示浏览器必须返回到渲染广告瀑布流的开头部分才能渲染动画。

检查是否存在丢帧

  1. 在 Chrome 开发者工具中打开渲染标签页
  2. 选中 FPS 计量器复选框。
  3. 在动画运行时观察这些值。

请注意 FPS 计量器界面顶部的 Frames 标签。 系统会显示类似 50% 1 (938 m) dropped of 1878 的值。高性能动画的百分比较高(例如 99%),这意味着只有很少的帧会被丢弃,并且动画看起来比较流畅。

fps 计量器显示 50% 的帧被丢弃
animation-with-top-left 示例会导致 50% 的帧被丢弃
fps 计量器显示仅有 1% 的帧丢失
animation-with-transform 示例只会导致 1% 的帧被丢弃。

检查动画是否会触发绘制

浏览器绘制某些属性的开销比其他属性要高。例如,绘制任何涉及模糊(例如阴影)的元素所花的时间将比绘制一个红框的时间要长。这些差异在 CSS 中并不总是显而易见,但浏览器开发者工具可以帮助您确定哪些区域需要重新绘制,以及其他与绘制相关的性能问题。

Chrome DevTools

  1. 在 Chrome 开发者工具中打开渲染标签页
  2. 选择绘制闪烁
  3. 在屏幕上移动指针。
以绿色突出显示的界面元素,表示该元素将重新绘制
在这个 Google 地图中的示例,您可以看到元素正在重新绘制。

如果您看到整个屏幕闪烁,或看到您认为不应发生变化的区域突出显示,请进一步调查。

如果您需要确定特定属性是否会导致与绘制相关的性能问题,Chrome 开发者工具中的绘制性能分析器可以派上用场。

Firefox 开发者工具

  1. 打开设置,然后为切换绘制闪烁添加 Toolbox 按钮。
  2. 在要检查的网页上,将该按钮切换到开启状态,然后移动鼠标或滚动屏幕即可查看突出显示的区域。

总结

请尽可能将动画限制为 opacitytransform,以便将动画保留在渲染路径的合成阶段。使用 DevTools 检查路径的哪个阶段受到动画的影响。

使用绘制性能分析器查看是否有任何绘制操作开销特别大。如果发现任何问题,请检查是否可以使用其他 CSS 属性实现相同的外观和风格,同时获得更好的性能。

请谨慎使用 will-change 属性,仅在遇到性能问题时才使用。