1. 程式人生 > >React、Vue實現購物車小球拋物線效果

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(a0)

設定小球起始點座標為 (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動畫,為每個動畫顯式宣告 appearafterAppear這兩個鉤子函式,在 appear函式中實現小球運動動畫,在 afterAppear函式中清除小球。

效果如下:

這裡寫圖片描述

動畫的主體部分大概就是這樣了,至於其他的一些小動畫,例如小球掉入購物車時,購物車發生抖動等就很簡單了,不多說。
具體程式碼放在 Github上了,有興趣可以看一下,順便 star哦~