一、transform 很容易被占用
眾所周知,CSS 中的transform是一個(gè)包含很多值的屬性,例如:
div{
transform: translate(-50%, -50%) scale(1.5)
}
但是,這并不是簡(jiǎn)寫,而是就該這么寫,這一點(diǎn)和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中的某一部分,就不太行了,必須把沒改變的部分也寫一遍,而且更改的部分也無(wú)法抽離出來(lái)作為一個(gè)公共的樣式。
div.scale{
transform: scale(2) /*這樣不行,會(huì)丟失translate*/
}
/*必須寫完整*/
div.scale{
transform: translate(-50%, -50%) scale(2)
}
那如何巧妙的解決這個(gè)問題呢?
二、目前的幾種思路
1. 用其他方式來(lái)代替 transform
碰到這種情況,第一感覺可能就是放棄原有transform屬性,用其他方式代替。
比如translate(-50%, -50%)一般是為了實(shí)現(xiàn)元素居中效果,可以用flex等其他方式實(shí)現(xiàn)。
.parent{
display: flex
}
div{
margin: auto; /*通過margin:auto實(shí)現(xiàn)居中*/
}
再比如translate(10px, 10px)這樣的偏移,可以用left或者margin-left等方式實(shí)現(xiàn)。
div{
position: relative;
left: 10px;
top: 10px;
}
或者干脆再包裹一層父級(jí),也能避免transform被占用的問題。
<div class="wrap">
<div class="box">
</div>
</div>
不過,這些方式都是規(guī)避方式,其實(shí)還有官方解決方案。
2. transform 的單獨(dú)賦值
前面提到過transform并不是一個(gè)簡(jiǎn)寫屬性,所以沒有辦法像background那樣對(duì)某一部分進(jìn)行賦值。為了解決這個(gè)問題,從Chrome 104開始,瀏覽器終于正式支持單獨(dú)賦值了。
有興趣的可以參考這篇文章:解放生產(chǎn)力!transform 支持單獨(dú)賦值改變[1]。
就拿前面那個(gè)例子來(lái)說(shuō):
div{
transform: translate(-50%, -50%) scale(1.5)
}
可以寫成。
div{
translate: -50% -50%;
scale: 1.5
}
這樣如果需要改變某一部分,就只需要像普通屬性一樣覆蓋就行了。
不過目前兼容性欠佳,謹(jǐn)慎使用?。ㄟm合內(nèi)部項(xiàng)目嘗鮮)。
三、借助 CSS 變量拆分屬性
前面的transform 的單獨(dú)賦值雖然很好,但是太新了,無(wú)法立刻在項(xiàng)目中使用。下面介紹一個(gè)兼容性更好,使用更放心的解決方案。
還是上面這個(gè)例子:
div{
transform: translate(-50%, -50%) scale(1.5)
}
通過 CSS 變量,將transform拆分;
div{
--translate: -50%, -50%;
--scale: 1.5;
transform: translate(var(--translate)) scale(var(--scale))
}
經(jīng)過這樣拆分以后,CSS 變量就成了獨(dú)立屬性,如果需要覆蓋,只需要修改其中一個(gè)就行了,而無(wú)需關(guān)注--translate是什么樣的,這樣變化的部分就可以單獨(dú)作為一個(gè)公共的樣式了,如下:
.div1{
--translate: -50%, -50%;
}
.div1{
--translate: 10px, 10px;
}
div.scale{
--scale: 2; /*無(wú)需關(guān)注其他transform,可以作為公共的樣式*/
}
是不是非常清晰明了?下面是一個(gè)演示demo;
[type="checkbox"]:checked+div{
--scale: 1.5
}
效果如下:

完整代碼可以查看以下任意鏈接:
- CSS transform (codepen.io)[2]
- CSS transform - 碼上掘金 (juejin.cn)[3]
- CSS transform (runjs.work)[4]
四、借助 @property 實(shí)現(xiàn)過渡動(dòng)畫
上面的實(shí)現(xiàn)看似完美,其實(shí)還是有問題的,比如加一個(gè)動(dòng)畫。
由于拆分成了 CSS 變量寫法,所以動(dòng)畫的變化對(duì)象也變成了 CSS 自定義屬性(--scale),如下:
div{
/**/
animation: scale 1s infinite linear alternate;
}
@keyframes scale {
from {
--scale:1
}
to {
--scale:1.5
}
}
實(shí)際效果是這樣的:

