如何创建高性能 CSS 动画

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

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

浏览器兼容性

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

transform

Browser Support

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

Source

opacity

Browser Support

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

Source

will-change

Browser Support

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

Source

移动元素

如需移动元素,请使用 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 的浏览器中强制创建图层,您可以设置 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));
  }
}

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

Chrome DevTools

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

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

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

Firefox 开发者工具

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

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

如果您看到 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 属性,仅在遇到性能问题时才使用。