過渡與動畫 - 緩動效果&基於貝塞爾曲線的調速函數

難題

給過渡和動畫加上緩動效果是一種常見的手法(好比具備回彈效果的過渡過程)是一種流行的表現手法,可讓界面顯得更加生動和真實:在現實世界中,物體A點到B點每每也是不徹底勻速的css

以純技術的角度來看,回彈效果是指當一個過渡達到最終值時,往回到一點,而後再次回到最終值,如此往復一次或者屢次,並逐漸收斂,最終穩定在最終值。有至關的多JavaScript類庫能夠建立動畫,且內置回彈效果等其餘緩動效果。可是眼下,咱們其實已經不須要藉助腳原本實現過渡和動畫了。不過,在CSS中實現回彈效果的最佳方式是什麼呢?html

彈跳效果

彈跳動畫

咱們的第一感受可能就是使用css動畫,而且設置以下關鍵幀:函數

@keyframes bounce{
    60%,80%,to{transform:translateY(350px);}
    70%{transform:translateY(250px);}
    90%{transform:translateY(300px);}
}

相信咱們都作過這樣的事,可是咱們跑一遍這個動畫,會發現它顯示的及其不真實,主要緣由在於,每當這個小球方向改變時,她得移動過程都是持續加速的,這看起來很不天然。緣由其實就是由於它的調速函數在關鍵幀的銜接都是同樣的工具

全部的過渡和動畫之間都是跟一條曲線有關的,這條曲線指定了動畫過程在整段時間中是如何推動的學習

若是不指定調速函數,就是獲得一個默認值。可是這個默認值並非咱們想象中的勻速效果,而是:優化

默認值

注意,當時間進行到一半時,這個過渡已經推動到80%.動畫

說到調速函數,咱們很天然聯繫到了css內置的緩動曲線和貝塞爾曲線。spa

不管是在animation/transition簡寫屬性中,仍是在animation-timing-function/transition-timing-function展開屬性中,你均可以把這個默認的調速函數顯示指定ease關鍵字。除了ease外,還有四種內置的緩動曲線,你能夠藉助他們來改變更畫的推動方式.net

ease-out

ease-in

ease-in-out

linear

從上面四個圖中,咱們很直觀的看出,ease-outease-in的反向版本。而這一對組合正是實現回彈效果所須要的:每當小球的運動方向相反時,咱們但願調速函數也是相反的。咱們但願小球下落是加速的(ease-out),而彈起向上是減速的(ease-in):code

@keyframes bounce{
    60%,80%,to{
        transform:translateY(400px);
        animation-timing-function:ease-out;
    }
    70%{transform:translateY(300px);}
    90%{transform:translateY(360px);}
}
.ball{
    animation:bounce 3s ease-in;
}

雖然咱們改動不大,可是已經發現回彈效果變得真實起來。不過顯然這五種內置的緩動曲線是不夠用的,假如咱們這個回彈效果是用來模擬自由落體的,那麼咱們須要一個更高的加速度和ease的反向版本,又如何獲得呢?

其實全部的這五種曲線都是經過(三次)貝塞爾曲線來指定的,而CSS的調速函數都是只有一個片斷的貝塞爾曲線,每一個函數也只有兩個控制錨點,CSS就提供了一個cubic-bezier()函數,容許咱們指定自定義調速函數。他接受四個參數,分別是兩個控制錨點的座標值,
cubic-bezier(x1,y1,x2,y2),曲線的兩個端點固定在(0,0)和(1,1)之間,前者是整個過渡的起點(時間進度0%,動畫進度0%)然後者是整個過渡的終點(時間進度100%,動畫進度100%)。

舉例來講,ease等同於cubic-bezier(.25,.1,.25,1),所以它的反向版本就是cubic-bezier(.1,.25,1,.25)

@keyframes bounce{
    60%,80%,to{
        transform:translateY(400px);
        animation-timing-function:ease;
    }
    70%{
        transform:translateY(300PX);
    }
    90%{
        transform:translateY(160px);
    }
}

.ball{
    animation:bounce 3s cubic-bezier(.1,.25,1,.25);
}

codepen中查看效果

See the Pen css-animation-easing by okaychen (@okaychen) on CodePen.


咱們能夠藉助cubic-bezier.com的圖形化工具,進行反覆嘗試和優化,從而進一步改寫這個回彈動畫.

最後

通過以上這些知識的學習儲備和練習,相信咱們已經能夠作出很棒的彈跳動畫了.
咱們在文章開始放了一個小球彈跳的gif圖效果,那麼就讓咱們真真正正的動手來寫一下吧!

codepen中查看效果

See the Pen css-animation-easing-practice by okaychen (@okaychen) on CodePen.


彈性過渡

假設咱們有一個文本輸入框,每當它被聚焦時,都須要展現一個提示框
咱們有以下結構:

