動(dòng)畫合成小技巧,你應(yīng)該了解一下!

以下文章來(lái)源于前端偵探 ,作者XboxYan

介紹一個(gè) CSS 動(dòng)畫合成小技巧。先看效果

Kapture 2022-06-26 at 16.57.42
這是一個(gè)非?!皠?dòng)感”的倒計(jì)時(shí)效果,在一些活動(dòng)開(kāi)場(chǎng)中比較常見(jiàn),分析一下整個(gè)動(dòng)畫過(guò)程,不難發(fā)現(xiàn),有以下幾類動(dòng)畫

數(shù)字的變化
縮小和放大
透明度變化
不知道小伙伴能否觀察出來(lái)呢?下面來(lái)一起來(lái)看看具體實(shí)現(xiàn)吧

一、數(shù)字的變化
先來(lái)看數(shù)字的變化。

在以前,數(shù)字的變化可能需要?jiǎng)?chuàng)建多個(gè)標(biāo)簽,然后改變位移來(lái)實(shí)現(xiàn)

<count-down>
 <span>5</span>
  <span>4</span>
  <span>3</span>
  <span>2</span>
  <span>1</span>
</count-down>
這種方式需要?jiǎng)?chuàng)建多個(gè)標(biāo)簽,略微繁瑣,也不易擴(kuò)展?,F(xiàn)在有更簡(jiǎn)潔的方式可以實(shí)現(xiàn)了,那就是 CSS @property[1]。這是干什么的呢?簡(jiǎn)單來(lái)講,可以自定義屬性,在這個(gè)例子中,可以讓數(shù)字像顏色一樣進(jìn)行過(guò)渡和動(dòng)畫,可能不太懂,直接看例子吧

假設(shè) HTML 是這樣的

<count-down style="--t: 5"></count-down>
然后我們通過(guò) CSS 變量將數(shù)字渲染到頁(yè)面,這里需要借助偽元素和計(jì)數(shù)器

有興趣的可以參考這篇文章:小tips: 如何借助content屬性顯示CSS var變量值[2]

count-down::after{
  counter-reset: time var(--t);
  content: counter(time);
}
效果如下







image-20220626135133534
如何讓這個(gè)數(shù)字變化呢?可以用到 CSS 動(dòng)畫

@keyframes count {
    to {
        --t: 0
    }
}
count-down::after{
    --t: 5;
    counter-reset: time var(--t);
    content: counter(time);
    animation: count 5s forwards;
}
效果如下


Kapture 2022-06-26 at 13.55.42
現(xiàn)在的效果僅僅是5秒后,數(shù)字從 5 變成了 0,并沒(méi)有 5 => 4 => 3 => 2 => 1 這種階段變化。然后最重要的一步來(lái)了,加上以下自定義屬性

@property --t {
    syntax: '<integer>';
    inherits: false;
    initial-value: 0;
}
對(duì)的,僅僅添加這一小段 CSS,效果就出來(lái)了


Kapture 2022-06-26 at 14.03.07
是不是很神奇?可以這么理解,通過(guò)@property定義后,這個(gè)變量--t本身可以單獨(dú)設(shè)置動(dòng)畫了,就像顏色變化一樣。

另外,使用計(jì)數(shù)器的好處是可以隨意更換類型,比如將上面的阿拉伯?dāng)?shù)字換成中文計(jì)數(shù),只需要更換計(jì)數(shù)器類型就行了

完整類型可以參考:list-style-type[3]

count-down::after{
    --t: 5;
    counter-reset: time var(--t);
    content: counter(time, cjk-decimal); /*中日韓十進(jìn)制數(shù)*/
    animation: count 5s forwards;
}
效果如下

Kapture 2022-06-26 at 14.14.20
是不是非常方便呢?

二、倒計(jì)時(shí)的終點(diǎn)
上面的計(jì)數(shù)器最后的終點(diǎn)是“0”,顯然我們需要一些特定的提示,比如“Go~”


image-20220626143135177
如何改變最后一幀的狀態(tài)呢?這里有兩種方式:

通過(guò)動(dòng)畫覆蓋
通過(guò)計(jì)數(shù)器覆蓋
首先來(lái)看第一種方式,這個(gè)比較好理解,重新定義一個(gè)動(dòng)畫,在倒計(jì)時(shí)結(jié)束后,將最后一幀重置一下

@keyframes stop {
    to {
        content: 'Go~';
    }
}
count-down::after{
    --t: 5;
    counter-reset: time var(--t);
    content: counter(time);
    animation: count 5s forwards,
    stop 5s step-end forwards;
}
·效果如下


Kapture 2022-06-26 at 14.46.18
注意這里動(dòng)畫函數(shù)是step-end,為啥是這個(gè)呢?step-end也可寫作steps(1,end),你可以理解為在整個(gè)動(dòng)畫只有兩種狀態(tài),在運(yùn)行過(guò)程中,都是初始狀態(tài),只有到達(dá)最后一幀才改變狀態(tài),下面是 MDN 的截圖


