過渡與動畫 - 逐幀動畫&steps調速函數


寫在前面

上一篇中我們熟悉五種內置的緩動曲線和(三次)貝塞爾曲線,並且基於此完成了緩動效果.

但是如果我們想要實現逐幀動畫,基於貝塞爾曲線的調速函數就顯得有些無能為力了,因為我們並不需要幀與幀之間的過渡狀態,就像上篇中所看到的,所有基於貝塞爾曲線的調速函數都會在關鍵幀之間進行插值運算,從而產生平滑的過渡效果。

這個特性顯然很棒,平滑的效果確實是我們使用css過渡和動畫所追求的。

但是在逐幀動畫的場景下,這種平滑的特性恰恰毀掉了我們想要實現的逐幀動畫的效果.

逐幀動畫

我們經常會看到一段卡通影片、一個復雜進度的提示框、一個小loading,
我們不會單純的選擇一張GIF動畫勝任,因為它的局限性和短板表現的很明顯.

  • GIF圖片所能使用的顏色數量被限制在256色
  • GIF不具有Alpha透明的特性,
  • GIF動畫一旦生成,參數就固定在文件內部,只能通過圖像處理軟件去重新生成.
    在某些場景下,基於圖片的逐幀動畫成了不錯的選擇。

steps()調速函數

寫在前面中提到,我們不能基於貝塞爾曲線的調速函數完成我們所需要的逐幀動畫,那么采用什么調速函數呢?

對,答案就是steps()調速函數,與貝塞爾曲線迥然不同的是,steps()會根據你指定的步進數量,把動畫分為很多幀,而且整個動畫會在幀與幀之間硬切,不會像貝塞爾曲線那樣做插值處理。

對比step(8)、linear以及默認ease的差異

通過上圖我們可以很明顯看出steps(8)、linear和ease的區別.

其實這種硬切效果是我們極力避免的,因此我們也很少聽到關於steps()的討論。在CSS調速函數的世界里,基於貝塞爾曲線的調速函數就像是被人追捧的白天鵝,而steps()則是旁人唯恐不及的丑小鴨。

其實無所謂好與不好,更多的是適合與不適合,我們都崇拜的貝塞爾曲線在像小"loading"這樣的逐幀動畫中失敗了,而steps()卻展示出我們想要的效果.

這個想法最初是Simurai在他的博客中推出http://simurai.com/blog/2012/12/03/step-animation,他使用steps()實現拼合圖片的動畫效果.讓人印象深刻

codepen中查看效果

See the Pen Steps Animation by simurai (@simurai) on CodePen.

ch單位 - css值與單位第三版

有時候,我們希望一段為本字符逐個顯示,模擬出一種打字的效果。這種效果在技術類網站中尤為常見,用等寬字體可以營造出一種終端命令行的感覺.

<h1>CSS is amazing!</h1>
@keyframes typing{
    from{width:0}
}
h1{
    width:7.7em;
    white-space:nowrap;
    overflow:hidden;
    animation:typing 8s;
}

我們想要模擬出一種打字效果,但是

  • 整個動畫是平滑連貫的,而不是逐字顯示
  • 目前我們已經使用em指定寬度是7.7,雖然他比像素單位好一些,但是仍然不夠理想,這個寬度為什么是7.7em.

我們很自然的想到了使用steps()來修復第一個問題,但是不幸的是,我們所需要的步進數量是由字符的數量來決定的

CSS值與單位(第三版)規范引入了一個新的單位,表示"0"字形的寬度。大多數場景下,我們不必關心"0"字形的寬度到底有多寬,因為在等寬字體中,"0"字形的寬度和其他所有字形的寬度是一樣的。因此,我們如果使用ch單位來表示h1的寬度,那取值實際上就是字符的數量:在上面的例子中就是15

@keyframes typing{
    from{ width:0 }
}
@keyframes caret{
    50%{ border-color:transparent }
}
h1{
    width:15ch;
    overflow:hidden;
    white-space:nowrap;
    border-right:0.5em solid;
    animation:typing 6s steps(15),caret 1s steps(1) infinite;
}

但是我們還是有些疑問:

  • 這樣的代碼是不易維護的,當更新標題的時候,我們總是需要根據字符的數量來指定不同的寬度樣式和steps()函數,這時候正是JavaScript的用武之地
function $$(selector,context){
    context = context||document;
    var elements = context.querySelector(selector);
    return Array.prototype.slice.call(elements);
}
$$('h1').forEach(function(h1){
    var len = h1.textContent.length,s = h1.style;

    s.width = len + 'ch';
    s.animationTimingFunction = "steps(" + len + "),steps(1)"
})
  • 如果瀏覽器不支持ch單位,我們該怎么辦?這時候就需要實現樣式的回退,如果不希望字體出現異常,會選擇補一行em作為單位的回退樣式

寫在最后

這一篇主要基於steps()函數和ch單位,詳細的比較了steps()調速函數和基於貝塞爾曲線調速函數的區別,雖然steps()調速函數像是旁人唯恐不及的丑小鴨,但是它亦有其獨特的魅力。

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM