likes
comments
collection
share

解放生产力!transform 支持单独赋值改变

作者站长头像
站长
· 阅读数 65

我的新的专栏 -- 现代 CSS 与 Web 动画解决方案

将专注于实现复杂布局,兼容设备差异,制作酷炫动画,制作复杂交互,提升可访问性及构建奇思妙想效果等方面的内容。

在兼顾基础概述的同时,注重对技巧的挖掘,结合实际进行运用,欢迎大家关注。

在 Chrome 104 中,支持了一个非常有意思的新特性。CSS 中的 transform 除了原本的整体写法法,新增了一种分开的写法。不要小看这一点,此点改动在很多时候,能够非常有效的解放生产力,算是一个非常 NICE 的更新。

下面我们就来浅析一下。

什么是分开写的 transform

什么意思呢?我们来看这样一个例子:

在之前,我们可以利用 transform 配合绝对定位的 topleft 进行任意元素的水平垂直居中,像是这样:

<div></div>
div {
    width: 200px;
    height: 200px;
    background: #000;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

这样,我们就得到了一个水平垂直居中的元素:

解放生产力!transform 支持单独赋值改变

但是,如果我们想对这个元素进行一个缩放的动画,该怎么做呢?会是这样:

div {
    width: 200px;
    height: 200px;
    background: #000;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
@keyframes scale {
    0% {
        transform: translate(-50%, -50%) scale(1);
    }
    100% {
        transform: translate(-50%, -50%) scale(1.2);
    }
}

好的,动画本身,并不是重点。重点在于,在上述的 @keyframes 代码中,我们想改变的其实只有 scale() 的值,但是基于现有的 CSS 机制,我们必须把前面控制定位的 translate() 一并写上。

transform 拆开书写

为了解决这个痛点,规范支持了将 transform 分开书写的方式。

所以,对于这一句,transform: translate(-50%, -50%) scale(1),我们可以分别拆开成 translatescale

div {
    width: 200px;
    height: 200px;
    background: #000;
    position: absolute;
    top: 50%;
    left: 50%;
    // transform: translate(-50%, -50%); 删掉这句
    translate: -50% -50%;
    scale: 1;
    animation: scale 1s infinite linear alternate;
    
}

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

是的,我们可以通过 translatescale 分开控制它们,这样我们就能愉快的在 @keyframes 中,只进行 scale 的动画了!妙哉。

所以,根据规范 -- W3 - individual-transforms,于 transform 而言,我们可以将它整个拆解为:

  1. translate
  2. rotate
  3. scale

具体的语法会有一点点不同,具体使用的时候,多看看文档,譬如 translate 接收 3 个参数,第 2、3 个参数如果不存在,则为零(例如:translate: 50% 50% 0)。又譬如 rotate,如果想改变 Y 轴的旋转角度,可以这样写 rotate: y 30deg

再举个例子,下面两组代码是等效的:

// 上下两组代码产生的效果等效!
{
    transform: translate(-50%, -50%) rotateZ(30deg) scale(1.2);
}
{
    translate: -50% -50%;
    rotate: Z 30deg;
    scale: 1.2;
}

是的,规范里没有涉及到 skew 的拆解,也没有 transform: matrix() 相关拆解的写法。

顺序对 transform 的影响

transform 各个值的书写顺序对结果有影响吗?答案是肯定的

借用一张图:

解放生产力!transform 支持单独赋值改变

  1. transform: scale(1.5) translate(50%, 0) 先放大再位移
  2. transform: translate(50%, 0) scale(1.5) 先位移再放大

可以看到,两者的结果是不一样的,因为 translate(50%, 0) 的百分比会以当前元素的实际大小作为参照,而很明显,元素放大前后,大小并不一致。

因此,拆开后和原本写在一起并非完全一样。

如果,我们使用的是 transform,将它们全部写在一起,像是这样:

div {
     transform: rotateY(90deg) translateZ(400px) scale(1.2);
}

此时,元素的 transform 变换遵循的是从左向右进行变换。也就是先旋转 rotate,再 translateZ(),最后缩放 scale()

但是如果,我们分开来写:

div {
    rotate: Y 90deg;
    translate: 0 0 400px;
    scale: 1.2;
}

虽然代码属性的顺序是 rotate --> translate --> scale,但是实际上,无论他们的书写顺序如何,解析的时候都会按照首先 translate,然后 rotate,最后 scale 的顺序进行!

这个会有什么影响吗?当然,这里我们来看这样一个例子。假设,我们想实现这样一个立方体:

解放生产力!transform 支持单独赋值改变

不算上下两个面,以其余 4 个面为例子,正常的做法是,在 transform-style: preserve-3d 状态下,每个面先绕 Y 轴旋转一定的角度,然后进行 translateZ() 的位移。

像是这样:

解放生产力!transform 支持单独赋值改变

所以,代码会是这样:

face1 {
  transform: rotateY(0) translateZ(200px);
}
face2 {
  transform: rotateY(90deg) translateZ(200px);
}
face3 {
  transform: rotateY(180deg) translateZ(200px);
}
face4 {
  transform: rotateY(270deg) translateZ(200px);
}

注意,这里 transform 的变换是先旋转,再位移

如果我们拆开写,变成:

face2 {
    rotate: Y 90deg;
    translate: 0 0 200px;
}
//...

则整个效果就变成了:

解放生产力!transform 支持单独赋值改变

CodePen Demo -- 3D Box View

因为这里实际执行的顺序是,先 translate,后 rotate

所以,实际使用的时候一定要注意,矩阵变换的顺序会影响最终的效果。如果对顺序有严格的要求,还是需要合在一起写。

两种语法共存?

当然,还有一种情况。

那就是当两种语法同时共存的时候。整个对 transform 的赋值,到底是以哪个为准呢?

我们来看看这个例子:

.mix {
    transform: translate(100px, 100px) rotateZ(60deg) scale(2);
    translate: 150px 150px;
    rotate: Z 30deg;
    scale: 1.5;
}

两种语法共存的情况下,transform 的各个值是以一种什么情况存在呢?transform各个值作用的顺序又是否会发生变化呢?

因此这里我们需要探讨两个问题:

  1. 如果两种语法同时存在,且作用顺序一致,重复出现的值会以哪个声明为主?
  2. 如果两种语法同时存在,且作用顺序不一致,作用的顺序以及 transform 每个值的最终计算值是多少?

同时存在,且作用顺序一致

我们先来看第一种情况,同时存在,且作用顺序一致。也就是上面给出的代码:

.mix {
    transform: translate(100px, 100px) rotateZ(60deg) scale(2);
    translate: 150px 150px;
    rotate: Z 30deg;
    scale: 1.5;
}

上面我们说了对于分开写的 transform 而言,无论其书写顺序,总是按照先 translate,然后 rotate,最后 scale 的顺序进行。然后合在一起写的语法其顺序也是如此。

在这里的情况下,结论是,两种写法的作用顺序是一致的,所以对顺序没有影响,而 translaterotatescale 的值最终会以一种叠加态存在。

也就是相当于:

  1. 先进行一次 transform: translate(100px, 100px) rotateZ(60deg) scale(2)
  2. 再接着进行 translate: 150px 150px
  3. 再接着进行 rotate: Z 30deg
  4. 再接着进行 scale: 1.5

同时存在,且作用顺序不一致

那如果作用顺序不一致,两种写法又同时存在,又会是怎么样一种情况呢?

像是这样:

.mix {
    transform: rotateZ(60deg) translate(100px, 100px) scale(2);
    translate: 150px 150px;
    rotate: Z 30deg;
    scale: 1.5;
}

这里,对于合在一起写的 transform: rotateZ(60deg) translate(100px, 100px) scale(2) 而言,执行顺序是 rotate --> translate --> scale,而对于分开写的 3 个值,执行顺序是 translate --> rotate --> scale

其实这里与上述也顺序不一致的规则也是一样的:

  1. 先进行一次 transform: translate(100px, 100px) rotateZ(60deg) scale(2)
  2. 以 (1) 的结果为基准,再进行一次 translate: 150px 150px
  3. 以 (2) 的结果为基准,再进行一次 rotate: Z 30degx
  4. 以 (3) 的结果为基准,再进行一次 scale: 1.5

两种语法共存的误区

当两种语法都存在时,容易产生一种理解上的误区。

看这样一种情况,假设我们有这样一个 div:

解放生产力!transform 支持单独赋值改变

如果存在两种 transform 语法,代码如下:

div {
    transform: rotateZ(60deg) translate(100px, 100px);
    translate: 150px 150px;
}

那么按照刚刚的叠加规则,其结果,是不是就等于下面的结果呢:

div {
    transform: rotateZ(60deg) translate(250px, 250px);
}

我们能否把单独的 translate: 150px 150px 合进上面整个的 transform 中呢?

答案是不行的!

两种语法的作用如下:

解放生产力!transform 支持单独赋值改变

这是因为,对于分开的写的语法而言,第二次的 translate: 150px 150px 是纯粹的向右位移 150px,向下位移 150px,不会考虑当前旋转角度的。

而对于 transform: rotateZ(60deg) translate(250px, 250px) 而言,这里的 250px, 250px 是基于旋转了 60° 的基础上而言的。这一点,再实际使用的过程中,一定要注意!

CodePen Demo -- individual transforms Demo

做好回退机制

当然,如果你迫不及待的想使用这个特性了。可以提前使用 @supports 语法进行兼容。到今天,@supports 语法的兼容性已经非常之好了。

我们可以这样使用:

.container {
  translate: 50% 10%;
  rotate: 80deg;
  scale: 1.5;
}

// 如果不支持上述的语法,则 supports 内的语法生效
@supports not (scale: 1) {
  transform: translate(50%, 10%) rotate(80deg) scale(1.5);
}

是的,如果不支持 .container 内的的语法,则 supports 内的语法生效。

总结一下

本文比较简单,没有复杂的 CSS 动效,主要介绍了从 Chrome 104 开始,transform 支持单独赋值这一更新。其中有几点需要注意:

  1. transform 而言,我们可以将它整个拆解为 translaterotatescale 的分开写法
  2. 对于完整的 transform 而言,其执行顺序遵循从左向右进行变换
  3. 对于分开写的 transform 而言,无论其书写顺序,总是按照先 translate,然后 rotate,最后 scale 的顺序进行!
  4. 对于两种语法而言,它们是可以共存的。并且会按照规则进行叠加,先作用 transform,再基于上述的整体,按照单个的 translate --> rotate --> scale 进行(如果有)。
  5. 使用 @supports 进行 fallback

最后

好了,本文到此结束,希望本文对你有所帮助 :)

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

转载自:https://juejin.cn/post/7152331836578856967
评论
请登录