image-20220626145733201
下面來(lái)看第二種方式,通過(guò)自定義計(jì)數(shù)器來(lái)實(shí)現(xiàn)。原理其實(shí)和 JS 思維有些類似,當(dāng)數(shù)字為 0 時(shí),讓計(jì)數(shù)器指定一個(gè)特殊的值,具體實(shí)現(xiàn)如下

@counter-style stop {
    system: cyclic;
    symbols: "Go~";
    range: 0 0;
}
這里簡(jiǎn)單解釋一下,這里有個(gè)range屬性,表示計(jì)數(shù)器的范圍,由于這里只需要指定為 0,所以是區(qū)間0 0。然后是system,表示計(jì)算系統(tǒng),這里為cyclic,表示循環(huán)使用開(kāi)發(fā)者提供的一套字符,字符由symbos定義。然后symbos表示計(jì)算符號(hào),也就是具體展示的字符,這里指定為Go~就行了。

這部分自定義計(jì)數(shù)器內(nèi)容比較復(fù)雜,也比較新,有興趣的可以參考張?chǎng)涡竦倪@篇文章:CSS @counter-style規(guī)則詳細(xì)介紹[4]

然后是應(yīng)用

count-down::after{
   /**/
    counter-reset: time var(--t);
    content: counter(time, stop); /*自定義計(jì)數(shù)器*/
}
這樣也能達(dá)到相同的效果,實(shí)現(xiàn)也更加優(yōu)雅


Kapture 2022-06-26 at 14.46.18
三、縮放和透明度變化
這兩個(gè)動(dòng)畫其實(shí)是同時(shí)進(jìn)行的,可以放在一個(gè)動(dòng)畫里

@keyframes shark {
    0%{
        opacity: 1;
        transform: scale(1);
    }
    
    50%{
        opacity: 0;
        transform: scale(0.4);
    }
}
然后設(shè)置動(dòng)畫時(shí)長(zhǎng)為 1s,循環(huán) 5 次

count-down::after{
    --t: 5;
    counter-reset: time var(--t);
    content: counter(time);
    animation: count 5s steps(5) forwards,
    shark 1s 5;
}
效果如下


Kapture 2022-06-26 at 16.47.09
是不是稍微有些突兀?因?yàn)閿?shù)字的變化是突然的,需要將數(shù)字的變化隱藏到透明度為 0 的時(shí)候,為了達(dá)到這種效果,只需要將閃爍動(dòng)畫延遲 0.5 秒即可

count-down::after{
    --t: 5;
    counter-reset: time var(--t);
    content: counter(time);
    animation: count 5s steps(5) forwards,
    shark 1s .5s 5; /*延遲 0.5s*/
}
這樣就自然多了







Kapture 2022-06-26 at 16.51.37
不過(guò)還有優(yōu)化的空間。比如現(xiàn)在數(shù)字動(dòng)畫有些太連貫了,如果希望數(shù)字出現(xiàn)后稍微停留一小會(huì),或者說(shuō)希望出現(xiàn)的慢一點(diǎn),消失的快一點(diǎn),如何處理呢?其實(shí)這比想象中的要容易許多,只需要改一下關(guān)鍵幀位置就行了,如下

@keyframes shark {
    0%{
        opacity: 1;
        transform: scale(1);
    }
    
    20%{
        opacity: 0;
        transform: scale(0.4);
    }
}
同時(shí),延遲的時(shí)間也需要改成 0.8 秒,效果如下

Kapture 2022-06-26 at 16.57.42
這樣就實(shí)現(xiàn)了文章開(kāi)頭所示效果

下面重點(diǎn)來(lái)了~完整代碼如下

@property --t {
    syntax: '<integer>';
    inherits: false;
    initial-value: 0;
}
@counter-style stop {
    system: cyclic;
    symbols: "Go~";
    range: infinite 0;
}
html,body{
    margin: 0;
    height: 100%;
    display: grid;
    place-content: center;
}
count-down{
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: Consolas, Monaco, monospace;
    font-size: 120px;
}
count-down::after{
    --t: 5;
    --dur: 1;
    counter-reset: time var(--t);
    content: counter(time, stop);
    animation: count calc( var(--t) * var(--dur) * 1s ) steps(var(--t)) forwards,
    shark calc(var(--dur) * 1s) calc(var(--dur) * .8s) calc(var(--t));
}
@keyframes count {
    to {
        --t: 0;
    }
}
@keyframes shark {
    0%{
        opacity: 1;
        transform: scale(1);
    }
    
    20%{
        opacity: 0;
        transform: scale(0.4);
    }
}
你也可以訪問(wèn)在線例子:CSS count-down(codepen.io)[5]或者CSS count-down(juejin.cn)[6]