<label>
        Your username:<input id="username" />
        <span class="callout">Only letters,numbers,usrescore(_) and hyphens (-) allowed!</span>
    </label>

每當用戶聚焦這個文本輸入框時,都會有一個半秒鐘的過渡,可能咱們會完成這樣的代碼

input:not(:focus) + .callout{
    transform:scale(0);
}
.callout{
    transition:.5s transform;
    transition-origin:1.4em -.4em;
}

這個過渡沒有任何問題,可是咱們但願它在結尾時能在誇張一點話,顯得更加天然生動,咱們可能會把這個過渡改成一個動畫,而後用上面提到的緩動曲線

@keyframes elastic-grow{
    from{transform:scale(0);}
    70% {
        transform:scale(1.1);
        animation-timing-function:cubic-bezier(.1,.25,1,.25);   /*反向的ease*/
    }
}

input:not(:focus) + .callout{ transform:scale(0); }

input:focus + .callout{ animation:elastic-grow .5s; }

.callout{ transform-origin:1.4em -.4em; }

添加了這個動畫以後,確實發揮了做用。不過這裏咱們其實只是須要一個過渡而已,而咱們本質上卻使用了一個動畫,顯得有些大材小用,有一種殺雞用牛刀的感受,咱們如何只用過渡完成這個效果呢?

這裏咱們就用到了上面提及的調速函數cubic-bezier(),在這個例子中,咱們但願調速函數先到達110%的程度(至關於scale(1.1)),而後在過渡回100%,咱們把控制錨點向上移,

cubic-bezier(.25,.1,.3,1.5)

這個自定義調速函數在垂直座標上已經超出0~1的區間,最終又回到1,在70%的時間點到達了110%的變形程度的高峯,而後繼續用剩下30%的時間回到它的最終值

整個過渡的推動,很是接近前面的動畫方案,但他僅須要一行代碼就能夠實現整個效果

input:not(:focus) + .callout{ transform:scale(0) }

.callout{
    transform-origin:1.4em -.4em;
    transition:.5s cubic-bezier(.25,.1,.3,1.5);
}

cubic-bezier(.25,.1,.3,1.5)

but,wait...當提示框收縮時,左下角出現的是什麼?其實,當咱們把焦點從輸入框切出去的時候,所觸發的過渡會以scale(1)做爲起始值,並以scale(0)做爲最終值,這個過渡仍然會在350ms後到達110%的變形程度。只不過在這裏,110%的變形程度的解析結果並非scale(1.1),而是scale(-0.1)

咱們能夠定義關閉狀態的css規則(假如咱們指定普通的ease調速函數)把當前的調速函數覆蓋掉

input:not(:focus) + .callout{
    transform:scale(0);
    transition-timing-function:ease;   /*覆蓋cubic-bezier*/
}

.callout{
    transform-origin:1.4em -.4em;
    transition:.5s cubic-bezier(.25,.1,.3,1.5);
}

再試一試,發現已經關閉提示框已經恢復到咱們設置cubic-bezier()以前的樣子了,

可是其實咱們仔細觀察發現另外一個問題:提示框的關閉動做明顯要遲鈍一些。咱們細細想來發現,在提示框展開過程當中,當時間爲50%(250ms)時,它就已經到達100%的尺寸效果了。可是在收縮過程當中,從0%~100%的變化會花費咱們爲過渡所指定的素有時間(500ms),所以感受會慢上通常

而後咱們會想到同時覆蓋過渡的持續時間:能夠用transition-duration這一屬性,也能夠用transition這個簡寫屬性來覆蓋全部值,若是選擇後者的話就不須要指定ease了,由於他原本就是transition的初始值:

input:not(:focus) + .callout{
    transform:scale(0);
    transition:.25s;
}

.callout{
    transform-origin:1.4em -.4em;
    transition:.5s cubic-bezier(.25,.1,.3,1.5);
}

codepen中查看效果

See the Pen css-animation-task by okaychen (@okaychen) on CodePen.

最後

雖然彈性過渡在不少過渡中均可以收到不錯的效果,可是某些時候他產生的效果可能至關糟糕。典型的反面案例出如今對顏色屬性的彈性過渡中。儘管顏色發生彈性過渡可能很是有趣,但這種效果在UI場景中一般是不合適的.

爲了不不當心對顏色設置了彈性過渡,能夠嘗試把過渡的做用範圍限制在某幾種特定的屬性上,transition不指定時,transition-property就會獲得它的初始值:all,這意味着只要是過渡的屬性都會參與過渡。咱們能夠在transition中設置transform

input:not(:focus){
    transform:scale(0);
    transition:.25s transform;
}

.callout{
    transition-origin:1.4em -.4em;
    transform:.5s cubic-bezier(.25,.1,.3,1.5) transform;
}

參考資料

相關文章
相關標籤/搜索