由 transform 被占用引发的思考
transform 很容易被占用
众所周知,CSS 中的 transform 是一个包含很多值的属性,例如
div {
transform: translate(-50%, -50%) scale(1.5);
}
但是,这并不是简写,而是就该这么写,这一点和 background 不一样
div {
background: url("1xxx") 10px 10px / 20px 20px no-repeat;
}
/*等同于以下写法*/
div {
background-image: url("1xxx");
background-position: 10px 10px;
background-size: 20px 20px;
background-repeat: no-repeat;
}
分开写的好处在于,如果只需要改变某一部分就很容易覆盖
div.div1 {
background-image: url("2xxx"); /*只改变图片,不改变其他*/
}
回到前面,如果希望改变 transform 中的某一部分,就不太行了,必须把没改变的部分也写一遍,而且更改的部分也无法抽离出来作为一个公共的样式
div.scale {
transform: scale(2); /*这样不行,会丢失translate*/
}
/*必须写完整*/
div.scale {
transform: translate(-50%, -50%) scale(2);
}
那如何巧妙的解决这个问题呢?
目前的几种思路
用其他方式来代替 transform
碰到这种情况,第一感觉可能就是放弃原有 transform 属性,用其他方式代替。
比如 translate(-50%, -50%)一般是为了实现元素居中效果,可以用 flex 等其他方式实现
.parent {
display: flex;
}
div {
margin: auto; /*通过margin:auto实现居中*/
}
再比如 translate(10px, 10px)这样的偏移,可以用 left 或者 margin-left 等方式实现
div {
position: relative;
left: 10px;
top: 10px;
}
或者干脆再包裹一层父级,也能避免 transform 被占用的问题。
<div class="wrap">
<div class="box"></div>
</div>
不过,这些方式都是规避方式,其实还有官方解决方案
transform 的单独赋值
前面提到过 transform 并不是一个简写属性,所以没有办法像 background 那样对某一部分进行赋值。为了解决这个问题,从 Chrome 104 开始,浏览器终于正式支持单独赋值了
就拿前面那个例子来说
div {
transform: translate(-50%, -50%) scale(1.5);
}
可以写成
div {
translate: -50% -50%;
scale: 1.5;
}
这样如果需要改变某一部分,就只需要像普通属性一样覆盖就行了
不过目前兼容性欠佳,谨慎使用!(适合内部项目尝鲜)
借助 CSS 变量拆分属性
前面的 transform 的单独赋值虽然很好,但是太新了,无法立刻在项目中使用。下面介绍一个兼容性更好,使用更放心的解决方案。
还是上面这个例子
div {
transform: translate(-50%, -50%) scale(1.5);
}
通过 CSS 变量,将 transform 拆分
div {
--translate: -50%, -50%;
--scale: 1.5;
transform: translate(var(--translate)) scale(var(--scale));
}
经过这样拆分以后,CSS 变量就成了独立属性,如果需要覆盖,只需要修改其中一个就行了,而无需关注--translate 是什么样的,这样变化的部分就可以单独作为一个公共的样式了,如下
.div1 {
--translate: -50%, -50%;
}
.div1 {
--translate: 10px, 10px;
}
div.scale {
--scale: 2; /*无需关注其他transform,可以作为公共的样式*/
}
是不是非常清晰明了?下面是一个演示 demo
[type="checkbox"]:checked + div {
--scale: 1.5;
}
@property 实现过渡动画
借助上面的实现看似完美,其实还是有问题的,比如加一个动画。
由于拆分成了 CSS 变量写法,所以动画的变化对象也变成了 CSS 自定义属性(--scale),如下
div {
/**/
animation: scale 1s infinite linear alternate;
}
@keyframes scale {
from {
--scale: 1;
}
to {
--scale: 1.5;
}
}
动是动了,但是没有过渡,太生硬了,那如何解决呢?
这就需要用到@property了,是干什么的呢?简单来讲,可以自定义属性,让自定义变量像颜色一样进行过渡和动画,换句话说,现在执行动画的是--scale 这个属性,而不再是 transform 了
所以,要实现过渡动画,只需要将这个自定义属性通过@property 定义一下就行了,如下
@property --scale {
syntax: "<number>";
inherits: false;
initial-value: 1;
}
不仅仅是 transform
下面来拓展一下,打开你的格局。
除了 transform,还有很多其他复杂的 CSS 属性,在现阶段并没有分开的情况下,这种通过 CSS 变量的方式就再适合不过了,比如 filter
el {
filter: brightness(0.4) invert(75%) grayscale(50%);
}
通过 CSS 变量分离开来就是
el {
--brightness: 0.4;
--invert: 75%;
--grayscale: 50%;
filter: brightness(0.4) invert(75%) grayscale(50%);
}
这样在做一些图片调整时,可以直接用单独的变量控制了。
再比如 rgb 颜色值,写成一个变量不好控制,拆分成三个变量就容易多了
el {
--r: 255;
--g: 255;
--b: 255;
color: rgb(var(--r), var(--g), var(--b));
}
这样在做一些颜色调整交互时就非常方便。
还有一些可以无限叠加的属性,比如 background-image,对,又是这个,可谓是相当复杂了,在有多层背景,而且背景都是很长一段 base64 时,就成了这样
el {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E"),
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E"),
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E");
}
这样一段 CSS 就像乱码一样,非常不利于阅读,分不清哪个图片是哪个,像这种情况就可以借助 CSS 变量改写成如下形式
:root{
--背景1:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E");
--背景2:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E");
--背景3:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm-32-316v116h-67c-10.7 0-16 12.9-8.5 20.5l99 99c4.7 4.7 12.3 4.7 17 0l99-99c7.6-7.6 2.2-20.5-8.5-20.5h-67V140c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12z'%3E%3C/path%3E %3C/svg%3E");
}
el{
background-image: var(--背景1), var(--背景2), var(--背景3)
}
这样对于 el 来说,用到了哪些背景是不是一目了然呢?