基于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,但愿会有帮助,谢谢。

相关文章
相关标签/搜索