通过单独的转换属性对 CSS 转换进行更精细的控制

使用 translaterotatescale 属性转换元素

CSS transform 属性

如需对元素应用转换,请使用 CSS transform 属性。该属性接受一个或多个 <transform-function>,这些 <transform-function> 会依次应用。

.target {
  transform: translateX(50%) rotate(30deg) scale(1.2);
}

目标元素在 X 轴上平移了 50%,旋转了 30 度,最后放大到 120%。

虽然 transform 属性可以正常运行,但如果您想单独更改其中的任何值,就会变得有些繁琐。

如需更改悬停时的缩放比例,您必须复制 transform 属性中的所有函数,即使其值保持不变也是如此。

.target:hover {
  transform: translateX(50%) rotate(30deg) scale(2); /* Only the value of scale() changed */
}

各个转换属性

随 Chrome 104 一起发布的是 CSS 转换的各个属性。这些属性分别是 scalerotatetranslate,您可以使用它们分别定义转换的各个部分。

这样一来,Chrome 就加入了 Firefox 和 Safari 的行列,这些浏览器已经支持这些属性。

浏览器支持

  • Chrome:104。
  • Edge:104。
  • Firefox:72.
  • Safari:14.1。

来源

使用各个属性重写前面的 transform 示例后,您的代码段将如下所示:

.target {
  translate: 50% 0;
  rotate: 30deg;
  scale: 1.2;
}

顺序很重要

原始 CSS transform 属性与新属性之间的一个关键区别是声明的转换的应用顺序。

使用 transform 时,转换函数会按编写的顺序应用,即从左(外部)到右(内部)。

对于各个转换属性,顺序不是它们的声明顺序。顺序始终相同:先是 translate(外部),然后是 rotate,最后是 scale(内部)。

这意味着以下两个代码段都会产生相同的结果:

.transform--individual {
  translate: 50% 0;
  rotate: 30deg;
  scale: 1.2;
}

.transform--individual-alt {
  rotate: 30deg;
  translate: 50% 0;
  scale: 1.2;
}

在这两种情况下,目标元素将首先沿 X 轴平移 50%,然后旋转 30deg,最后按 1.2 缩放。

如果将某个单独的转换属性与 transform 属性一起声明,则系统会先应用各个转换(translaterotatescale),最后应用 transform(内部)。如需了解详情,请参阅定义应如何计算转换矩阵的规范。

动画

添加这些属性的主要原因是让动画更易于实现。假设您要为元素添加动画效果,如下所示:

关键帧图。

使用 transform

如需使用 transform 实现此动画,您必须计算所有已定义的转换的所有中间值,并将这些值包含在每个关键帧中。例如,如需在 10% 处执行旋转,则必须计算其他转换的值,因为 transform 属性需要所有这些值。

包含计算得出中间值的关键帧图表。

生成的 CSS 代码如下所示:

@keyframes anim {
  0% { transform: translateX(0%); }
  5% { transform: translateX(5%) rotate(90deg) scale(1.2); }
  10% { transform: translateX(10%) rotate(180deg) scale(1.2); }
  90% { transform: translateX(90%) rotate(180deg) scale(1.2); }
  95% { transform: translateX(95%) rotate(270deg) scale(1.2); }
  100% { transform: translateX(100%) rotate(360deg); }
}

.target {
  animation: anim 2s;
  animation-fill-mode: forwards;
}

使用单独的转换属性

使用各个转换属性,编写起来会更容易。您可以单独定位到每个转换,而不是将所有转换从一个关键帧拖动到另一个关键帧。您也不再需要计算所有中间值。

@keyframes anim {
  0% { translate: 0% 0; }
  100% { translate: 100% 0; }

  0%, 100% { scale: 1; }
  5%, 95% { scale: 1.2; }

  0% { rotate: 0deg; }
  10%, 90% { rotate: 180deg; }
  100% { rotate: 360deg; }
}

.target {
  animation: anim 2s;
  animation-fill-mode: forwards;
}

使用各个转换属性和多个关键帧

为了使代码模块化,您可以将每个子动画拆分为各自的一组关键帧。

@keyframes move {
  0% { translate: 0% 0; }
  100% { translate: 100% 0; }
}

@keyframes scale {
  0%, 100% { scale: 1; }
  5%, 95% { scale: 1.2; }
}

@keyframes rotate {
  0% { rotate: 0deg; }
  10%, 90% { rotate: 180deg; }
  100% { rotate: 360deg; }
}

.target {
  animation: move 2s, scale 2s, rotate 2s;
  animation-fill-mode: forwards;
}

得益于这种拆分,您可以根据需要应用每组单独的关键帧,因为 transform 属性(现在已成为单独的属性)不再相互覆盖。除此之外,您还可以为每个转换设置不同的时间,而无需重写所有转换。

性能

使用这些新属性的动画的效率与现有 transform 属性的动画一样高。

translaterotatescale 的动画在合成器上运行的方式与 transform 的动画相同,因此它们对动画性能的提升效果与 transform 一样

这些新属性还可与 will-change 属性搭配使用。一般来说,最好避免过度使用 will-change,只对所需元素的最低数量使用 will-change,并且尽可能缩短使用时间。不过,尽可能具体说明问题也是有益的。例如,如果您使用 will-change 通过 rotatefilter 属性优化动画,则应使用 will-change: rotate, filter 进行声明。在为 rotatefilter 添加动画时,这种方法比使用 will-change: transform, filter 略胜一筹,因为 Chrome 在您使用 will-change 时预先创建的一些数据结构在 transformrotate 之间有所不同。

“新近可互操作”系列的一部分