動畫合成小技巧!CSS 實現動感的倒數計時效果

XboxYan發表於2022-07-11
歡迎關注微信公眾號: 前端偵探

介紹一個 CSS 動畫合成小技巧。先看效果

Kapture 2022-06-26 at 16.57.42

這是一個非常“動感”的倒數計時效果,通常在一些活動開場中比較常見,分析一下整個動畫過程,不難發現,有以下幾類動畫

  1. 數字的變化
  2. 縮小和放大
  3. 透明度變化

不知道小夥伴能否觀察出來呢?下面來一起來看看具體實現吧

一、數字的變化

先來看數字的變化。

這個技巧在之前的文章:還在使用定時器嗎?CSS 也能實現電子時鐘 中首次用到,這裡再次介紹一下

在以前,數字的變化可能需要建立多個標籤,然後改變位移來實現

<count-down>
    <span>5</span>
  <span>4</span>
  <span>3</span>
  <span>2</span>
  <span>1</span>
</count-down>

這種方式需要建立多個標籤,略微繁瑣,也不易擴充套件。現在有更簡潔的方式可以實現了,那就是 CSS @property。這是幹什麼的呢?簡單來講,可以自定義屬性,在這個例子中,可以讓數字像顏色一樣進行過渡和動畫,可能不太懂,直接看例子吧

假設 HTML 是這樣的

<count-down style="--t: 5"></count-down>

然後我們通過 CSS 變數將數字渲染到頁面,這裡需要藉助偽元素和計數器

有興趣的可以參考這篇文章:小tips: 如何藉助content屬性顯示CSS var變數值
count-down::after{
  counter-reset: time var(--t);
  content: counter(time);
}

效果如下

image-20220626135133534

如何讓這個數字變化呢?可以用到 CSS 動畫

@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

現在的效果僅僅是5秒後,數字從 5 變成了 0,並沒有 5 => 4 => 3 => 2 => 1 這種階段變化。然後最重要的一步來了,加上以下自定義屬性

@property --t { 
    syntax: '<integer>';
    inherits: false;
    initial-value: 0;
}

對的,僅僅新增這一小段 CSS,效果就出來了

Kapture 2022-06-26 at 14.03.07

是不是很神奇?可以這麼理解,通過@property定義後,這個變數--t本身可以單獨設定動畫了,就像顏色變化一樣。

另外,使用計數器的好處是可以隨意更換型別,比如將上面的阿拉伯數字換成中文計數,只需要更換計數器型別就行了

完整型別可以參考:list-style-type
count-down::after{
    --t: 5;
    counter-reset: time var(--t);
    content: counter(time, cjk-decimal); /*中日韓十進位制數*/
    animation: count 5s forwards;
}

效果如下

Kapture 2022-06-26 at 14.14.20

是不是非常方便呢?

二、倒數計時的終點

上面的計數器最後的終點是“0”,顯然我們需要一些特定的提示,比如“Go~”

image-20220626143135177

如何改變最後一幀的狀態呢?這裡有兩種方式:

  1. 通過動畫覆蓋
  2. 通過計數器覆蓋

首先來看第一種方式,這個比較好理解,重新定義一個動畫,在倒數計時結束後,將最後一幀重置一下

@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

注意這裡動畫函式是step-end,為啥是這個呢?step-end也可寫作steps(1,end),你可以理解為在整個動畫只有兩種狀態,在執行過程中,都是初始狀態,只有到達最後一幀才改變狀態,下面是 MDN 的截圖

image-20220626145733201

下面來看第二種方式,通過自定義計數器來實現。原理其實和 JS 思維有些類似,當數字為 0 時,讓計數器指定一個特殊的值,具體實現如下

@counter-style stop {
    system: cyclic;
    symbols: "Go~";
    range: 0 0;
}