另外,demo 中還有個(gè)小彩蛋,點(diǎn)擊可以重新運(yùn)行動(dòng)畫,實(shí)現(xiàn)方式如下

count-down:active::after{
    animation: none;
}
四、其他動(dòng)畫效果
除了縮放效果,還可以有一些位移的動(dòng)畫,比如這樣的

@keyframes shark {
    0%{
        opacity: 1;
        transform: translateY(0);
    }
    
    20%{
        opacity: 0;
        transform: translateY(100px);
    }
}
效果如下


Kapture 2022-06-26 at 17.07.00
是不是有點(diǎn)奇怪?動(dòng)畫不夠連貫,一會(huì)向下一會(huì)向上,有沒(méi)有辦法消失和出現(xiàn)都是從上到下的呢?當(dāng)然也是可以的,實(shí)現(xiàn)如下

@keyframes shark {
    0%{
        opacity: 1;
        transform: translateY(0);
    }
    
    20%{
        opacity: 0;
        transform: translateY(100px);
    }

    21%{
        opacity: 0;
        transform: translateY(-100px);
    }
}
這里多加了一個(gè)非?!班徑钡年P(guān)鍵幀,表示在透明狀態(tài)下,“迅速”改變位移,這樣在數(shù)字出現(xiàn)時(shí)的動(dòng)畫就感覺(jué)是從上到下的,整體更為流暢,效果如下


Kapture 2022-06-26 at 17.15.02
還可以調(diào)整一下前面的縮放效果,讓出來(lái)的時(shí)候更大,效果也更為震撼

@keyframes shark {
    0%{
        opacity: 1;
        transform: scale(1);
    }
    
    20%{
        opacity: 0;
        transform: scale(.4);
    }

    21%{
        opacity: 0;
        transform: scale(5);
    }
}
效果如下


Kapture 2022-06-26 at 17.38.54
當(dāng)然還有其他效果,比如旋轉(zhuǎn),斜切等,這就需要發(fā)揮你的想象了~

五、總結(jié)和說(shuō)明
以上就是本文的全部?jī)?nèi)容了,一個(gè)簡(jiǎn)單的小動(dòng)畫,你學(xué)會(huì)了嗎?下面總結(jié)一下實(shí)現(xiàn)要點(diǎn):

復(fù)雜動(dòng)畫可以分解成多個(gè)簡(jiǎn)單的動(dòng)畫
數(shù)字的變化可以通過(guò)多個(gè)標(biāo)簽,改變位移實(shí)現(xiàn)
CSS 計(jì)數(shù)器可以將數(shù)字變量渲染到頁(yè)面
CSS @property 可以將CSS變量設(shè)置動(dòng)畫,就像顏色變化一樣
CSS 計(jì)數(shù)器的好處是可以隨意更改類型,比如中文計(jì)數(shù)
倒計(jì)時(shí)的終點(diǎn)默認(rèn)是數(shù)字 0 ,可以通過(guò)另一個(gè)動(dòng)畫重置最后一幀
可以通過(guò)自定義 CSS 計(jì)數(shù)器,讓某個(gè)計(jì)數(shù)符號(hào)渲染成指定字符
縮放和透明的變化是同時(shí)進(jìn)行的,可以放在一個(gè)動(dòng)畫里
數(shù)字的變化需要注意安排在透明度為0的時(shí)候,不然數(shù)字變化很突兀
數(shù)字的出現(xiàn)和消失動(dòng)畫可以添加一個(gè)鄰近的關(guān)鍵幀來(lái)快速歸位
另外,文中用了一些比較新的屬性,比如 @property,還有自定義計(jì)數(shù)器,不過(guò)沒(méi)關(guān)系,文中也都提到了其他解決方案,動(dòng)畫的整體思路是不變的,如何觀察和分解動(dòng)畫,這個(gè)才是最重要的。最后,如果覺(jué)得還不錯(cuò),對(duì)你有幫助的話,歡迎點(diǎn)贊、收藏、轉(zhuǎn)發(fā)???

參考資料
[1]
CSS @property: https://developer.mozilla.org/zh-CN/docs/Web/CSS/@property

[2]
小tips: 如何借助content屬性顯示CSS var變量值: https://www.zhangxinxu.com/wordpress/2019/05/content-css-var/

[3]
list-style-type: https://developer.mozilla.org/zh-CN/docs/Web/CSS/list-style-type

[4]
CSS @counter-style規(guī)則詳細(xì)介紹: https://www.zhangxinxu.com/wordpress/2021/10/css-counter-style/

[5]
CSS count-down(codepen.io): https://codepen.io/xboxyan/pen/GRxKqPm

[6]
CSS count-down(juejin.cn): https://code.juejin.cn/pen/7113470934589112327






作者:XboxYan

歡迎關(guān)注微信公眾號(hào) :前端印象