本文將介紹一種基於 CSS 變數技巧,透過合理使用 CSS 變數,實現 CSS 動畫 @keyframes
的複用。
CSS 自定義變數
CSS 自定義變數大家應該都比較熟悉了,已經不能算是新知識了,快速過一遍。
CSS 自定義變數(CSS Variable),在之前也叫做 CSS 自定義屬性,其使用方式如下:
// 宣告一個變數:
:root{
--bgColor: #000;
}
這裡我們藉助了上面#12、結構性偽類
中的:root{ }
偽類,在全域性:root{ }
偽類中定義了一個 CSS 變數,取名為--bgColor
。
定義完了之後則是使用,假設我要設定一個 div 的背景色為黑色:
.main{
background:var(--bgColor);
}
這裡,我們在需要使用之前定義變數的地方,透過 var(定義的變數名)
來呼叫。
在 @keyframes 中使用 CSS 變數
OK,迴歸我們的正題。巧用 CSS 變數,實現動畫函式複用。
假設,我們現在有多個元素,需要實現一個位移動畫,從位置 A 位移到 位置 B,位置 A 相同,但是位置 B 不一樣,像是這樣:
正常而言,由於終點不一樣,我們可能需要實現 3 個不一樣的 @keyframes
,像是這樣:
<ul>
<li></li>
<li></li>
<li></li>
</ul>
li:nth-child(1) {
animation: move1 2s linear;
}
li:nth-child(2) {
animation: move2 2s linear;
}
li:nth-child(3) {
animation: move3 2s linear;
}
@keyframes move1 {
60%,
100% {
transform: translate(150px);
}
}
@keyframes move2 {
60%,
100% {
transform: translate(120px);
}
}
@keyframes move3 {
60%,
100% {
transform: translate(200px);
}
}
這個程式碼有問題嗎?沒有。
但是,我們可以利用 CSS 變數,讓它變得更為簡潔,我們改造一下 @keyframes
程式碼,將固定的位移值,變成一個變數:
@keyframes move {
60%,
100% {
transform: translate(var(--dis));
}
}
由於 CSS 變數是存在作用域的,我們可以透過 CSS 變數的方式,給每一個 li 定義一個不同的 --dis
變數,像是這樣:
li:nth-child(1) {
--dis: 150px;
}
li:nth-child(2) {
--dis: 120px;
}
li:nth-child(3) {
--dis: 200px;
}
這樣,雖然動畫的結束點不一樣,但是我們利用 CSS 變數,複用了同一個 @keyframes
函式:
透過內聯 style 屬性傳入自定義變數
除了透過在 <style>
內傳入不同的自定義變數,我們還可以透過內聯 style 屬性傳入自定義變數。
我們再改造一下我們的 @keyframes
:
@keyframes move {
60%,
100% {
transform: translate(var(--end));
background: var(--color);
}
}
這一次,我們不需要透過 :nth-child()
去修改每一個 li 的 CSS,而是透過 HTML 元素的內聯 style
屬性,像是這樣:
<ul>
<li style="--end: 150px; --color: red;"></li>
<li style="--end: 200px; --color: blue;"></li>
<li style="--end: 120px; --color: green;"></li>
</ul>
是的,每個 li 元素的 @keyframes
可以讀取到每個 li 的 style
裡面定義的不一樣的 CSS 變數。
這樣,我們就可以得到如下效果:
完整的程式碼,可以戳這裡:CodePen Demo -- 巧用 CSS 變數,實現動畫函式複用
實戰演練
下面我們實戰演練一下,上一點難度。
在很久之前,我們實現過這樣一個動畫效果:
這個動畫效果的實現方式在於:
- 父級元素實現一個 rotateZ(360deg) 的勻速動畫
- 子級元素實現一個反向的 rotateZ(-360deg) 的勻速動畫
- 給父級元素新增一個 rotateX(40deg) 的動畫
由於父容器和子容器同時相反向旋轉,所以子元素看上去其實和沒有旋轉是一樣的。但是由於又新增了一個 rotateX(40deg)
動畫,因此看上去就會有這樣一種 3D 效果。
在之前,我們的程式碼是這樣的:
<div class="reverseRotate">
<div class="rotate">
</div>
</div>
.rotate {
animation: rotate 5s linear infinite;
}
.reverseRotate {
animation: reverseRotate 5s linear infinite;
}
@keyframes rotate {
0% {
transform: rotateX(0deg) rotateZ(0deg);
}
50% {
transform: rotateX(40deg) rotateZ(180deg);
}
100% {
transform: rotateX(0deg) rotateZ(360deg);
}
}
@keyframes reverseRotate {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(-360deg);
}
}
可以看到,我們這裡實現了兩個動畫效果:
@keyframes rotate {}
父容器的旋轉動畫@keyframes reverseRotate {}
子容器的旋轉動畫
其實,這裡,運用今天的技巧,我們可以把兩個動畫合成為一個,利用 CSS 自定義變數進行控制。改造後更簡潔的 CSS 程式碼如下:
.rotate {
--degZ: 360deg;
--degZMiddle: 180deg;
--degX: 30deg;
animation: rotate 5s linear infinite;
}
.reverseRotate {
--degZ: -360deg;
--degZMiddle: -180deg;
--degX: 0;
animation: rotate 5s linear infinite;
}
@keyframes rotate {
0% {
transform: rotateX(0deg) rotateZ(0deg);
}
50% {
transform: rotateX(var(--degX)) rotateZ(var(--degZMiddle));
}
100% {
transform: rotateX(0deg) rotateZ(var(--degZ));
}
}
是的,我們可以得到同樣的效果!
完整的程式碼,你可以戳這裡:CodePen DEMO -- Css動畫正反旋轉相消
圖片旋轉配合容器旋轉
下面,我們再來嘗試一個有意思的動畫效果,圖片旋轉配合容器旋轉。
在上述的基礎上,如果我們把子元素,改成圖片,整個效果就會有意思不少,我們稍微改變一點點程式碼:
<div class="reverseRotate">
<img class="rotate" src="https://picsum.photos/1000/1000?random=5" alt="">
</div>
.rotate,
.reverseRotate {
width: 60vh;
height: 60vh;
}
.reverseRotate {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 3px solid #999;
overflow: hidden;
}
.rotate {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
}
.rotate {
--degZ: 360deg;
animation: rotate 5s linear infinite;
}
.reverseRotate {
--degZ: -360deg;
animation: rotate 5s linear infinite;
}
@keyframes rotate {
0% {
transform: translate(-50%, -50%) rotateZ(0deg);
}
100% {
transform: translate(-50%, -50%) rotateZ(var(--degZ));
}
}
這裡,我們做了什麼事情呢?
- 去掉了 3D 效果
- 給外層容器加了邊框
- 內層圖片基於父容器絕對定位,水平垂直居中
- 內外兩層容器反向旋轉 360° 動畫
這樣,我們就能看到,雖然內外兩層容器同時在進行相反方向的旋轉 360° 動畫,但是內部的圖片其實是靜止不動的!
效果如下:
由於,內部圖片的大小為父容器的 100%
,所以在旋轉過程中,父容器會有明顯的無法包裹住整個圖片的情況。
這個很好解決,我們只需要把圖片大小調整大一點:
// ... 其它程式碼不變
.rotate {
width: 150%;
height: 150%;
}
.rotate {
--degZ: 360deg;
animation: rotate 5s linear infinite;
}
正常而言,對於正方形容器,內部圖片設定到 141%
即可滿足父容器旋轉過程,可以一直包裹住圖片的效果。那麼,我們就能得到這樣一種效果:
完整的程式碼,你可以戳這裡:CodePen Demo -- Css動畫正反旋轉相消
Grid 佈局配合正反旋轉動畫
當然,上述當只有一個容器的時候,整個動畫效果還不夠震撼。
如果我們可以把這個效果融合進整個佈局的動畫之中,整個效果又會完全不一樣。
在 Rotating gallery with CSS scroll-driven animations 這篇文章中,作者提供了一種非常巧妙的思路,將 Grid 佈局動畫與上述動畫效果巧妙的結合了起來。
首先,我們利用 Grid 佈局,實現這樣一個簡單的網格佈局結構:
<div class="container">
<div class="A">
<img src="https://picsum.photos/600/600?random=1" alt=""></div>
<div class="B">
<img src="https://picsum.photos/600/600?random=2" alt=""></div>
<div class="C">
<img src="https://picsum.photos/600/600?random=3" alt=""></div>
<div class="D">
<img src="https://picsum.photos/600/600?random=4" alt=""></div>
<div class="E">
<img src="https://picsum.photos/600/600?random=5" alt=""></div>
</div>
.container {
width: 60vmin;
height: 60vmin;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
gap: 4px;
grid-template-areas:
"E B B"
"E A C"
"D D C";
}
.container > div {
border: 3px solid #431312;
border-radius: 5px;
}
.A {
grid-area: A;
}
.B {
grid-area: B;
}
.C {
grid-area: C;
}
.D {
grid-area: D;
}
.E {
grid-area: E;
}
效果如下:
接下來,我們要做的,就是結合上面的知識點,容器滾動起來,圖片反向滾動起來,配合一些 tranfrom 變換。
有了上面的鋪墊,下面的新增的程式碼就非常好理解了:
.container > div img {
--scale: 1;
--rotation: -360deg;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 260%;
height: 260%;
object-fit: cover;
object-position: center;
}
.container,
.container > div img {
animation: 10s scale-up both ease-in-out infinite alternate;
}
@keyframes scale-up {
0% {
transform: translate(-50%, -50%) scale(var(--scale)) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) scale(1) rotate(var(--rotation));
}
}
這樣,我們就得到了一個高階感拉滿的網格旋轉動畫:
注意,這裡我們依舊是透過 CSS 自定義變數,在不同元素間,複用了同一個動畫 @keyframes
函式。
完整的程式碼,你可以戳這裡:CodePen Demo -- Grid 圖片旋轉動畫 & 使用 CSS 變數複用動畫函式
最後
好了,本文到此結束,希望本文對你有所幫助 :)
想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ?
更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。