動(dòng)是動(dòng)了,但是沒有過渡,太生硬了,那如何解決呢?
這就需要用到@property[5]了,是干什么的呢?簡(jiǎn)單來(lái)講,可以自定義屬性,讓自定義變量像顏色一樣進(jìn)行過渡和動(dòng)畫,換句話說(shuō),現(xiàn)在執(zhí)行動(dòng)畫的是--scale這個(gè)屬性,而不再是transform了。
所以,要實(shí)現(xiàn)過渡動(dòng)畫,只需要將這個(gè)自定義屬性通過@property定義一下就行了,如下:
@property --scale {
syntax: '<number>';
inherits: false;
initial-value: 1;
}
效果如下

和傳統(tǒng)實(shí)現(xiàn)效果一樣,但是代碼上更加優(yōu)雅。
完整代碼可以查看以下任意鏈接:
- CSS transform @property (codepen.io)[6]
- CSS transform @property - 碼上掘金 (juejin.cn)[7]
- CSS transform @property (runjs.work)[8]
五、不僅僅是 transform
下面來(lái)拓展一下,打開你的格局。
除了transform,還有很多其他復(fù)雜的 CSS 屬性,在現(xiàn)階段并沒有分開的情況下,這種通過 CSS 變量的方式就再適合不過了,比如filter。
el{
filter: brightness(0.4) invert(75%) grayscale(50%);
}
通過 CSS 變量分離開來(lái)就是;
el{
--brightness: 0.4;
--invert: 75%;
--grayscale: 50%;
filter: brightness(0.4) invert(75%) grayscale(50%);
}
這樣在做一些圖片調(diào)整時(shí),可以直接用單獨(dú)的變量控制了,比如像keynote這樣的。

再比如rgb顏色值,寫成一個(gè)變量不好控制,拆分成三個(gè)變量就容易多了。
el{
--r: 255;
--g: 255;
--b: 255;
color: rgb(var(--r), var(--g), var(--b));
}
這樣在做一些顏色調(diào)整交互時(shí)就非常方便,還是keynote中的例子。

還有一些可以無(wú)限疊加的屬性,比如background-image,對(duì),又是這個(gè),可謂是相當(dāng)復(fù)雜了,在有多層背景,而且背景都是很長(zhǎng)一段base64時(shí),就成了這樣。
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 就像亂碼一樣,非常不利于閱讀,分不清哪個(gè)圖片是哪個(gè),像這種情況就可以借助 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)
}
這樣對(duì)于el來(lái)說(shuō),用到了哪些背景是不是一目了然呢?
六、總結(jié)一下
以上就是本文全部?jī)?nèi)容了,也是 CSS 變量的一些實(shí)戰(zhàn)小技巧,下面總結(jié)一下:
- 部分 CSS 屬性比較復(fù)雜,比如transform,很容易被占用
- 通常的解決方式是規(guī)避,比如換一種居中方式,或者用left、top等代替
- 新出現(xiàn)的transform 的單獨(dú)屬性可以解決這個(gè)問題,目前還比較新,僅適合內(nèi)部項(xiàng)目嘗鮮
- 借助 CSS 變量可以拆分復(fù)雜的屬性
- 借助@property 讓CSS自定義屬性支持過渡動(dòng)畫
- 不僅僅是transform,思維放開,復(fù)雜的屬性都可以采取這樣的思路
參考資料
[1]解放生產(chǎn)力!transform 支持單獨(dú)賦值改變: https://juejin.cn/post/7152331836578856967
[2]CSS transform (codepen.io): https://codepen.io/xboxyan/pen/BaVLMpv
[3]CSS transform - 碼上掘金 (juejin.cn): https://code.juejin.cn/pen/7161704312681758755
[4]CSS transform (runjs.work): https://runjs.work/projects/4714accc300d441a
[5]@property: https://developer.mozilla.org/zh-CN/docs/Web/CSS/@property
[6]CSS transform @property (codepen.io): https://codepen.io/xboxyan/pen/OJERdpr
[7]CSS transform @property - 碼上掘金 (juejin.cn): https://code.juejin.cn/pen/7161709269698478083
[8]CSS transform @property (runjs.work): https://runjs.work/projects/377a051b84d54bf6