写在前面, 年会将至, 需求自然也跟各种抽奖有关啦. 最近刚好接了一个紧急的九宫格抽奖需求, 顺便也记录一下撸这个简易九宫格的过程吧.
本文可能涉及以下内容:
九宫格布局
九宫格动效
抽奖逻辑处理
前后端联调
九宫格布局
九宫格大家应该都挺熟悉的吧, 就是九个格子嘛, 下面给大家看看我们线上的九宫格抽奖↓↓
等等.. 上面这个好像不是九个格子 (它是 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, 希望会有帮助, 谢谢.
来源: https://juejin.im/post/5a71cb28518825734a75025f