基於React跑一個簡易版九宮格抽獎

mykurisu發表於2018-02-01

寫在前面,年會將至,需求自然也跟各種抽獎有關啦。最近剛好接了一個緊急的九宮格抽獎需求,順便也記錄一下擼這個簡易九宮格的過程吧。

本文可能涉及以下內容:

  • 九宮格佈局
  • 九宮格動效
  • 抽獎邏輯處理
  • 前後端聯調

九宮格佈局

九宮格大家應該都挺熟悉的吧,就是九個格子嘛,下面給大家看看我們線上的九宮格抽獎↓↓

基於React跑一個簡易版九宮格抽獎

等等..上面這個好像不是九個格子(它是DEMO)

基於React跑一個簡易版九宮格抽獎

這個佈局相信大家都很熟悉吧,特別是看過阮一峰Flex 佈局教程:例項篇的童鞋們,是不是倍感親切。

沒錯,我們這個佈局是基於flex完成的,主要思路是縱橫元素的分離。

  • 整個九宮格區域應該是一個定寬高(其實不定也無所謂)的塊元素,將每一行(row)縱向排列。
  • 每一行都是一個row,我們在row中將每個item塊space-between或space-around(根據業務自行判斷)。
  • 每個item裡面的內容也用flex水平居中一下,然後該怎麼還原設計稿就怎麼還原吧。

遇坑點,其實也不算坑,算是一個移動端自適應上的問題吧。

這裡涉及到了rem佈局的問題,由於某些歷史原因,我們的專案並沒有使用rem的方法來進行移動端頁面的開發.

這就導致我們在開發某些需要高自適應的元件時比較蛋疼。但是最近在寫其他專案的時候想到一個挺適合我們使用的方案,不過對瀏覽器、手機系統的版本可能有些許要求。

@function pxWithVw($n){
  @return 100vw * $n / 375
}

@function pxWithVwMax($n){
  @return 480px * $n / 375
}
@mixin pxVw2width($n) {
  width: pxWithVw($n);
  max-width: pxWithVwMax($n);
}
@mixin pxVw2height($n) {
  height: pxWithVw($n);
  max-height: pxWithVwMax($n);
}
複製程式碼

雖然我們沒用rem佈局,但是我們還是接了scss的,借用scss的mixin我們可以很爽的還原設計稿的各種引數,之所以還要限定一個Max值主要還是因為我們的專案支援在PC端檢視,所以需要給它限定一個極限的寬度。

問題來了,pxVw2height裡面為啥有個vw?其實就是個畫素比例的問題,設計稿中寬度與設計稿中裝置寬度的比例不就自然是每個px對應螢幕的比例了嘛。

不過這樣寫會有個不確定的地方,就是不同的DPR下,這種方法對畫素還原是否有影響,關於這點我暫時無法確定,不過還是會抽空去試驗一下的。

九宮格動效&抽獎邏輯處理

按理來說動效和抽獎邏輯是兩碼事,但是我們是在React裡面開發的,我覺得有必要把它們放到一起講。

動效核心

動效的觸發核心是activedId的實時變更,通過定時器,在某一時間間隔內改變父元件state中的activedId,以達到九宮格中"蹬蹬蹬"的效果。

基於React跑一個簡易版九宮格抽獎
class RowItem extends React.Component {
    renderImgClass () {
        switch (this.props.content.raw_name) {
            ...
        }
    }

    render() {
        const { content, activedId } = this.props;
        return (
            <div className={`${activedId === content.id ? 'row__item row__item-active' : 'row__item'}`} id={`row_item_${content.id}`}>
                <img src={content.img} alt="" className={this.renderImgClass()}/>
                {content.name}
            </div>
        )
    }
}
複製程式碼

上面是每個小方塊的原始碼,不難看出決定每個小方塊該作何顯示的地方這裡

${activedId === content.id ? 'row__item row__item-active' : 'row__item'}
複製程式碼

通過props傳進來的activedId來決定輪到哪一個方塊展示動效雖然我的九宮格那不叫動效,不過原理是互通的,想展示啥就儘管在這個元件裡面整就好了。

抽獎邏輯

