開發一個抽獎大轉盤 Lucky-spin

大桔子發表於2022-06-21
最近有一個需求要開發一個抽獎大轉盤類的一個頁面,本來想著這種東西肯定有現成的,但找了一圈後發現基本上都與我們的設計圖有些出入,如果直接使用現成的包估計肯定是驗收不通過的

開源專案的佈局一般都是這樣的(轉盤內的文字正對圓心),推薦一個比較熱門的開源專案,支援多端,多種遊戲方式(大轉盤、九宮格、老虎機)

image.png

而我們的設計圖卻是設計成這樣的(文字與半徑平行)

image.png

無奈現成的包的不能使用,故準備把開源專案 down 下來修改一下使之符合我們的業務場景要求

stackblitz效果預覽

下面總結了一些實現思路:分為 canvas 版本和 div+css 版本

canvas 思路實現

  1. canvas 轉盤繪製,包括每片扇區上的文字及圖片的繪製(其實就這一點需要更改)
  2. 遞迴 requestAnimationFrame 然後不斷改變繪製起始角度(canvas繪製的初始角度)使轉盤動起來
  3. 使用緩動演算法,使轉盤轉動更加自然:轉盤初始階段緩慢加速 -> 勻速 -> 減速 最終停在指定扇區(中獎結果肯定是 api 返回的,前端需要控制最後要停在指定位置上,按照 lucky-canvas 上的說法就是刻舟求劍)

下面分別說一下注意點:

  1. canvas 繪製需要注意,起始角度是從 三點鐘開始的,繪製一圈是 2 * Math.PI,為了讓起始位置落在第一個扇區上需要逆時針旋轉一定的角度(按我的需求是有8個扇區,也就是需要旋轉 2 * Math.PI / 8 * 2 ),開發時建議將弧度(不知道此處用弧度表示對不對?)轉換成角度,這樣運算過程更加便捷,也會減少無限不迴圈小數產生的誤差

    /**
     * 轉換為運算角度
     * @param { number } deg 數學角度
     * @return { number } canvas 畫圓角度
     */
    const getAngle(deg: number): number => (Math.PI / 180) * deg
  2. requestAnimationFrame 沒什麼好說的,現在瀏覽相容性也不是什麼問題了
  3. 緩動演算法:其實就是根據一些列變數,算出一個值(動畫的變化量),具體參考張鑫旭大大的tween.js的簡單實現
  4. 轉盤停止停在的扇區,根據 api 返回的中獎結果得出扇區索引 index 即:const prizeDeg = 360 - index * (360 / 8) + initDeg; (initDeg即最開始旋轉的初始角度)
在需求完成之後,我發現不用 canvas,用 div 也能很方便的實現這種效果

div 實現

除了第一步轉盤繪製,其他兩步思路大致相同

  1. 用 div + css 代替 canvas ,主要用到了 transform: rotate(x) skew(y),需要注意的一點是,旋轉扭曲之後 div 的子元素也會跟著旋轉扭曲,我本來以為這樣寫就行了 transform: rotate(-x) skew(-y),但殊不知 transform 變換是有順序的要這樣寫 transform: skew(-y) rotate(-x) 參考
  2. 用了 div 之後,旋轉就不是不斷地重繪 canvas 了,只需要不斷地改變父集元素的 transform: rotate(),相比 canvas 要簡單了許多

程式碼參考 https://github.com/hugeorange...

  1. 核心程式碼採用 ts 開發(不依賴框架)
  2. 考慮到我的這個程式碼滿足的場景比較小眾並沒有封裝成 npm 包
  3. 有同樣場景需求的朋友可以直接拷貝程式碼

相關文章