如何创建高性能 CSS 动画

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

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

浏览器兼容性

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

移动元素

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

例如,如需将某个项滑入视图中,请使用 translate

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

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

内容也可以旋转,如下例所示: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 中,此属性可以应用于任何选择器:

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

不过,该规范建议只对始终会发生变化的元素采用此方法。 如果上面的示例是用户可以滑入和滑出的边栏,也可能是这样。 您网页上的某些项目可能不会经常更改,因此最好使用 JavaScript 应用 will-change,这样在很有可能会发生更改的位置使用 JavaScript。您需要确保给浏览器足够的时间来执行所需的优化,然后在更改停止后移除该属性。

如果您需要在某种不支持 will-change 的极少数浏览器(目前很可能是 Internet Explorer)中强制创建图层,可以设置 transform: translateZ(0)

调试动画播放缓慢或卡顿

Chrome 开发者工具和 Firefox 开发者工具提供了许多工具来帮助您找出动画速度缓慢或卡顿的原因。

检查动画是否触发布局

使用 transform 以外的元素移动元素的动画可能速度很慢。在以下示例中,我在为 topleft 添加动画效果以及使用 transform 时获得了相同的视觉效果。

错误做法
.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”面板显示渲染用时 37 毫秒,绘制用时 79 毫秒。
animation-with-top-left 示例会导致渲染工作。
“Summary”面板的渲染和绘制显示的值为零。
Animation-with-transform 示例不会导致渲染工作。

Firefox 开发者工具

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

  1. 打开性能面板。
  2. 在动画播放过程中,在“Start Recording Performance”(开始录制性能)面板中。
  3. 停止录制并检查 Waterfall 标签页。

如果您看到重新计算样式对应的条目,则表示浏览器必须从呈现广告瀑布流的起始点开始。

检查动画是否掉帧

  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 而言,这并不总是很明显:background: red;box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5); 看起来不一定具有截然不同的性能特征,但它们确实存在。

浏览器开发者工具可以帮助您确定哪些区域需要重新绘制,以及与绘制相关的性能问题。

Chrome DevTools

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

如果您看到整个屏幕在闪烁,或者您认为不应更改的区域突出显示,您可以进行调查。

如果您需要深入了解特定属性是否因绘制而引起性能问题,可以使用 Chrome 开发者工具中的绘制性能分析器

Firefox 开发者工具

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

总结

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

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

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