這裡簡單解釋一下,這裡有個range屬性,表示計數器的範圍,由於這裡只需要指定為 0,所以是區間0 0。然後是system,表示計算系統,這裡為cyclic,表示迴圈使用開發者提供的一套字元,字元由symbos定義。然後symbos表示計算符號,也就是具體展示的字元,這裡指定為Go~就行了。

這部分自定義計數器內容比較複雜,也比較新,有興趣的可以參考張鑫旭的這篇文章:CSS @counter-style規則詳細介紹

然後是應用

count-down::after{
      /**/
    counter-reset: time var(--t);
    content: counter(time, stop); /*自定義計數器*/
}

這樣也能達到相同的效果,實現也更加優雅

Kapture 2022-06-26 at 14.46.18

三、縮放和透明度變化

這兩個動畫其實是同時進行的,可以放在一個動畫裡

@keyframes shark {
    0%{
        opacity: 1;
        transform: scale(1);
    }
    
    50%{
        opacity: 0;
        transform: scale(0.4);
    }
}

然後設定動畫時長為 1s,迴圈 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

是不是稍微有些突兀?因為數字的變化是突然的,需要將數字的變化隱藏到透明度為 0 的時候,為了達到這種效果,只需要將閃爍動畫延遲 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

不過還有優化的空間。比如現在數字動畫有些太連貫了,如果希望數字出現後稍微停留一小會,或者說希望出現的慢一點,消失的快一點,如何處理呢?其實這比想象中的要容易許多,只需要改一下關鍵幀位置就行了,如下

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

同時,延遲的時間也需要改成 0.8 秒,效果如下

Kapture 2022-06-26 at 16.57.42

這樣就實現了文章開頭所示效果

下面重點來了~完整程式碼如下

@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);
    }
}

你也可以訪問線上例子:CSS count-down(codepen.io)或者CSS count-down(juejin.cn)

另外,demo 中還有個小彩蛋,點選可以重新執行動畫,實現方式如下

count-down:active::after{
    animation: none;
}

四、其他動畫效果

除了縮放效果,還可以有一些位移的動畫,比如這樣的

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

效果如下

Kapture 2022-06-26 at 17.07.00

是不是有點奇怪?動畫不夠連貫,一會向下一會向上,有沒有辦法消失和出現都是從上到下的呢?當然也是可以的,實現如下

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

    21%{
        opacity: 0;
        transform: translateY(-100px);
    }
}

這裡多加了一個非常“鄰近”的關鍵幀,表示在透明狀態下,“迅速”改變位移,這樣在數字出現時的動畫就感覺是從上到下的,整體更為流暢,效果如下

Kapture 2022-06-26 at 17.15.02

還可以調整一下前面的縮放效果,讓出來的時候更大,效果也更為震撼

@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

當然還有其他效果,比如旋轉,斜切等,這就需要發揮你的想象了~

五、總結和說明

以上就是本文的全部內容了,一個簡單的小動畫,你學會了嗎?下面總結一下實現要點:

  1. 複雜動畫可以分解成多個簡單的動畫
  2. 數字的變化可以通過多個標籤,改變位移實現
  3. CSS 計數器可以將數字變數渲染到頁面
  4. CSS @property 以將CSS變數設定動畫,就像顏色變化一樣
  5. CSS 計數器的好處是可以隨意更改型別,比如中文計數
  6. 倒數計時的終點預設是數字 0 ,可以通過另一個動畫重置最後一幀
  7. 可以通過自定義 CSS 計數器,讓某個計數符號渲染成指定字元
  8. 縮放和透明的變化是同時進行的,可以放在一個動畫裡
  9. 數字的變化需要注意安排在透明度為0的時候,不然數字變化很突兀
  10. 數字的出現和消失動畫可以新增一個鄰近的關鍵幀來快速歸位

文中用了一些比較新的屬性,比如 @property,還有自定義計數器,不過沒關係,文中也都提到了其他解決方案,動畫的整體思路是不變的,如何觀察和分解動畫,這個才是最重要的。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

歡迎關注微信公眾號: 前端偵探

相關文章