從零開始完成 React Native 抽獎

從零開始完成React Native 九宮格抽獎

本文將介紹從零開始實現一個 React Native 版本的九宮格抽獎轉盤,先看最終效果圖html

也能夠直接使用react-native-super-lottery組件開發抽獎功能。react

1、佈局

佈局很簡單,咱們能夠採用flex 3行佈局,也能夠單行、配合flex-wrap子控件自動折行實現。直接上代碼git

const LotteryStyle = StyleSheet.create({
  container: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center',
  }
});

const img_width = 100; // 圖片的寬度
const img_height = 80; // 圖片的高度

// 觀察上圖的轉盤,會發現存在三種類型的宮格
// 當前被高亮的宮格(增長了蒙層效果) , 正常的功能,以及正中心能夠被點擊的宮格
// 在真實的狀況裏,就須要根據 item、 index、 highLightIndex 等參數處理不一樣的宮格效果
// 具體能夠參考本文最後開源的抽獎組件
function renderItem(item, index, highLightIndex) {
    const { url } = item;
    return <Image style={{ width, height }} source={{ uri: url }}/>; } <View style={LotteryStyle.container}> { data.map((item, index) => { return renderItem(item); }) } </View> 複製代碼

動畫

接下來重點介紹如何實現轉盤動畫效果,仔細觀察會發現整個轉盤動畫能夠分爲三個階段:github

  1. 抽獎開始:轉盤加速旋轉階段
  2. 等待抽獎結果:勻速旋轉階段(可能有、可能無,根據後端抽獎接口響應速度決定)
  3. 轉盤降速階段,逐步停到中獎宮格

九宮格的加速和衰減曲線決定了轉盤動畫的流暢程度。後端

實現原理:react-native

轉盤的轉動,能夠採用 setTimeout 快速修改下一個應該高亮的宮格,從而達到轉動的視覺效果函數

旋轉的速度其實就是 setTimeout 的interval間隔,interval越大速度越慢佈局

所以轉盤動畫的流暢程度實際取決於 setTimeout interval 值變化的連續性flex

方案一:手動模擬三個階段

手動模擬三個階段大致思路以下:動畫

  1. 前 CYCLE_TIMES = 30 次, 每次interval time 遞減10ms,轉盤逐漸加速
  2. 30次後若是沒有返回了抽獎結果,則執行勻速旋轉等待後端返回,若是返回告終果執行第三條
  3. 以後 8 次每次interval time 遞增20ms,轉盤逐漸降速
  4. 中獎前一次速度減80ms

下面的僞代碼:

function startLottery() {
    this.setState({
        highLightIndex: currentIndex
    }, () => {
            this.currentIndex += 1;
            if (this.currentIndex > CYCLE_TIMES + 8 + this.uniformTimes && this.luckyOrder === currentOrder) {
                clearTimeout(this.lotteryTimer);
                // 完成抽獎,展現獎品彈窗等
            } else {
                if (this.currentIndex < CYCLE_TIMES) {
                    // CYCLE_TIMES = 30 次, 每次速度遞加 10ms,
                    this.speed -= 10;
                } else if (this.currentIndex > CYCLE_TIMES + 8 + this.uniformTimes && this.luckyOrder === currentOrder + 1) {
                    // 中獎前一次降速 80 急停效果
                    this.speed += 80;
                } else if(this.luckyOrder) {
                    // 後端爲返回結果是勻速旋轉
                    this.uniformTimes += 1;
                else {
                    this.speed += 20;
                }
                // 確保速度不能低於 50 ms
                if (this.speed < 50) {
                    this.speed = 50;
                }
                this.lotteryTimer = setTimeout(this.startLottery, this.speed);
            }
        }
    );
}
複製代碼

方案二:Tween.js 動畫庫

Tween.js是一個JavaScript的動畫補間庫,容許你以平滑的方式更改對象的屬性或者某一個特殊的值。你只需告訴它什麼屬性要更改,當補間結束運行時它們應該具備哪些最終值,須要進過多長時間或者多少次數,補間引擎將負責計算從起始點到結束點任意時間點應該返回的值。

聽起來正是咱們想要的效果,咱們能夠以下定義加速、勻速、減速三個動畫效果:

function animate(): void{
    TWEEN.update();
}

// 轉盤加速階段 : interval 通過20次 從初始值 100(啓動速度) 下降到 40 (轉盤最高速度) 
// interval 變化曲線爲 TWEEN.Easing.Quadratic.In 
// 關於 Tween.js 各類曲線數值變化能夠參考這裏 https://sole.github.io/tween.js/examples/03_graphs.html
const speedUpTween = new TWEEN.Tween({ interval: 100 })
    .to({ interval: 40 }, 20)
    .easing(TWEEN.Easing.Quadratic.In)
    .onUpdate((object) => {
        // onUpdate 每次數值變化的回調, 能夠拿到本次 interval 的值, 而後 setTimeout 開始動畫
        setTimeout(() => {
            setHighLightIndex(highIndex + 1);
            animate();
        }, object.interval);
        currentSpeed = object.interval;
    })
    // 加速階段完畢進入勻速階段
    .onComplete(() => {
        speedUniformAnimate();
    })
    .start();

// 勻速運動
function speedUniformAnimate(): void{
    setTimeout(() => {
        // 若是沒有拿到抽獎結果 轉盤繼續勻速轉動
        if (lotteryResult) {
            setHighLightIndex(highIndex + 1);
            speedUniformAnimate();
        } else {
            // 勻速階段完畢 開始降速
            speedDownTween.start();
        }
    }, currentSpeed);
}

// 降速階段 從當前速度 currentSpeed 實際是 40 通過 8次衰減 降爲 500,
// interval 變化曲線爲 TWEEN.Easing.Quadratic.Out
const speedDownTween = new TWEEN.Tween({ interval: currentSpeed })
    .to({ interval: 500 }, 8)
    .easing(TWEEN.Easing.Quadratic.Out)
    .onUpdate((object) => {
        setTimeout(() => {
            setHighLightIndex(highIndex + 1);
            animate();
        }, object.interval);
    });
複製代碼

上述Demo有兩個問題:

由於 Tween動畫 必需要指定目標值,但在降速階段有可能通過8次減速 最終中止的位置並非咱們中獎宮格的位置。固然這個問題咱們能夠反向經過控制降速階段的開始時間:在勻速運動階段,只有拿到抽獎結果而且距離中獎位置還有8格的時候纔開始降速

第二個問題是 若是轉盤須要支持屢次抽獎 speedUpTween、 speedDownTween 等預約義動畫須要從新初始化,官方文檔裏並無找到相似reset的方法,暫時只能從新生成一遍。

開源的組件react-native-supper-lottery目前採用的是方案一。這裏提供一個方案二模擬三階段的Demo感興趣的小夥伴能夠仿照實現一版基於Tween.js的動畫。

完成了佈局和動畫等核心功能,以後的封裝組件提供start、stop等抽獎函數就很簡單了,這裏再也不詳述,詳細代碼能夠參考組件react-native-supper-lottery,也能夠直接使用。

相關文章
相關標籤/搜索