你有可能从来没有认识过不具备转换属性能力的CSS。这一功能是CSS的核心,也是我们今天所知的用户界面(UI)的基石。然而,近年来,CSS并没有回避对其最基础的一些部分进行大修。在用flexbox和grid革新了布局,并用逻辑属性重组了它的盒子模型之后,是时候引入它的下一个进化了......。
尽管许多微妙的UI交互看起来很柔和、很受欢迎,但创建和编辑它们却不是那么容易的。这是因为CSS有一个单一的transform 属性来管理其所有不同的值,如rotate 、scale 和translate 。
当转换一个单一的值时,单一的属性工作得很好。然而,当处理多个值时,它就变成了一个繁重的认知负担--CSS希望通过引入单独的变换属性来解决这一认知负担。
首先,让我们回顾一下当前的transform 属性,然后发现它的功能是如何通过使用新的单个变换属性而得到改善的。让我们开始吧。
transform 属性的挑战
为了理解单个转换属性的好处,让我们首先看看它们试图解决的两个关键挑战。这两个挑战在开始的时候都不是很明显。
使用它或失去它
下面这个transform 属性并不太复杂。它将缩放、平移,然后旋转该元素:
.item {
transform: scale(1.5) translate(0, 50%) rotate(90deg);
}
但如果我们想改变hover 上的缩放量,transform 会发生什么?
每个transform 函数都必须在每个状态下定义,否则它的值就会丢失。为了缩小hover 上的项目而不丢失其translate 和rotate 的值,它们都必须与更新的缩放值一起被复制:
.item:hover {
transform: scale(0.5) translate(0, 50%) rotate(90deg);
}
对于一个单一的hover 状态,这可能不是一个太大的负担。但是,随着变换的增长或者创建多帧动画时,这就变得更加复杂了。
然而,需要重复每一个transform 的函数则是另一个挑战。
操作的顺序
当创建具有多个函数的变换时,重要的是要注意,浏览器将按照从左到右的顺序应用这些值。这意味着下面的变换在视觉上会有不同的结果,尽管有相同的值:
.item:first-child {
transform: scale(1.75) translate(0, 50%);
}
.item:last-child {
transform: translate(0, 50%) scale(1.75);
}
在第一个项目缩放后,它将被翻译成相对于它的新尺寸。同时,第二个项目将在翻译后进行缩放,导致一个元素的位置与第一个元素不完全一样。
随着变换越来越复杂,使用的transform 函数也越来越多,管理整个属性就变得越来越困难。以一个有多帧的动画为例。
当创建一个有多个transform 值的动画时,在每一帧中以正确的顺序管理每个属性的认知负担会变得相当沉重:
@keyframes animate {
10%, 15% {
transform: translateX(0);
}
16% {
transform: translateX(0) scale(0.5);
}
18% {
transform: translateX(0) scale(1.5);
}
20% {
transform: translateX(0) scale(1);
}
50% {
transform: translateX(50%) scale(1) rotate(180deg);
}
65% {
transform: translateX(-50%) scale(1) rotate(180deg);
}
}
正是这些挑战和认知负担,看起来可以通过引入CSS单独的变换属性来消除。
什么是CSS单个转换属性?
CSS引入了三个新的单独转换属性。rotate,scale, 和translate:
.item {
rotate: 180deg;
scale: 1.5;
translate: 50% 0;
}
这些新属性的工作方式与传统的transform 功能类似,但没有传统的挑战。
因为这些新的单独属性是相互独立的,所以不需要在不同的状态下重复取值。而且,如果不需要在不同的状态下重复数值,那么顺序就变得更容易管理了,除了个别转换属性也不依赖于它们的顺序。
在传统的transform 函数按从左到右的顺序应用的情况下,单个变换属性是按更有利的顺序应用的。1、平移 2、旋转 3、缩放。
在解决了使用transform 属性的关键挑战后,先前的动画可以被重构为一个更容易管理和可读的@keyframes 块,如下所示:
@keyframes animate {
10%, 15% {
scale: 1;
translate: 0;
}
16% {
scale: 0.5;
}
18% {
scale: 1.5;
}
20% {
rotate: 0deg;
scale: 1;
}
50% {
rotate: 180deg;
translate: 50% 0;
}
65% {
rotate: 180deg;
translate: -50% 0;
}
}
单个变换属性的注意事项
在使用单个变换属性时,需要考虑一些其他的问题,这些问题可能与传统的属性不同。我们将在下面更深入地讨论它们。
留下的一些属性
虽然CSS引入了三个单独的属性rotate 、scale 、和translate ,但其余的transform 功能并没有得到同样的重视。正因为如此,个人属性和transform 属性可以一起工作。
当转换一个元素时,通常也会使用transform-origin 属性。虽然大多数浏览器默认对一个元素进行变换,例如从中心点旋转图像,但transform-origin 属性允许明确控制元素被变换的点。
因为这两个属性有相似的名字,transform 和transform-origin ,所以心理模型很清楚这两个属性是相关的--这个心理模型已经与单个的变换属性断开了联系。
然而,尽管属性名称不再一致,rotate 、scale 和translate 属性都作为变换的功能,仍然像预期的那样坚持任何transform-origin 值。这意味着现有的利用显式transform-origin 点的变换可以被重构为使用单独的变换属性而没有任何冲突。
.item {
scale: 1.5;
transform-origin: top right;
}
将值设置为0
当在CSS中设置几乎所有的值为0 ,通常可以接受不提供任何单位给该值。当值为0 时,它是0px 还是0rem 其实并不重要。这同样适用于transform 属性和旋转函数:
.item {
transform: rotate(90deg);
}
.item:hover {
transform: rotate(0);
}
然而,当使用单独的rotate 属性时,必须定义一个单位或CSS关键字:
.item {
rotate: 90deg;
}
.item:hover {
//
will-change 属性
与transform-origin 很相似,单个的变换属性也与will-change 属性一起工作。虽然,使用 will-change 时仍应遵循同样的考虑 ,例如,只有在动画或变换已经出现性能问题时才应用该属性。
如果transform 属性没有造成任何性能问题,切换到单个变换属性不会改变这一点。
整体性能
使用单独的变换属性与原来的transform 属性一样有效。
支持和回退
如果不能使用CSS单独的变换属性,那么它的好处就毫无价值。幸运的是,现代对这些属性的支持已经相当不错了,至少在所有主要浏览器的最新版本中都有支持,在v104中被引入Chrome和Edge,Safari 14.1,以及Firefox 103。

不过,只为主要浏览器的最新版本构建产品,往往是一种幻想,而不是网络开发的现实。但是,由于各个转换属性可以直接映射到传统的transform 值,因此可以使用可靠的回退方法来进行渐进式增强:
.container {
rotate: 80deg;
scale: 1.5;
translate: 50% 10%;
@supports not (scale: 1) {
// Use transform fallback if CSS individual transform properties are not supported
transform: translate(50%, 10%) rotate(80deg) scale(1.5);
}
}
通过使用带有not 关键字的@supports 查询,我们能够优先考虑较新的属性,只在需要的环境中呈现回退。但是要警惕,因为transform 属性依赖于它的值的顺序,回退的编写必须考虑到这一点。
为了使编写回退的过程更容易,可以使用一个用于单个变换属性的SCSS混合器来自动处理回退的transform 属性及其值的顺序。
总结
长期以来,变换一直是CSS的一个基本特征。它们的相互作用定义了我们今天所知的网络。随着单个变换属性(rotate 、scale 、translate )的引入,动画和变换的边界可能被进一步推开。
这些属性还可以在哪些方面发挥作用?你是否也希望看到其他的transform 功能,如skew 和特定轴的功能,被移到它们自己的属性中?
如果不出意外的话,CSS的单个变换属性有两个关键的好处:
- 现在对初学者来说,对变换和动画的介绍可能会更好一些
- 能够清理现有的变换和动画
仅仅因为这两个原因,单独的变换属性是对CSS基础的一个值得欢迎的转变。