妙啊!動畫還可以這樣控制?

2022-05-24 12:01:53

今天,有在群裡看到這樣一個問題:有一個動畫,一開始靜止處於第一幀,只在使用者 hover 的時候執行動畫,在執行一次後停止,並且停留在最後一幀,使用 CSS 可以完成麼?

像是這樣:

一個非常有意思的問題,答案是可以的。我們抽取一下其中的關鍵點:

  1. 動畫只執行一次,未執行前處於第一幀,執行完後處於最後一幀
  2. 動畫通過 hover 驅動,只有使用者 hover 元素的時候,動畫才進行

animation-fill-mode 控制元素在各個階段的狀態

首先,動畫只執行一次,未執行前處於第一幀,執行完後處於最後一幀。

這個剛好利用 CSS 動畫的 animation-fill-mode: both 即可。

  1. animation-fill-mode: backwards:可以讓元素在動畫開始之前的樣式為動畫執行時的第一幀,動畫結束後的樣式則恢復為 CSS 規則設定的樣式
  2. animation-fill-mode: forwards:元素在動畫開始之前的樣式為 CSS 規則設定的樣式,而動畫結束後的樣式則表現為由執行期間遇到的最後一個關鍵幀計算值(也就是停在最後一幀)

而,animation-fill-mode: both 兼顧了上面兩種模式的特點,可以使得動畫開始前的樣式為動畫執行時的第一幀,動畫結束後停在最後一幀

反向利用 animation-play-state 實現 hover 觸發動畫行進

動畫通過 hover 驅動,只有使用者 hover 元素的時候,動畫才進行這一點,利用 animation-play-state 即可。

我們都知道,正常情況下,動畫應該是執行狀態,那如果我們將動畫的預設狀態設定為暫停,只有當滑鼠點選或者 hover 的時候,才設定其 animation-play-state: running,這樣就可以利用 hover 控制動畫的行進!

基於上述兩點,我們來實現一個有意思的打字動畫,做到動畫只觸發單次,並且只有 hover 的時候動畫會執行。

<p>Hover Me - You are a pig!</p>
p {
    position: relative;
    font-family: monospace;
    width: 26ch;
    animation: typing 3s steps(15, end);
    animation-fill-mode: both;
    animation-play-state: paused;
    overflow: hidden;
}
p:hover  {
    animation-play-state: running;
}
p::before {
    position: absolute;
    content: "";
    width: 4px;
    top: 0;
    bottom: 0;
    right: 0;
    animation: blink .8s linear infinite;
}
@keyframes blink {
    0%, 50% {
        border-right: 4px solid transparent;
    }
    50%, 100% {
        border-right: 4px solid #000;
    }
}
@keyframes typing {
    from {
        width: 11ch;
    }
    to {
        width: 26ch;
    }
}

預設情況下,展示這樣一個介面:

接下來,我們把滑鼠放上去,看看會發生什麼:

有意思,完美的實現了上面說的要求 -- 動畫通過 hover 驅動,只有使用者 hover 元素的時候,動畫才進行

當然,這裡還運用了幾個小技巧,一併解釋下:

  1. 打字動畫運用了逐幀動畫,而不是補間動畫,主要利用了 CSS 動畫的 step-timing-function 步驟緩動函數,也就是程式碼中的 steps(15, end)
  2. ch 是 CSS 當中的一個相對單位,這一單位代表元素所用字型 font 中 「0」 這一字形的寬度
  3. font-family: monospace 表示等寬字型,每個字元佔據的寬度是一樣,因為我們使用了 26ch 來充當 <p> 元素的寬度,而 Hover Me - You are a pig 這一段文字算上空格剛好 26 個字元,26ch 剛好表示這一段文字的長度
  4. 一開始展示的文字 Hover me - 算上空格是 11ch 寬度,而最後整個文字展示完需要 26ch 的寬度,中間需要經過 15 步的逐幀動畫,這裡的元素剛好和程式碼中的一一對應上

藉助上面 4 步及搭配我們上文介紹的 animation-fill-mode: bothanimation-play-state: paused 的應用,我們就完美的實現了這樣一個非常有意思的打字動畫。

完整的程式碼,你可以戳這裡 CodePen Demo -- running once animation by hover

如果你想對 CSS 動畫有更深入細緻的瞭解,可以翻看我的這篇文章,對動畫的每一個屬性都有著十分細緻的講解:深入淺出 CSS 動畫

最後

OK,本文到此結束,希望本文對你有所幫助