寫在前面,年會將至,需求天然也跟各類抽獎有關啦。最近恰好接了一個緊急的九宮格抽獎需求,順便也記錄一下擼這個簡易九宮格的過程吧。css
本文可能涉及如下內容:html
九宮格你們應該都挺熟悉的吧,就是九個格子嘛,下面給你們看看咱們線上的九宮格抽獎↓↓前端
等等..上面這個好像不是九個格子(它是DEMO)react
這個佈局相信你們都很熟悉吧,特別是看過阮一峯Flex 佈局教程:實例篇的童鞋們,是否是倍感親切。ios
沒錯,咱們這個佈局是基於flex完成的,主要思路是縱橫元素的分離。git
遇坑點,其實也不算坑,算是一個移動端自適應上的問題吧。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() {
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() {
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都不該該由前端決定。
從前端的角度說,咱們大概須要兩個接口:
本次記錄的是個這兩天作的小需求,也算是我首次嘗試作相似的東西,相信在不遠的未來會有一個大轉盤等着我(微笑)。
九宮格抽獎DEMO源碼-React,一個用create-react-app臨時搭的demo,但願會有幫助,謝謝。