React、Vue實現購物車小球拋物線效果
大家對下面這個動畫效果想必都並不陌生了:
點選增加商品的按鈕,會有一個小球做拋物線掉入購物車中,對這個動畫一直很感興趣,所以稍稍探究了一下
拋物線軌跡方程
實現此動畫效果的難點在於,如何控制拋物小球在每一幀的座標,因為只要控制好小球的每幀座標,則小球的運動軌跡也就確定了。
一般情況下,這種拋物小球的運動軌跡都是拋物線,或者近似拋物線(例如本文開頭的那個餓了麼圖片,小球運動感覺並不是真正的拋物線運動),道理其實都是一樣的,這裡先設定小球就是拋物線運動。
拋物線的一般方程為:
y=ax^2+bx+c
其中, x y
為座標,所以此方程中只要確定 a b c
這 3個引數的值,就能得到一條拋物線軌跡方程式。
3個引數需要 3個條件才能完全求解,也就是說只要知道拋物線軌跡上隨意 3個點的座標即可。
不過,因為這裡的座標軸是我們自己虛構的,我們是在解決實際問題,而不是解決數學問題,所以不需要太複雜,怎麼簡單怎麼來,如果假定拋物線軌跡的起始點座標為 (0,0)
,那麼根據上述拋物線方程可以得到 c=0
,拋物線方程就變成了:
y=ax^2+bx
另外, 引數 a
實際上是一個可控的常量,通過設定 a
的值來確定拋物線在每一點處的曲率,所以 a
可以看做是已知的
所以,只需要求出 b
即可:
b=(y+ ax^2) / x
只要再知道拋物線軌跡上任意一點座標即可,而小球所在的拋物線軌跡上,除了起始點座標外,還有一點座標肯定是已知的,那就是拋物線末尾座標,即購物車元素的座標,到此,拋物線方程就搞定了。
小球拋物線動畫的關鍵就是這個拋物線軌跡方程,只要得到了此方程公式,剩下的就很好解決了,關於拋物線方程更多詳細可見 張鑫旭
在 React中實現拋物小球動畫
搞定了拋物線方程之後,接下來就是如何處理拋物小球的問題了。
每一次點選增加按鈕,就會出現一個沿著拋物線軌跡拋向購物車的小球,每個小球因為出現的座標以及時間不同,拋物線軌跡並不一定完全重合,所以需要對這些小球進行單獨控制。
可以將每個拋物小球封裝成一個元件,每多處一個小球就相當於是在頁面上多增加了一個此元件,需要給元件傳入的關鍵 props
資料有如下幾個。
- 小球的起始座標
根據上述關於拋物線方程的推導可知,想要確定此方程,需要小球的起始座標以及末尾座標,設定其實座標如下表示:
// 起始點座標
position = {x0, y0}
// 末尾點座標
target = {x1, y1}
曲率
curvature
在上述推導的拋物線方程中,引數a
是可控的,由我們自己來定,通過這個引數控制拋物線在各點的曲率速度
speed
小球的運動速度也是可以控制的,通過此引數來控制小球的運動速度清除函式以及小球
id
小球在進入到購物車後會自動消失,所以這裡需要處理一下,頁面中可能同時存在很多個小球,根據id
來確定到底哪個小球需要清除掉。
根據以上推斷,小球元件可以是下面這個樣子:
<Ball
position={position}
target={target}
id={id}
curvature={curvature}
speed={speed}
changeFlyBallCount={changeFlyBallCount}/>
設定一個 state
作為頁面上的小球元件資料來源:
balls: []
balls
陣列中的每一項代表著一個需要顯示的小球,當需要在頁面上顯示小球時,則向 balls
中追加一個小球資料項,此項中包含小球的起始點座標以及 一個獨一無二的小球id
(可以使用時間戳代替),如下:
balls: [
{id, position: {x, y}}
]
當小球進入到購物車中需要被清除掉時,只需要根據 id
查找出小球的資料項,然後將此項從 balls
中刪掉即可,如下:
for (let i=0; i<balls.length; i++) {
if(balls[i].id === id) {
// 清除小球
balls.splice(i, 1)
break
}
}
效果如下:
在 Vue中實現拋物小球動畫
上述 react
實現的動畫,如果應用在實際專案中,有的時候可能會感覺拋物線有點彆扭,我們可能是希望拋物線軌跡與小球丟擲點的水平方向相切,即小球丟擲點座標是拋物線的頂點,看起來會比較順滑,類似下面這種:
如果是這樣的話,那麼拋物線方程就要重新設定一下了。
由於小球丟擲點為頂點,頂點已知,那麼可根據拋物線頂點式進行推導:
// 拋物線頂點式,(h,k)為頂點座標
y=a(x-h)^2+k(a≠0)
設定小球起始點座標為 (0, 0)
,則只需要再已知一點座標,得到引數 a
即可,而這一點座標顯而易見可以是 拋物線末尾點座標(x1,y1)
上式中的引數 a
為:
a = y1/(x1^2)
拋物線方程搞定。
這裡可以使用 Vue
動畫鉤子函式來完成小球拋物動畫,其餘的與上述 react
大體相同,設定一個數組資料來源 balls
,當需要顯示小球時,就向裡面追加一個數據項,有一點需要注意,由於Vue
動畫本身的邏輯,當需要清除小球的時候,就不能像上面 react
實現的那樣直接將對應的資料項從 balls
數組裡面刪除了,可以多設定一個標誌變數 show
,當需要顯示的時候,將 show
設為 true
,當需要小球消失時,將對應的小球資料項的 show
設為 false
,如下:
// x,y為小球起始點座標
balls: [
{id, show, x, y}
]
每個小球都看作是一個 transiton
動畫,為每個動畫顯式宣告 appear
和afterAppear
這兩個鉤子函式,在 appear
函式中實現小球運動動畫,在 afterAppear
函式中清除小球。
效果如下:
動畫的主體部分大概就是這樣了,至於其他的一些小動畫,例如小球掉入購物車時,購物車發生抖動等就很簡單了,不多說。
具體程式碼放在 Github上了,有興趣可以看一下,順便 star
哦~