微信搜尋 【大遷世界】, 我會第一時間和你分享前端行業趨勢,學習途徑等等。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。
我們每天都在網上摸魚,作為前端開發人員,網站上微妙的細節變化透過比別人會更關注。我一直注意到的一件事是網站上的動畫的流暢性。動畫對於使用者體驗來說是非常好的,有時我們可以一些有趣的動畫來留住使用者。
建立高階動畫聽起來是一個很難的話題,但好訊息是,在CSS中,可以將多個簡單的動畫相互疊加,以建立一個更復雜的動畫
在這節課中,我們會學習如下幾點:
- 什麼是貝塞爾曲線,以及如何用一行CSS來建立一個 "複雜"的動畫
- 如何將動畫相互疊加以建立一個高階動畫
- 如何透過應用上面學到的兩點來建立一個雲霄飛車動畫
什麼是貝塞爾曲線
CSS中的 cubic-bezier 函式是一個緩動函式,可以讓我們完全控制動畫在時間上的表現。下面是官方的定義:
貝塞爾緩動函式是一種由四個實數定義的緩和函式,指定了貝塞爾曲線的兩個控制點P1
和P2
,其端點P0
和P3
分別固定在(0, 0
)和(1, 1)
。P1
和P2
的x
座標被限制在[0, 1]
範圍內。
什麼是緩動函式?
線性曲線
想象兩個點P0
和P1
,其中P0
是動畫的起點,P1
是結束點。現在想象另一個點在兩點之間線性移動,如下所示
這就是所謂的線性曲線,也是最簡單的動畫。
二次貝塞爾曲線
如下圖所示,有三個點。P0、P1和P2。我們想讓動畫從P0
移動到P2
。在這種情況下,P1
是一個控制點,控制動畫的曲線。
二次方貝塞爾概念:
- 在P0和P1之間以及P1和P2之間(用灰線表示)連線虛線
- 點Q0沿著P0和P1之間的直線移動。同時,點Q1沿著P1和P2之間的直線移動
- 在Q0和Q1之間連線一條虛線(用綠線表示)
- 在Q0和Q1開始移動的同時,點B開始沿著綠線移動,B點所走的路徑就是動畫路徑
請注意,Q0、Q1和B不以相同的速度移動。它們都必須在同一時間開始,並在同一時間完成它們的路徑。因此,每一個點都是根據它所移動的線長以適當的速度移動的。
三次貝塞爾曲線
三次貝塞爾曲線由4個點組成。P0, P1, P2和P3。動畫開始於P0,結束於P3。P1和P2是我們的控制點。
三次貝賽爾的工作原理如下:
- 在(P0, P1)、(P1, P2)和(P2, P3)之間連線虛線,由灰線表示
- 點Q0、Q1和Q2分別沿直線(P0,P1)、(P1,P2)和(P2,P3)移動
- 在(Q0, Q1)和(Q1, Q2)之間連線虛線,它們由綠線表示。
- 點R0和R1分別沿直線(Q0, Q1)和(Q1, Q2)移動
- 連線R0和R1之間的線(用藍線表示)
- 最後,B點沿著R0和R1之間的連線線移動,B點所走的路徑就是動畫路徑
如果你想更好地瞭解三次體貝塞爾的工作原理,建議你看看這個desmos連結。玩玩控制點,看看動畫如何隨時間變化。(注意,連結中的動畫是由黑線表示的)。
疊加動畫
有很多步驟的大動畫可以被分解成多個小動畫。在 css 中,透過新增animation-delay
屬性來實現這一點。計算延遲很簡單,把你要計算動畫延遲的那個動畫之前的所有動畫的時間加起來。
例如:
animation: movePointLeft 4s linear forwards, movePointDown 3s linear forwards;
這裡,我們有兩個動畫,movePointLeft
和movePointDown
。movePointLeft
的動畫延遲是零,因為它是我們想先執行的動畫。movePointDown
的動畫延遲是4
秒,因為movePointLeft
將在這段時間後完成。
因此,animation-delay
屬性:
animation-delay: 0s, 4s;
注意,如果有兩個或更多的動畫同時開始,它們的動畫延遲將是一樣的。此外,當你計算即將開始的動畫的延遲時,把它們視為一個動。例如 :
animation: x 4s linear forwards, y 4s linear forwards, jump 2s linear forwards;
假設x
和y
同時開始。在這種情況下,x
和y
的動畫延遲都將為零,而 jump
動畫的延遲將為4秒(而不是8
秒!)。
animation-delay: 0s, 0s, 4s;
建立雲霄飛車
掌握了上面的知識,是時候應用一下了。
瞭解動畫
雲霄飛車路徑由三部分組成:
- 滑動部分
- 迴圈部分
- 還會有一些動畫,在上面的兩個動畫之間創造水平空間
我們將首先建立一個簡單的球,作為我們雲霄飛車的 "車"。
hmtl 部分:
<div id="the-cart" class="cart"></div>
css 部分:
.cart {
background-color: rgb(100, 210, 128);
height: 50px;
width: 50px;
border: 1px solid black;
border-radius: 50px;
position: absolute;
left: 10vw;
top: 30vh;
}
滑動部分
建立小球滑動的部分可以用cubic-bezier
函式來完成! 這個動畫是由2個動畫組成的,一個是沿x
軸的動畫,另一個是沿y
軸的動畫。X
軸動畫是一個沿X
軸的普通線性動畫。它的關鍵幀如下:
@keyframes x {
to {
left: 40vw;
}
將其新增到球路徑的 animation
屬性中,如下所示
animation: x 4s linear forwards
y軸動畫是我們將使用cubic-bezier函式的部分。首先定義動畫的關鍵幀。我們希望起始點和結束點之間的差異很小,以至於球達到的高度幾乎相同。
@keyframes y {
to {
top: 29.99vh;
}
}}
現在讓我們來思考一下cubic-bezier函式。我們希望我們的路徑先向右緩慢移動,然後當它滑動時,它應該走得更快。
向右緩慢移動意味著
$P1$
將沿x
軸移動。所以,我們知道它是在(V,0)。- 我們需要選擇一個合適的V,使我們的動畫緩慢地向右移動,但又不能太多,以免佔用整個空間。在這種情況下,我發現
0.55
最適合。
- 我們需要選擇一個合適的V,使我們的動畫緩慢地向右移動,但又不能太多,以免佔用整個空間。在這種情況下,我發現
為了達到滑動效果,我們需要將
P2
向Y
軸下移(負值),所以P2=(X,-Y)
。- Y應該是一個大值。在這種情況下,我選擇
Y=5000
。 - 為了得到
X
,我們知道我們的動畫速度在滑動時應該更快,在再次上升時應該更慢。所以,X越接近於零,動畫在滑動時就越陡峭。在這種情況下,讓X = 0.8
。
- Y應該是一個大值。在這種情況下,我選擇
現在,我們得到了一個cubic-bezier函式:
cubic-bezier(0.55, 0, 0.2, -800).
為動畫屬性新增關鍵幀:
animation: x 4s linear forwards,
y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards;
這是我們動畫的第一部分,所以動畫延遲為零。我們應該新增一個animation-delay
屬性,因為從下面的動畫開始,動畫的開始時間將與第一個動畫不同。
animation-delay: 0s, 0s;
地址:https://codepen.io/smashingmag/pen/VwxXBQb
新增水平空間
在做迴圈之前,球應該沿著X
軸移動一小會兒,所以兩個動畫之間有空間。
定義關鍵幀
@keyframes x2 {
to {
left: 50vw;
}
}
把它新增到 animation
屬性中:
animation: x 4s linear forwards,
y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards, x2 0.5s linear forwards;
這個動畫應該在滑動動畫之後開始,而滑動動畫需要4秒,因此,動畫延遲將是4秒。
animation-delay: 0s, 0s, 4s;
地址:https://codepen.io/smashingmag/pen/dyemExY
迴圈部分
要在CSS中建立一個圓(迴圈),我們需要把圓移到迴圈的中心,然後從那裡開始做動畫。圓的半徑是100px
,所以我們把圓的位置改為top: 20vh
(30
是期望的半徑(這裡是10vh
))。然而,這需要在滑動動畫完成後發生,所以我們將建立另一個持續時間為0秒
的動畫,並新增一個合適的動畫延遲。
關鍵幀:
@keyframes pointOfCircle {
to {
top: 20vh;
}
}
新增到 animation 動畫中:
animation: x 4s linear forwards,
y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards, x2 0.5s linear forwards,
pointOfCircle 0s linear forwards;
新增動畫延遲, 4.5s
:
animation-delay: 0s, 0s, 4s, 4.5s;
迴圈本身
建立一個迴圈動畫:
- 建立一個關鍵幀,將球移回原來的位置,然後旋轉球。
@keyframes loop {
from {
transform: rotate(0deg) translateY(10vh) rotate(0deg);
}
to {
transform: rotate(-360deg) translateY(10vh) rotate(360deg);
}
}
新增到 animation 中:
animation: x 4s linear forwards,
y 4s cubic-bezier(0.55, 0, 0.2, -5000) forwards, x2 0.5s linear forwards,
pointOfCircle 0s linear forwards, loop 3s linear forwards;
新增動畫延遲,這裡是4.5s
:
animation-delay: 0s, 0s, 4s, 4.5s, 4.5s;
地址:https://codepen.io/smashingmag/pen/mdLxZdR
新增水平空間
快完成了,最後 只需要在動畫之後沿著x
軸移動球,這樣球就不會像上圖中那樣在迴圈之後完全停止。
關鍵幀:
@keyframes x3 {
to {
left: 70vw;
}
}
新增到 animation 中:
animation: x 4s linear forwards,
y 4s cubic-bezier(0.55, 0, 0.2, -800) forwards, x2 0.5s linear forwards,
pointOfCircle 0s linear forwards, loop 3s linear forwards,
x3 2s linear forwards;
加上適當的延遲,這裡是7.5s
:
animation-delay: 0s, 0s, 4s, 4.5s, 4.5s, 7.5s;
地址:https://codepen.io/smashingmag/pen/wvjmLKp
總結
在本節中,我們介紹瞭如何結合多個關鍵幀來建立一個複雜的動畫路徑。我們還介紹了貝塞爾以及如何使用它們來建立你自己的緩動函式。建議大家自己多多動手,才能更好的掌握 css 動畫。
來源:https://www.smashingmagazine.com/2022/10/advanced-animations-...
編輯中可能存在的bug沒法實時知道,事後為了解決這些bug,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
交流
有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。