Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
chokcoco opened this issue Jul 18, 2023 · 0 comments
Open

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

chokcoco opened this issue Jul 18, 2023 · 0 comments

Comments

@chokcoco
Copy link
Owner

在 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%);
}

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

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

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 的影响

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 的顺序进行!

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

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

像是这样:

所以,代码会是这样:

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;
}
//...

则整个效果就变成了:

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 语法,代码如下:

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

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

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

我们能否把单独的 translate: 150px 150px 合进上面整个的 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

最后

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

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant