性能
影响 Web 动画性能的主要因素有两个:渲染 和 硬件加速。
渲染是指获取 DOM 的更新并将其变化反映在屏幕上的过程。这会影响所有动画的性能。
硬件加速是指在主 JavaScript 线程之外运行动画的能力。这会影响与其他 JS 进程(如 React 渲染或其他数据密集型运算)同时运行的动画的性能。
让我们深入了解一下每个因素。
渲染
当我们更新元素的样式时
element.style.height = "500px"
浏览器需要通过重新渲染页面来反映此更改,即将 HTML 和 CSS 转换为在屏幕上显示的图像。
一般来说,浏览器渲染器执行此操作的步骤是
布局(Layout):计算页面上元素的大小和位置
绘制(Paint):将页面绘制到图形图层(layers)中 - 本质上是构成页面的各个图像
合成(Composite):将这些图层绘制到视口。
根据经验,工作量越少速度越快。
缓慢渲染
例如,如果我们更新一个元素的 height
,这会改变元素的大小。更改元素的大小可能会影响兄弟元素或子元素的大小和/或位置,这本身可能会产生连锁反应,依此类推。因此,我们需要重新计算所有受影响元素的布局。
每当页面布局发生变化时,浏览器还需要重新绘制和重新合成受影响的图层。许多样式会影响布局,例如 height
、border-width
、padding
、position
等。
流畅的动画通常以每秒 60 帧 (fps) 的速度运行,与大多数屏幕刷新率相同。因此,如果我们想以 60fps 的速度更改元素的 height
,我们需要能够在 16.7 毫秒内重新渲染。屏幕和浏览器越来越能够以 120fps 的速度进行动画处理,因此此时间窗口可以进一步缩短至 8 毫秒。
重新渲染很容易花费超过 100 毫秒!这就是为什么通常不鼓励动画布局的原因。
但是,这不是一成不变的规则。如果元素的布局是 изолированный 的,例如它具有 position: absolute
并且子元素很少,那么您或许可以流畅地对其进行动画处理。只需确保在低功耗设备上测试这些动画。
快速渲染
性能最佳的样式是那些仅触发第三个渲染步骤(合成器)的样式。
在所有现代浏览器中,transform
和 opacity
将直接在图层上操作。因此,这些是在所有设备上进行动画处理的最安全的值。
浏览器也在此列表中添加更多值。例如,Chrome 和 Firefox 完全在合成器上处理 filter
和 SVG,而 Chrome 即将添加对 background-color
和 clip-path
的支持。
此外,由于 Motion 构建于 Web Animations API 之上,因此浏览器足够智能,可以自动将动画处理这些值的元素放置在新的图形图层上。
介于两者之间
还有许多值(如 box-shadow
和 border-radius
)可以在不触发渲染的情况下更新,但确实需要可能代价高昂的绘制(步骤 2)。
您应始终在低功耗设备上测试这些属性的动画效果。
但是,仍然有一些方法可以提高这些动画的性能。
减小图层大小
首先,浏览器绘制图层所需的时间与图层的大小成正比。因此,您可以通过制作更小的图层来提高这些动画的性能。
您可以通过设置 willChange
来提示浏览器应该创建新图层样式为 "transform"
element.style.willChange = "transform" animate(element, { borderRadius: "50%" })
创建新图层并非没有代价。每个图层都占用 GPU 上的空间。因此请谨慎使用。
使用替代样式
其次,您可以尝试用性能更好的替代样式替换样式。
我们已经看到浏览器正在改进对合成器上 filter
的支持。因此,与其动画处理 boxShadow
,不如动画处理 filter
使用drop-shadow
函数:
// ❌ animate(element, { boxShadow: "10px 10px black" }) // ✅ animate(element, { filter: "drop-shadow(10px 10px black)" })
同样,浏览器正在将 clipPath
添加到合成器。因此,与其动画处理 borderRadius
,不如动画处理 clipPath
使用inset
函数:
// ❌ animate(element, { borderRadius: "50px" }) // ✅ animate(element, { clipPath: "inset(0 round 50px)" })
哪个是哪个?
那么,您如何判断哪些样式会触发布局或绘制,哪些只会触发合成?
关于此主题的大多数教程都会推荐CSS Triggers。虽然这仍然是捕捉触发布局的样式(border
、width
、top
)的好指南,但它已经非常过时,缺少有关 clip-path
、filter
的数据以及关于其他信息的过时信息。
浏览器一直在变化,因此最好的方法是在冒险使用 transform
和 opacity
之外的样式时,进行跨浏览器和跨设备测试。每个浏览器都有性能分析工具,大多数浏览器都可以远程调试移动设备,因此请务必使用这些工具进行深入调查。
硬件加速
渲染性能应该是您首要关注的重点,因为这将是您制作的每个动画都需要考虑的因素。但还有动画过程本身的额外因素,以及它是否可以硬件加速。
动画最基本的形式是在一段时间内混合两个值。例如,如果我们想在一秒钟内从 "100px"
动画到 "200px"
,如果 0.5
秒已过,我们的动画将计算出 "150px"
。这是一个非常简单的计算,与渲染相比,不会产生明显的开销。
但是,在浏览器中,我们可以通过多种方式计算此值
JavaScript 代码始终在主 JS 线程上运行。这意味着如果您的应用程序同时运行其他 JS 代码,您的动画代码可能会被阻止运行。这将导致动画卡顿。
但是,WAAPI 和 CSS 可以在主 JS 线程之外,直接在合成器本身上运行某些动画。由于这通常是 GPU,因此通常称为硬件加速。
硬件加速的动画将保持流畅,无论您的主 JS 线程变得多么繁忙。
由于 Motion 构建于 WAAPI 之上,因此它也可以运行硬件加速动画。
加速值
一般来说,如果样式仅调用合成渲染步骤,则理论上也可以对其进行动画处理。
transform
和 opacity
是广泛支持的合成器样式,并且这些样式通常在合成器上进行动画处理。
其他值(如 filter
、background-color
、clip-path
和 SVG)在大多数浏览器中要么已经支持,要么正在获得支持。
CSS 变量和 individual transforms
Motion 与 WAAPI 和 CSS 的独特之处在于,它支持 individual transforms 的动画处理
animate(".box", { x: 100, scale: 2 })
在底层,它正在动画处理 CSS 变量,但目前这些变量未加速,即使它们正在应用于 transform
。
因此,如果硬件加速在您的用例中至关重要,请坚持动画处理 transform
animate(".box", { transform: "translateX(100px) scale(2)" })
其他例外情况
即使在动画处理据称性能良好的样式时,每个浏览器对于动画是否获得加速都有不同的规则。
例如,直到最近,如果 Chrome 检测到像这样的基于 %
的 transform
animate(element, { transform: "translateX(100%)" })
它不会进行硬件加速。
渐进增强
所有这些都表明,确保您的动画进行硬件加速可能是一个雷区。
我们建议将加速视为渐进增强。当浏览器支持它时,它很棒,但通常不是必需的。
如果您确实遇到来自繁忙 JavaScript 线程的大量卡顿,请坚持使用 transform
、filter
、clipPath
和 opacity
。
结论
为了实现流畅的动画,您的首要任务应该是您动画处理哪些值。作为开发人员,这是您可以最大程度控制的事情。
transform
和 opacity
在所有浏览器中渲染成本最低。浏览器一直在改进其他样式(和 SVG)的渲染性能,filter
、background-color
和 clip-path
即将到来。
布局触发样式可以在不影响周围元素布局的元素上进行动画处理(例如,如果它们是 position: absolute
)。但请确保在许多浏览器中,尤其是在低功耗设备中测试这些动画。
最后,如果您的网站在动画期间执行繁重的处理,硬件加速是避免卡顿的绝佳工具。但这不是您可以完全控制的东西,在许多情况下它会被禁用。
影响 Web 动画性能的主要因素有两个:渲染 和 硬件加速。
渲染是指获取 DOM 的更新并将其变化反映在屏幕上的过程。这会影响所有动画的性能。
硬件加速是指在主 JavaScript 线程之外运行动画的能力。这会影响与其他 JS 进程(如 React 渲染或其他数据密集型运算)同时运行的动画的性能。
让我们深入了解一下每个因素。
渲染
当我们更新元素的样式时
element.style.height = "500px"
浏览器需要通过重新渲染页面来反映此更改,即将 HTML 和 CSS 转换为在屏幕上显示的图像。
一般来说,浏览器渲染器执行此操作的步骤是
布局(Layout):计算页面上元素的大小和位置
绘制(Paint):将页面绘制到图形图层(layers)中 - 本质上是构成页面的各个图像
合成(Composite):将这些图层绘制到视口。
根据经验,工作量越少速度越快。
缓慢渲染
例如,如果我们更新一个元素的 height
,这会改变元素的大小。更改元素的大小可能会影响兄弟元素或子元素的大小和/或位置,这本身可能会产生连锁反应,依此类推。因此,我们需要重新计算所有受影响元素的布局。
每当页面布局发生变化时,浏览器还需要重新绘制和重新合成受影响的图层。许多样式会影响布局,例如 height
、border-width
、padding
、position
等。
流畅的动画通常以每秒 60 帧 (fps) 的速度运行,与大多数屏幕刷新率相同。因此,如果我们想以 60fps 的速度更改元素的 height
,我们需要能够在 16.7 毫秒内重新渲染。屏幕和浏览器越来越能够以 120fps 的速度进行动画处理,因此此时间窗口可以进一步缩短至 8 毫秒。
重新渲染很容易花费超过 100 毫秒!这就是为什么通常不鼓励动画布局的原因。
但是,这不是一成不变的规则。如果元素的布局是 изолированный 的,例如它具有 position: absolute
并且子元素很少,那么您或许可以流畅地对其进行动画处理。只需确保在低功耗设备上测试这些动画。
快速渲染
性能最佳的样式是那些仅触发第三个渲染步骤(合成器)的样式。
在所有现代浏览器中,transform
和 opacity
将直接在图层上操作。因此,这些是在所有设备上进行动画处理的最安全的值。
浏览器也在此列表中添加更多值。例如,Chrome 和 Firefox 完全在合成器上处理 filter
和 SVG,而 Chrome 即将添加对 background-color
和 clip-path
的支持。
此外,由于 Motion 构建于 Web Animations API 之上,因此浏览器足够智能,可以自动将动画处理这些值的元素放置在新的图形图层上。
介于两者之间
还有许多值(如 box-shadow
和 border-radius
)可以在不触发渲染的情况下更新,但确实需要可能代价高昂的绘制(步骤 2)。
您应始终在低功耗设备上测试这些属性的动画效果。
但是,仍然有一些方法可以提高这些动画的性能。
减小图层大小
首先,浏览器绘制图层所需的时间与图层的大小成正比。因此,您可以通过制作更小的图层来提高这些动画的性能。
您可以通过设置 willChange
来提示浏览器应该创建新图层样式为 "transform"
element.style.willChange = "transform" animate(element, { borderRadius: "50%" })
创建新图层并非没有代价。每个图层都占用 GPU 上的空间。因此请谨慎使用。
使用替代样式
其次,您可以尝试用性能更好的替代样式替换样式。
我们已经看到浏览器正在改进对合成器上 filter
的支持。因此,与其动画处理 boxShadow
,不如动画处理 filter
使用drop-shadow
函数:
// ❌ animate(element, { boxShadow: "10px 10px black" }) // ✅ animate(element, { filter: "drop-shadow(10px 10px black)" })
同样,浏览器正在将 clipPath
添加到合成器。因此,与其动画处理 borderRadius
,不如动画处理 clipPath
使用inset
函数:
// ❌ animate(element, { borderRadius: "50px" }) // ✅ animate(element, { clipPath: "inset(0 round 50px)" })
哪个是哪个?
那么,您如何判断哪些样式会触发布局或绘制,哪些只会触发合成?
关于此主题的大多数教程都会推荐CSS Triggers。虽然这仍然是捕捉触发布局的样式(border
、width
、top
)的好指南,但它已经非常过时,缺少有关 clip-path
、filter
的数据以及关于其他信息的过时信息。
浏览器一直在变化,因此最好的方法是在冒险使用 transform
和 opacity
之外的样式时,进行跨浏览器和跨设备测试。每个浏览器都有性能分析工具,大多数浏览器都可以远程调试移动设备,因此请务必使用这些工具进行深入调查。
硬件加速
渲染性能应该是您首要关注的重点,因为这将是您制作的每个动画都需要考虑的因素。但还有动画过程本身的额外因素,以及它是否可以硬件加速。
动画最基本的形式是在一段时间内混合两个值。例如,如果我们想在一秒钟内从 "100px"
动画到 "200px"
,如果 0.5
秒已过,我们的动画将计算出 "150px"
。这是一个非常简单的计算,与渲染相比,不会产生明显的开销。
但是,在浏览器中,我们可以通过多种方式计算此值
JavaScript 代码始终在主 JS 线程上运行。这意味着如果您的应用程序同时运行其他 JS 代码,您的动画代码可能会被阻止运行。这将导致动画卡顿。
但是,WAAPI 和 CSS 可以在主 JS 线程之外,直接在合成器本身上运行某些动画。由于这通常是 GPU,因此通常称为硬件加速。
硬件加速的动画将保持流畅,无论您的主 JS 线程变得多么繁忙。
由于 Motion 构建于 WAAPI 之上,因此它也可以运行硬件加速动画。
加速值
一般来说,如果样式仅调用合成渲染步骤,则理论上也可以对其进行动画处理。
transform
和 opacity
是广泛支持的合成器样式,并且这些样式通常在合成器上进行动画处理。
其他值(如 filter
、background-color
、clip-path
和 SVG)在大多数浏览器中要么已经支持,要么正在获得支持。
CSS 变量和 individual transforms
Motion 与 WAAPI 和 CSS 的独特之处在于,它支持 individual transforms 的动画处理
animate(".box", { x: 100, scale: 2 })
在底层,它正在动画处理 CSS 变量,但目前这些变量未加速,即使它们正在应用于 transform
。
因此,如果硬件加速在您的用例中至关重要,请坚持动画处理 transform
animate(".box", { transform: "translateX(100px) scale(2)" })
其他例外情况
即使在动画处理据称性能良好的样式时,每个浏览器对于动画是否获得加速都有不同的规则。
例如,直到最近,如果 Chrome 检测到像这样的基于 %
的 transform
animate(element, { transform: "translateX(100%)" })
它不会进行硬件加速。
渐进增强
所有这些都表明,确保您的动画进行硬件加速可能是一个雷区。
我们建议将加速视为渐进增强。当浏览器支持它时,它很棒,但通常不是必需的。
如果您确实遇到来自繁忙 JavaScript 线程的大量卡顿,请坚持使用 transform
、filter
、clipPath
和 opacity
。
结论
为了实现流畅的动画,您的首要任务应该是您动画处理哪些值。作为开发人员,这是您可以最大程度控制的事情。
transform
和 opacity
在所有浏览器中渲染成本最低。浏览器一直在改进其他样式(和 SVG)的渲染性能,filter
、background-color
和 clip-path
即将到来。
布局触发样式可以在不影响周围元素布局的元素上进行动画处理(例如,如果它们是 position: absolute
)。但请确保在许多浏览器中,尤其是在低功耗设备中测试这些动画。
最后,如果您的网站在动画期间执行繁重的处理,硬件加速是避免卡顿的绝佳工具。但这不是您可以完全控制的东西,在许多情况下它会被禁用。
影响 Web 动画性能的主要因素有两个:渲染 和 硬件加速。
渲染是指获取 DOM 的更新并将其变化反映在屏幕上的过程。这会影响所有动画的性能。
硬件加速是指在主 JavaScript 线程之外运行动画的能力。这会影响与其他 JS 进程(如 React 渲染或其他数据密集型运算)同时运行的动画的性能。
让我们深入了解一下每个因素。
渲染
当我们更新元素的样式时
element.style.height = "500px"
浏览器需要通过重新渲染页面来反映此更改,即将 HTML 和 CSS 转换为在屏幕上显示的图像。
一般来说,浏览器渲染器执行此操作的步骤是
布局(Layout):计算页面上元素的大小和位置
绘制(Paint):将页面绘制到图形图层(layers)中 - 本质上是构成页面的各个图像
合成(Composite):将这些图层绘制到视口。
根据经验,工作量越少速度越快。
缓慢渲染
例如,如果我们更新一个元素的 height
,这会改变元素的大小。更改元素的大小可能会影响兄弟元素或子元素的大小和/或位置,这本身可能会产生连锁反应,依此类推。因此,我们需要重新计算所有受影响元素的布局。
每当页面布局发生变化时,浏览器还需要重新绘制和重新合成受影响的图层。许多样式会影响布局,例如 height
、border-width
、padding
、position
等。
流畅的动画通常以每秒 60 帧 (fps) 的速度运行,与大多数屏幕刷新率相同。因此,如果我们想以 60fps 的速度更改元素的 height
,我们需要能够在 16.7 毫秒内重新渲染。屏幕和浏览器越来越能够以 120fps 的速度进行动画处理,因此此时间窗口可以进一步缩短至 8 毫秒。
重新渲染很容易花费超过 100 毫秒!这就是为什么通常不鼓励动画布局的原因。
但是,这不是一成不变的规则。如果元素的布局是 изолированный 的,例如它具有 position: absolute
并且子元素很少,那么您或许可以流畅地对其进行动画处理。只需确保在低功耗设备上测试这些动画。
快速渲染
性能最佳的样式是那些仅触发第三个渲染步骤(合成器)的样式。
在所有现代浏览器中,transform
和 opacity
将直接在图层上操作。因此,这些是在所有设备上进行动画处理的最安全的值。
浏览器也在此列表中添加更多值。例如,Chrome 和 Firefox 完全在合成器上处理 filter
和 SVG,而 Chrome 即将添加对 background-color
和 clip-path
的支持。
此外,由于 Motion 构建于 Web Animations API 之上,因此浏览器足够智能,可以自动将动画处理这些值的元素放置在新的图形图层上。
介于两者之间
还有许多值(如 box-shadow
和 border-radius
)可以在不触发渲染的情况下更新,但确实需要可能代价高昂的绘制(步骤 2)。
您应始终在低功耗设备上测试这些属性的动画效果。
但是,仍然有一些方法可以提高这些动画的性能。
减小图层大小
首先,浏览器绘制图层所需的时间与图层的大小成正比。因此,您可以通过制作更小的图层来提高这些动画的性能。
您可以通过设置 willChange
来提示浏览器应该创建新图层样式为 "transform"
element.style.willChange = "transform" animate(element, { borderRadius: "50%" })
创建新图层并非没有代价。每个图层都占用 GPU 上的空间。因此请谨慎使用。
使用替代样式
其次,您可以尝试用性能更好的替代样式替换样式。
我们已经看到浏览器正在改进对合成器上 filter
的支持。因此,与其动画处理 boxShadow
,不如动画处理 filter
使用drop-shadow
函数:
// ❌ animate(element, { boxShadow: "10px 10px black" }) // ✅ animate(element, { filter: "drop-shadow(10px 10px black)" })
同样,浏览器正在将 clipPath
添加到合成器。因此,与其动画处理 borderRadius
,不如动画处理 clipPath
使用inset
函数:
// ❌ animate(element, { borderRadius: "50px" }) // ✅ animate(element, { clipPath: "inset(0 round 50px)" })
哪个是哪个?
那么,您如何判断哪些样式会触发布局或绘制,哪些只会触发合成?
关于此主题的大多数教程都会推荐CSS Triggers。虽然这仍然是捕捉触发布局的样式(border
、width
、top
)的好指南,但它已经非常过时,缺少有关 clip-path
、filter
的数据以及关于其他信息的过时信息。
浏览器一直在变化,因此最好的方法是在冒险使用 transform
和 opacity
之外的样式时,进行跨浏览器和跨设备测试。每个浏览器都有性能分析工具,大多数浏览器都可以远程调试移动设备,因此请务必使用这些工具进行深入调查。
硬件加速
渲染性能应该是您首要关注的重点,因为这将是您制作的每个动画都需要考虑的因素。但还有动画过程本身的额外因素,以及它是否可以硬件加速。
动画最基本的形式是在一段时间内混合两个值。例如,如果我们想在一秒钟内从 "100px"
动画到 "200px"
,如果 0.5
秒已过,我们的动画将计算出 "150px"
。这是一个非常简单的计算,与渲染相比,不会产生明显的开销。
但是,在浏览器中,我们可以通过多种方式计算此值
JavaScript 代码始终在主 JS 线程上运行。这意味着如果您的应用程序同时运行其他 JS 代码,您的动画代码可能会被阻止运行。这将导致动画卡顿。
但是,WAAPI 和 CSS 可以在主 JS 线程之外,直接在合成器本身上运行某些动画。由于这通常是 GPU,因此通常称为硬件加速。
硬件加速的动画将保持流畅,无论您的主 JS 线程变得多么繁忙。
由于 Motion 构建于 WAAPI 之上,因此它也可以运行硬件加速动画。
加速值
一般来说,如果样式仅调用合成渲染步骤,则理论上也可以对其进行动画处理。
transform
和 opacity
是广泛支持的合成器样式,并且这些样式通常在合成器上进行动画处理。
其他值(如 filter
、background-color
、clip-path
和 SVG)在大多数浏览器中要么已经支持,要么正在获得支持。
CSS 变量和 individual transforms
Motion 与 WAAPI 和 CSS 的独特之处在于,它支持 individual transforms 的动画处理
animate(".box", { x: 100, scale: 2 })
在底层,它正在动画处理 CSS 变量,但目前这些变量未加速,即使它们正在应用于 transform
。
因此,如果硬件加速在您的用例中至关重要,请坚持动画处理 transform
animate(".box", { transform: "translateX(100px) scale(2)" })
其他例外情况
即使在动画处理据称性能良好的样式时,每个浏览器对于动画是否获得加速都有不同的规则。
例如,直到最近,如果 Chrome 检测到像这样的基于 %
的 transform
animate(element, { transform: "translateX(100%)" })
它不会进行硬件加速。
渐进增强
所有这些都表明,确保您的动画进行硬件加速可能是一个雷区。
我们建议将加速视为渐进增强。当浏览器支持它时,它很棒,但通常不是必需的。
如果您确实遇到来自繁忙 JavaScript 线程的大量卡顿,请坚持使用 transform
、filter
、clipPath
和 opacity
。
结论
为了实现流畅的动画,您的首要任务应该是您动画处理哪些值。作为开发人员,这是您可以最大程度控制的事情。
transform
和 opacity
在所有浏览器中渲染成本最低。浏览器一直在改进其他样式(和 SVG)的渲染性能,filter
、background-color
和 clip-path
即将到来。
布局触发样式可以在不影响周围元素布局的元素上进行动画处理(例如,如果它们是 position: absolute
)。但请确保在许多浏览器中,尤其是在低功耗设备中测试这些动画。
最后,如果您的网站在动画期间执行繁重的处理,硬件加速是避免卡顿的绝佳工具。但这不是您可以完全控制的东西,在许多情况下它会被禁用。