下一代CSS的Transform
最新版本的Firefox(v72)浏览器的发布,在CSS方面带来了一些变化,比如对CSS路径动画(CSS Motion Path)的支持和CSS Transforms Module Level 2部分功能的支持。在这篇文章,我们就来一探CSS Transforms Module Level 2给CSS Transform带来的变化以及和CSS Transforms Module Level 1的差异。但在这篇文章中我们不会和大家聊所有Level 2中的内容,只是聊聊和level 1的变化,如果你感兴趣的话,请继续往下阅读。
未来的CSS Transform
首先给大家上一张图:
从上图中我们可获知,在未来使用CSS的transform
时,我们可以独立的使用相关的函数。目前为止,Firefox 72+中,我们要是使用CSS的transform
的话,对于translate
、scale
和rotate
三个变换函数可以独立使用,不再需要放到transform
属性中。
独立使用translate
、rotate
和scale
属性允许使用者(CSSer)独立的指定简单的transform
,而不必记住transform
中的顺序,这些顺序使translate()
、rotate()
和scale()
的操作独立于屏幕坐标。
那么这三个属性独立使用和放置到transform
中的差异是什么呢?如果感兴趣,请继续往下阅读。
如果希望能看到
translate()
、rotate()
和scale()
独立使用渲染出来的效果,首先请使用Firefox 72+版本。在接下来的示例中,如无特别声明,请使用该版本的Firefox浏览器查阅。
Transform Level 1 和 Transform Level 2的异和同
接下来的内容只会围绕着
translate
、rotate
和scale
三者展开,有关于CSS Transform内容暂不在该文讨论。
在CSS Transforms Module Level 1中,translate()
、rotate()
和scale()
三者是transform
的函数(<transform-function>
),他们都是transform
属性的值。在transform
中可以独立使用,也可以组合使用。而且独立使用和组合使用得到的效果都将不同,在组合状态和书写顺序也有着紧密的联系。
在CSS Transforms Module Level 2中,translate
、rotate
和scale
三者不再是transform
的函数,而是独立的CSS属性。
虽然在Level 1和Level 2中,他们的性质不同,但效果是不是相同呢?这个就需要我们一探究竟。
translate
先来看他们在语法上的差异:
// Level 1
transform: translate( <length-percentage> [, <length-percentage> ]?)
translate()
可以拆分为三个独立的函数:translateX()
、translateY()
和translateZ()
。而Level 2的语法则是:
// Level 2
tanslate: none | <length-percentage> [ <length-percentage> <length>? ]?
translate
属性接受1~3
个值,每个值指定一个轴上的平移,这三个值分别代表的是X
、Y
和Z
轴的值:
- 如果只给
translate
属性指定一个或两个属性值,那么该属性就指定了一个2D
空间的平移,相当于transform
的translate()
函数。如果第二个值缺失,则默认为0px
- 如果给
translate
属性指定三个属性值,那么该属性氷指定了一个3D
空间的平移,相当于transform
的translate3d()
函数
来看一个示例:
.old {
transform: translate(20vh, 30vh);
}
.new {
translate: 20vh 30vh;
}
效果如下:
在Chrome浏览器中开启“Web实验性属性”(
chrome://flags/#enable-experimental-web-platform-features
),也可以看到正常的效果。
注意,为了便于向大家展示3D
空间的变换效果,接下来的示例将以下面的HTML结构来构建:
<div class="camera">
<div class="space">
<div class="box"></div>
</div>
</div>
这个结构在transform
的3D
空间是很有用的。最外层的div.camera
相当于设定了摄影镜头,在该元素上设置perspective-origin
和perspective
两个属性的值:
.camera {
perspective-origin: center center;
perspective: 500px;
}
简单来说就是透视点以及镜头到透视点的距离。
第二层的div.space
用来设定一个立体空间,这个空间的设定方式很简单,只需要将transform-style
属性的值设置为3d
就能构建一个3D
立体空间:
.space{
transform-style:3d;
}
第三层的div.box
大家应该清楚了,就是我们要变换的元素盒子。我们接下来所做的位移、旋转和缩放都是围绕着.box
来做的:
为了大家更好的看到相应的效果,在上面的示例基础上做一些调整,你可以在示例中尝试着调整进度条来修改不同轴的值:
:root {
--x: 50%;
--y: 50%;
--z: 0px;
}
.old {
transform: translate3d(var(--x), var(--y), var(--z));
}
.new {
translate: var(--x) var(--y) var(--z);
}
效果如下:
从效果上来看,transform: translate(x,y)
和translate: x y
效果等同;transform: translate3d(x, y x)
和translate: x y z
的效果等同。
在CSS Transforms Module Level 1中的translate()
可以拆分为translateX()
和translateY()
,而translate3d()
可以拆分为translateX()
、translateY()
和translateZ()
。即:
transform: translate() = transform: translateX() translateY()
transform: translate3d() = transform: translateX() translateY() translateZ()
但是在CSS Transforms Module Level 2中的translate
属性并没有这样的语法规则。不过我们可以尝试让其中一个值为0
是否具备相应的效果。我们来验证一下这方面的想法:
.old {
transform: translateX(var(--x));
}
.new {
translate: var(--x) 0; // 或者:translate: var(--x)
}
两者的效果是等同的:
当
translate
取值为一个值时,那么y
轴和z
轴相当于设置了值为0
。
按同样的方式,实现translateY()
的效果:
.old {
transform: translateY(var(--y));
}
.new {
translate: 0 var(--y);
}
效果是等同的:
但要注意,当translate
属性取一个值,比如translate: var(--y)
时和translateY()
效果并不等同,而会和translateX()
等同,那是因为在translate
只显式的设置一个值的话,则会默认为x
轴的值,此时y
轴和z
轴相当于等于0
,不会做任何的位移。这一点切记。
最后来验证translateZ()
:
.old {
transform: translateZ(var(--z));
}
.new {
translate: 0 0 var(--z);
}
效果如下:
rotate
CSS中的旋转rotate()
相对于translate()
要复杂的多。就CSS Transforms Module Level1中的旋转,他的语法规则也较为复杂:
// Level 1
transform: rotate(<angle>) // 2d
transform: rotate3d(<number>, <number>, <number>, <angle>)
其中rotate3d()
也可以分拆为三个独立的函数,即rotateX(<angle>)
、rotateY(<angle>)
和rotateZ(<angle>)
。
在Level 2中的rotate
属性,他的语法规则较为清晰:
// Level 2
rotate: none | <angle> | [ x | y | z | <number>{3} ] && <angle>
rotate
属性接受一个角度(<angle>
)来旋转一个元素,也可以接受一个轴(x | y | z
)来旋转元素,如果使用轴来旋转元素需要带上一定的角度(<angle>
),这样表示元素绕着指定的轴旋转指定的角度。如果轴被省略,rotate
属性相当于一个2D
空间的旋转,和transform
中的rotate()
函数等同。否则就是一个3D
空间的旋转,相当于transform
中的rotate3d()
函数。在3D
空间中,可以分为:
- 如果在
rotate
属性中显式指定了x
、y
或z
中的任何一个轴,表示元素会绕着指定的轴旋转,相当于transform
中的rotateX()
、rotateY()
和rotateZ()
三个函数 - 另外一种方法是,可以通过给出三个轴表示的原点为中心的向量的
x
、y
和z
分量的数字,这个时候相当于transform
中的rotate3d()
函数
比如,rotate: 30deg
和rotate: z 30deg
都可以让元素旋转,第一个声明的是一个2D
空间的旋转,等价于transform: rotate(30deg)
;第二个声明的是一个3D
空间的旋转,等价于transform: rotateZ(30deg)
。
注意,CSS变换中的旋转,除了可以使用
deg
角度作为单位之外,还可以使用rad
、grad
和turn
等。有关于这方面更详细的介绍可以阅读《CSS 的值和单位》和《聊聊Web中的度数单位》。
接下来,我们分别来看看Level 1和Level 2具体的使用,以及他们之间的异同。先来看roate()
和roate
:
:root {
--angle: 45deg;
}
.box {
transform-origin: left bottom; //将变换原点设置为盒子的左下角顶点处
}
.old {
transform: rotate(var(--angle));
}
.new {
rotate: var(--angle);
}
效果和大家预期的一样,两者的效果是等同的:
上面我们看到的是一个2D
空间的旋转效果的对比,接下来看看3D
空间的旋转。我们都知道transform
属性取值为rotate3d()
、rotateX()
、rotateY()
和rotateZ()
函数时,表示的都是元素在3D
空间旋转。在新的rotate
也有类似的:
transform: rotateX(<angle>)
相当于rotate: x <angle>
transform: rotateY(<angle>)
相当于rotate: y <angle>
transform: rotateZ(<angle>)
相当于rotate: z <angle>
transform: rotate3d(x, y, z, <angle>)
相当于rotate: x y z <angle>
先来看rotateX(<angle>)
和rotate: x <angle>
的效果。在上面的示例上稍作调整:
:root {
--angle: 45deg;
}
.old {
transform: rotateX(var(--angle));
}
.new {
rotate: x var(--angle);
}
效果是等同的:
以同样的方式来看rotateY(<angle>)
和rotate: y <angle>
的效果:
:root {
--angle: 45deg;
}
.old {
transform: rotateY(var(--angle));
}
.new {
rotate: y var(--angle);
}
效果如下:
下面这个示例是让元素围绕着Z
轴旋转,即rotateZ(<angle>)
和rotate: z <angle>
的效果:
:root {
--angle: 45deg;
}
.old {
transform: rotateZ(var(--angle));
}
.new {
rotate: z var(--angle);
}
最后来看rotate3d()
,这个相对来说复杂一点,该函数接受四个参数,前三个接受的是<number>
值,一般是0~1
之间的值,分别对应的是x
、y
和z
轴的向量值,最后一个值是<angle>
,即旋转的角度值。比如下面这个效果:
:root {
--x: 0.5;
--y: 0.3;
--z: 0.2;
--angle: 45deg;
}
.box {
transform: rotate3d(var(--x), var(--y), var(--z), var(--angle));
}
效果如下:
我们在上面这个示例的基础来扩展,把rotate: x y z <angle>
的示例添加上去:
:root {
--x: 0.5;
--y: 0.3;
--z: 0.2;
--angle: 45deg;
}
.old {
transform: rotate3d(var(--x), var(--y), var(--z), var(--angle));
}
.new {
rotate: var(--x) var(--y) var(--z) var(--angle);
}
效果如下:
在rotate3d()
中有些场景和rotateX()
、rotateY()
和rotateZ()
是等同的:
rotateX(<angle>)
函数功能等同于rotate3d(1,0,0,<angle>)
rotateY(<angle>)
函数功能等同于rotate3d(0,1,0,<angle>)
rotateZ(<angle>)
函数功能等同于rotate3d(0,0,1,<angle>)
那么相应的延伸到rotate
属性中:
rotate: 1 0 0 <angle>
等同于rotate: x <angle>
rotate: 0 1 0 <angle>
等同于rotate: y <angle>
rotate: 0 0 1 <angle>
等同于rotate: z <angle>
在Level 1中,我们还可以将多个旋转函数运用于同一个transfrom
属性,这种现象常称为链式变换,比如下面这个示例:
:root {
--angleX: 45deg;
--angleY: 30deg;
--angleZ: 20deg;
}
.old {
transform: rotateX(var(--angleX)) rotateY(var(--angleY)) rotateZ(var(--angleZ));
}
效果如下:
CSS的链式transform
是个复杂的事情:
:root {
--angleX: 45deg;
--angleY: 30deg;
--angleZ: 20deg;
}
.old {
transform: rotateX(var(--angleX)) rotateY(var(--angleY)) rotateZ(var(--angleZ));
}
.new {
transform: rotateZ(var(--angleZ)) rotateY(var(--angleY)) rotateX(var(--angleX));
}
就上例来说,如果我们把书写顺序换一换,效果就会不同:
但是在Level 2中暂时是还不支持链式的写法,比如:
.new {
rotate: x var(--angleX) y var(--angleY) z var(--angleZ)
}
上面的这个是无效的语法值。如果换多行写:
.new {
rotate: x var(--angleX);
rotate: y var(--angleY);
rotate: z var(--angleZ);
}
根据CSS语法规则来判断,最终仅rotate: z var(--angleZ)
运用到元素.new
上,这是因为相同CSS属性运用在同一个元素之上的话,那么后面的相同的属性名会替换前面的属性。
在链式的变换中,还可吧不同的变换函数。这个我们放到后面独立来和大家探讨。从这里我们可以得知:
在Level 2中的
rotate
属性,无法获取链式的旋转效果。
scale
同样的,在CSS的transform
中有关于缩放的函数有scale()
和scale3d()
,前者是2D
空间的缩放,后者是3D
空间的缩放。其语法规则:
// Level 1
transform: scale( <number> [, <number> ]? )
transform: scale3d(<number>, <number>, <number>)
其中也可以拆分scaleX(<number>)
、scaleY(<number>)
和scaleZ(<number>)
三个函数。
在CSS Transforms Module Levle中scale
属性的语法规则是:
// Level 2
scale: none | <number>{1,3}
scale
属性可以接受1~3
个值,每个值按照x
、y
和z
的顺序来排列。如果只给出一个x
值,那么y
轴的值将默认为和x
轴的值相同。当给scale
属性值的个数不同,所代表的意思也不同:
- 当
scale
属性只有一个或两个值,表示的是2D
空间的缩放,相当于transform
的scale()
函数 - 当
scale
属性给出三个值,表示的是3D
空间的缩放,相当于transform
的scale3d()
函数
如果希望用scale
属性来表达scaleX()
、scaleY()
或scaleZ()
函数的话,则可以像下面这样来表述:
scale: <number> 1 1
则与scaleX(<number>)
等效scale: 1 <number> 1
则与scaleY(<number>)
等效scale: 1 1 <number>
则与scaleZ(<number>)
等效
因为在CSS变换中的缩放,当<number>
的值为1
表示既不放大,也不缩小。当<number>
的值小于1
表示在原来的基础上缩小,当<number>
大于1
时表示在原来的基础上放大。很多时候常把<number>
称为缩放因子。
和上面一样,我们通过示例来验证。先来看transform: scale()
和scale
如何实现2D
空间的缩放:
:root {
--scale: 1;
}
.old {
transform: scale(var(--scale));
}
.new {
scale: var(--scale);
}
前面也提到过了,不管是在scale()
函数中还是scale
属性中,只显式的设置了一个<number>
的值,则表示x
轴和y
轴的值是相同的。
从示例的结果中我们不难发现scale(<number>)
和scale: <number>
的效果是等同的:
在上面的示例基础上,我们稍做调整,意思就是 x
轴和y
轴的比例因子取不同的效果:
:root {
--scaleX: 1;
--scaleY: 1;
}
.old {
transform: scale(var(--scaleX), var(--scaleY));
}
.new {
scale: var(--scaleX) var(--scaleY);
}
效果如下所示:
继续在x
、y
轴的基础上加上z
的值。这样一来就创建了3D
空间的缩放,也就是scale3d()
:
:root {
--scaleX: 1;
--scaleY: 1;
--scaleZ: 1;
}
.old {
transform: scale3d(var(--scaleX), var(--scaleY), var(--scaleZ));
}
.new {
scale: var(--scaleX) var(--scaleY) var(--scaleZ);
}
效果如下:
在transform
中可以在每个轴都有独立的缩放函数,比如scaleX()
,但在scale
属性中需要变个方式来构建类似scaleX()
的功能,即将scale
属性中的y
轴和z
轴的值都显式的设置为1
:
:root {
--scale: 1;
}
.old {
transform: scaleX(var(--scale));
}
.new {
scale: var(--scale) 1 1;
}
效果是相同的:
对于scaleY()
函数,可以用scale: 1 number 1
来实现等同的效果:
同样的,scaleZ()
函数可以用scale: 1 1 number
来实现等同的效果:
skew()
和matrix()
以及其他
在CSS Transforms Module Level 2中,到目前为止仅translate
、rotate
和scale
提出来成为独立的CSS属性,但skew()
、matrix()
等还是需要和transform
属性配合使用。另外,在Level 2中也列出了transform-style
、peerspective
、perspective-origin
和backface-visibility
等属性的描述。不过这里不做过多的阐述,如果你对这方面的感兴趣的话,可以阅读:
- CSS Transforms Module Level 1
- CSS 3D Transforms Module Level 3
- transform-function
- CSS3 2D Transform
- CSS3 3D Transform
- 玩轉 CSS 3D: 原理篇
- 好吧,CSS3 3D transform变换,不过如此!
- 理解CSS3 transform中的Matrix(矩阵)
链式CSS Transforms
前面提到过链式的CSS Transforms,用的是roate()
为例子。接下来我们回到文章开头的示例:
element {
scale: 2;
rotate: 30deg;
translate: -50% -50%;
}
在同一个元素上使用了多个transform
,我们把这种行为称为 链式CSS Transform。在CSS Transforms Module level中,我们会这样使用:
element {
transform: scale(2) rotate(30deg) translate(-50%, -50%)
}
而且在transform
属性中链式使用多个变换函数时,跟书写顺序有着很紧密的关系,比如上面的代码,如果我们换成:
element {
transform: translate(-50%, -50%) rotate(30deg) scale(2)
}
效果会完全不同:
具体原因不在这里阐述,感兴趣的可以阅读:
这里我们更想验证的是:
element {
scale: 2;
rotate: 30deg;
translate: -50% -50%;
}
和transform
的:
element {
transform: scale(2) rotate(30deg) translate(-50%, -50%)
}
是不是具有相同的效果。因为这种链式的变换用法还是很常见的。我们在上面的示例基础上稍作修改:
从效果上来看还是不同的,咱们再来换换:
.old {
scale: 2;
rotate: 30deg;
translate: -50% -50%;
}
.new {
translate: -50% -50%;
rotate: 30deg;
scale: 2;
}
可以看到,最终的效果是相同的:
从上面两个示例我们可以得出:
CSS Transforms Module Level2中的
translate
、rotate
和scale
属性是没有链式一说,也没有顺序之分。而且多个属性同时使用时和transform
链式写法有一定的差异。
小结
在上面主要探索了一下CSS Transforms Module Level 2中的translate
、rotate
和scale
三个属性,以及这三个属性和transform
对应的translate()
、rotate()
、scale()
以及相关联函数的对比。从上面的验证我们不难发现,这些属性可以找到与之匹配的transform
效果,不同的是和链式的transform
使用有一定的差异,而且这三个属性在样式规则中没有书定顺序的要求。不同的书写顺序得到的效果是一致的。
到目前为止,CSS Transforms Module Level 2还是草案阶段,后面很有可能会有相关的变化。感兴趣的同学可以持续关注相面的更新。如果你在这方面有相关的经验和建议欢迎在下面的评论中与我们共享。