如何创建高性能 CSS 动画

本指南将教您如何创建高性能 CSS 动画。

请参阅为什么有些动画速度很慢?,了解这些建议背后的理论。

浏览器兼容性

本指南推荐的所有 CSS 属性都具有良好的跨浏览器支持。

transform

浏览器支持

  • 36
  • 12
  • 16
  • 9

来源

opacity

浏览器支持

  • 1
  • 12
  • 1
  • 2

来源

will-change

浏览器支持

  • 36
  • 79
  • 36
  • 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 开发者工具和 Firefox 开发者工具提供许多工具,可帮助您弄清楚动画运行缓慢或出现故障的原因。

检查动画是否触发布局

使用 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. 检查 Summary 标签页。

如果您在 Summary 标签页中看到 Rendering 的非零值,可能表示您的动画让浏览器执行布局工作。

“Summary”面板显示渲染用时 37 毫秒,绘制用时 79 毫秒。
animation-with-top-left 示例会导致渲染工作。
“Summary”面板的渲染和绘制显示的值为零。
Animation-with-transform 示例不会导致渲染工作。

Firefox 开发者工具

在 Firefox 开发者工具中,Waterfall 可以帮助您了解浏览器将时间花在何处。

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

如果您看到重新计算样式的条目,则表示浏览器必须返回到呈现瀑布流的起始位置才能渲染动画。

检查是否存在丢帧

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

请注意 FPS 计量器界面顶部的标签。 这会显示类似 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. 选择 Paint Flashing
  3. 在屏幕上移动指针。
以绿色突出显示的界面元素,表示将会重新绘制
在这个来自 Google 地图的示例中,您可以看到正在重新绘制的元素。

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

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

Firefox 开发者工具

  1. 打开 Settings,然后为 Toggle Paint flashing 添加 Toolbox 按钮。
  2. 在您要检查的页面上,开启该按钮,然后移动鼠标或滚动以查看突出显示的区域。

总结

请尽可能将动画限制为 opacitytransform,以使动画保留在渲染路径的合成阶段。使用开发者工具检查路径的哪个阶段正受动画影响。

您可以使用绘制性能分析器查看任何绘制操作是否特别耗费资源。如果您发现任何信息,请检查其他 CSS 属性是否提供相同的外观和风格且性能更好。

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