寫在前面,年會將至,需求自然也跟各種抽獎有關啦。最近剛好接了一個緊急的九宮格抽獎需求,順便也記錄一下擼這個簡易九宮格的過程吧。
本文可能涉及以下內容:
- 九宮格佈局
- 九宮格動效
- 抽獎邏輯處理
- 前後端聯調
九宮格佈局
九宮格大家應該都挺熟悉的吧,就是九個格子嘛,下面給大家看看我們線上的九宮格抽獎↓↓
等等..上面這個好像不是九個格子(它是DEMO)
這個佈局相信大家都很熟悉吧,特別是看過阮一峰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,以達到九宮格中"蹬蹬蹬"的效果。
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,希望會有幫助,謝謝。