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

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

本文可能涉及如下內容:html

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

九宮格佈局

九宮格你們應該都挺熟悉的吧,就是九個格子嘛,下面給你們看看咱們線上的九宮格抽獎↓↓前端

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

這個佈局相信你們都很熟悉吧,特別是看過阮一峯Flex 佈局教程:實例篇的童鞋們,是否是倍感親切。ios

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

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

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

這裏涉及到了rem佈局的問題,因爲某些歷史緣由,咱們的項目並無使用rem的方法來進行移動端頁面的開發.axios

這就致使咱們在開發某些須要高自適應的組件時比較蛋疼。可是最近在寫其餘項目的時候想到一個挺適合咱們使用的方案,不過對瀏覽器、手機系統的版本可能有些許要求。後端

@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,以達到九宮格中"蹬蹬蹬"的效果。

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,但願會有幫助,謝謝。

相關文章
相關標籤/搜索