写在前面,年会将至,需求天然也跟各类抽奖有关啦。最近恰好接了一个紧急的九宫格抽奖需求,顺便也记录一下撸这个简易九宫格的过程吧。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,但愿会有帮助,谢谢。