1. 定义:will-change 是什么?
will-change 是一个 CSS 属性,它允许你提前“告知”浏览器,某个元素上的特定属性即将发生变化。
可以把它想象成一个给浏览器的“性能提示”。当你使用这个属性时,浏览器就可以提前为这个元素的动画或变换做好优化准备,例如为其创建一个独立的合成层(Compositing Layer),从而在动画实际发生时,能够更流畅、更高效地进行渲染。
2. 技术原理:它如何提升性能?
为了理解 will-change 的作用,我们需要先了解浏览器的基本渲染流程:
- 布局 (Layout/Reflow) : 计算元素在屏幕上的位置和大小。
- 绘制 (Paint) : 填充像素,绘制元素的颜色、边框等视觉表现。
- 合成 (Composite) : 将所有绘制好的“图层”(Layers)按正确的顺序合并,最终显示在屏幕上。
大多数 CSS 属性(如 width, height, margin)的改变都会触发耗费性能的“布局”和“绘制”过程。然而,transform 和 opacity 这两个属性非常特殊,改变它们通常只需要执行“合成”这一步,这个过程可以被 GPU (显卡) 加速,因此性能极高。
will-change 的核心作用就是,当你提示浏览器某个元素的 transform 或 opacity 即将改变时,浏览器会倾向于提前将该元素提升到一个独立的合成层。这样,当动画发生时,浏览器只需让 GPU 去移动或改变这个独立图层的透明度,而无需重新布局和绘制,从而避免了主线程的阻塞,实现了丝滑的动画效果。
3. 语法与参数详解
will-change 接受以下几种类型的参数值:
| 参数值 | 作用 |
|---|---|
auto | 默认值。不进行任何特别的优化。 |
<custom-ident> | 最常用的值。直接填入一个或多个你计划用动画改变的 CSS 属性名,用逗号隔开。 |
transform: 用于 2D/3D 变换(移动、缩放、旋转)。will-change 的最佳应用场景。 | |
opacity: 用于改变透明度。同样是 will-change 的理想应用场景。 | |
left, top 等:也可以使用,但这些属性本身会触发重排,优化效果不如 transform。 | |
scroll-position | 专门用于优化滚动。提示浏览器该元素会因为滚动而频繁改变位置。 |
contents | 提示浏览器该元素的内部内容即将发生变化。 |
4. 如何与动画“绑定”?
一个常见的误解是 will-change 会绑定到某个动画名称。实际上,它不绑定动画本身,而是绑定到动画所操作的 CSS 属性上。
它们的关系如下:
- 你决定要对一个元素的
transform属性做动画。 - 你使用
will-change: transform;提前准备。 - 浏览器收到提示,进行优化(如创建新图层)。
- 你使用
transition或animation定义动画。 - 当
transform的值被某个事件(如:hover)触发改变时,流畅的动画便会执行。
5. 代码示例
示例 1:与 transition 配合的悬停效果
/* 基础样式和过渡效果定义 */
.card {
transition: transform 0.3s ease-out;
}
/* 最佳实践:只在即将动画时添加 will-change */
.card:hover {
will-change: transform;
transform: translateY(-10px);
}
示例 2:与 @keyframes 动画配合
/* 定义动画 */
@keyframes slide-in {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* 对即将应用动画的元素给出提示 */
.panel.is-entering {
/* 提示浏览器,transform 和 opacity 两个属性都将发生变化 */
will-change: transform, opacity;
animation: slide-in 0.5s forwards;
}
示例 3:使用 JavaScript 精确控制 (最推荐)
在实际项目中,通过 JavaScript 在动画开始前添加、结束后移除 will-change 是最理想的方式。
const element = document.querySelector('.my-element');
element.addEventListener('mouseenter', () => {
// 动画开始前,添加 will-change
element.style.willChange = 'transform';
});
element.addEventListener('animationend', () => {
// 动画结束后,移除 will-change 以释放资源
element.style.willChange = 'auto';
});
6. 使用要点与“陷阱”(重要!)
will-change 是一把双刃剑,滥用它比不用它更糟糕。请务必遵守以下原则:
-
不要滥用
绝对不要把它应用到大量元素上。每创建一个合成层都会消耗额外的内存和GPU资源。如果你让所有元素都“准备好”,不仅会浪费大量资源,也等于没有给出任何有效提示。
绝对禁止的做法:
* { will-change: transform; /* 灾难性的性能杀手!*/ } -
在恰当的时机使用
will-change 的设计初衷是作为一个临时性的优化。最佳实践是在动画或交互即将开始时添加它,在结束后立即移除它。让它永久挂在一个元素上,意味着让这个元素永久地占用额外的内存资源。
-
给浏览器一点时间
will-change 是一个“提示”,浏览器收到后需要一点时间来完成优化设置。如果你在改变属性的同一帧才添加 will-change,可能就太晚了,起不到任何优化效果。这就是为什么最好在交互发生前(如 mouseenter 时,而不是 :active 时)就添加它。
7. 官方文档
- MDN Web Docs:developer.mozilla.org/zh-CN/docs/…