如何创建高性能 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 示例中对此进行测试,并使用 DevTools 探索性能。

Chrome DevTools

  1. 打开 Performance 面板。
  2. 记录运行时性能,同时动画正在发生。
  3. 检查 Summary 选项卡。

如果您在 Summary 选项卡中看到 Rendering 的非零值,则可能意味着您的动画正在使浏览器执行布局工作。

The Summary panel shows 37ms for rendering and 79ms for painting.
animation-with-top-left 示例会导致渲染工作。
The Summary panel show zero values for rendering and painting.
animation-with-transform 示例不会导致渲染工作。

Firefox DevTools

在 Firefox DevTools 中,瀑布图可以帮助您了解浏览器花费时间的位置。

  1. 打开 Performance 面板。
  2. 在动画发生时开始记录性能。
  3. 停止录制并检查 Waterfall 选项卡。

如果您看到 重新计算样式 的条目,则意味着浏览器必须返回到渲染瀑布图的开头以渲染动画。

检查是否丢帧

  1. 打开 Chrome DevTools 中的 Rendering 选项卡
  2. 启用 FPS 仪表复选框。
  3. 在动画运行时观看这些值。

注意 FPS 仪表 UI 顶部的 Frames 标签。这会显示类似 50% 1 (938 m) dropped of 1878 的值。高性能动画具有较高的百分比,例如 99%,这意味着丢帧很少,并且动画看起来很流畅。

The fps meter shows 50% of frames were dropped
animation-with-top-left 示例导致 50% 的帧被丢弃
The fps meter shows only 1% of frames were dropped
animation-with-transform 示例仅导致 1% 的帧被丢弃。

检查动画是否触发绘制

与绘制其他属性相比,某些属性的浏览器绘制成本更高。例如,任何涉及模糊效果(例如阴影)的属性都比绘制红色框需要更长的时间。这些差异在 CSS 中并不总是很明显,但浏览器 DevTools 可以帮助您确定哪些区域需要重新绘制,以及其他与绘制相关的性能问题。

Chrome DevTools

  1. 打开 Chrome DevTools 中的 Rendering 选项卡
  2. 选择 Paint Flashing
  3. 在屏幕上移动指针。
A UI element highlighted in green to demonstrate it will be repainted
在此 Google 地图示例中,您可以看到正在重新绘制的元素。

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

如果您需要确定特定属性是否导致与绘制相关的性能问题,Chrome DevTools 中的绘制分析器可以提供帮助。

Firefox DevTools

  1. 打开 Settings 并为 切换绘制闪烁添加一个工具箱按钮。
  2. 在您要检查的页面上,切换按钮为开启状态,然后移动鼠标或滚动以查看突出显示的区域。

结论

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

使用绘制分析器查看是否有任何绘制操作特别耗费资源。如果您发现任何问题,请检查不同的 CSS 属性是否能提供相同的外观和感觉,并具有更好的性能。

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