先梳理一下這類九宮格抽獎的流程,剝離那些各種附加的特效,其實本質就是隨機(或指定)某個item的id為中獎id,然後我們圍繞著這個id確定該迴圈轉圈的次數,最後再確保中獎框能停留在指定id的item即可。

點選按鈕抽獎這個過程,我將它分成了兩個部分,一個是狀態檢測與歸零,另一個是觸發抽獎方法(其實也可以寫在一起,個人習慣將其分離,自己看起來比較舒服)。

  • handleBegin負責狀態檢測與歸零
handleBegin() {
    if (!this.state.prizePlaying) {
        this.setState({
            prizePlaying: true
        })
        axios.post(url)
            .then(res => {
                if (res.data.code === 0) {
                    ...
                    axios.get(url2)
                        .then(res => {
                            if (res.data.code === 0) {
                                this.setState({
                                    ...
                                }, () => {
                                    this.setState({
                                        prizeActivedId: '',
                                        prizePrizeId: null,
                                        prizeTimes: 0,
                                        prizeActTimes: 0
                                    }, () => {
                                        this.handlePlay()
                                    })
                                })
                            }
                        })
                } else {
                    ...
                }
            })
    }
}
複製程式碼

一開始先檢測當前是否處於抽獎狀態,如果不是才進行下面的請求,請求都完成後,將關於九宮格的狀態都進行復原,然後才進行下一步操作。關於狀態復原,個人認為這是相對便捷而且安全係數較高的做法,當然如果要保留原有的抽獎狀態也是可以的,不過在用時候需要注意兩個Times的關係。

  • handlePlay真正的抽獎方法
handlePlay() {
    let prize;
    switch (this.state.prizeLottery) {
        prize = ...
    }
    this.setState({
        prizePrizeId: prize,
        prizeActivedId: 0
    })
    let times = this.state.prizeList.length * Math.floor(Math.random() * 5 + 4)
    this.setState({
        prizeTimes: times
    })
    this.begin = setInterval(() => {
        let num;
        if (this.state.prizeActivedId === this.state.prizePrizeId && this.state.prizeActTimes > this.state.prizeTimes) {
            clearInterval(this.begin)
            ...
            this.setState({
                prizePlaying: false
            })
            return
        }
        if (this.state.prizeActivedId === '') {
            num = 0
            this.setState({
                prizeActivedId: num
            })
        } else {
            num = this.state.prizeActivedId
            if (num === 7) {
                num = 0
                this.setState({
                    prizeActivedId: num
                })
            } else {
                num = num + 1
                this.setState({
                    prizeActivedId: num
                })
            }
        }
        this.setState({
            prizeActTimes: this.state.prizeActTimes + 1
        })
    }, 100)
}
複製程式碼

確定中獎prizePrizeId,然後隨機計算出一個最小的動畫迴圈次數,然後就可以啟動定時器開始動態更換prizeActivedId,隨著prizePrizeId的變更RowItem也會重新渲染,也就成為了所謂的"蹬蹬蹬"效果啦。

如果看上面面的解釋覺得不夠詳細,可以在文章末尾找到github的傳送門,裡面有demo原始碼並且有相關的註釋。

前後端聯調

這裡主要想講講開始開發之前,自己對整個專案的規劃(或想法),本著前端不可信原則,我們每次啟動抽獎之前都應該與後端溝通,無論是次數還是最後的prizeId都不應該由前端決定。

從前端的角度說,我們大概需要兩個介面:

  • ① 類似init的介面,告訴我們的次數,以及是否已經有獲獎
  • ② 類似active的抽獎介面,我們告訴後端這裡發起了一次抽獎 請求②介面之後接著返回給我們獎品是什麼,然後我們在根據返回做前端id的轉換,這樣就能完成一個較為完善的閉環。

總結

本次記錄的是個這兩天做的小需求,也算是我首次嘗試做類似的東西,相信在不遠的將來會有一個大轉盤等著我(微笑)

九宮格抽獎DEMO原始碼-React,一個用create-react-app臨時搭的demo,希望會有幫助,謝謝。

基於React跑一個簡易版九宮格抽獎

相關文章