目标
希望实现一个九宫格跑马灯效果的抽奖功能,但希望不止局限于固定的样式或效果。考虑做成一个n*n的可自定义某些参数的组件。可自定义某些参数例如:能指定动画效果的,能局部自定义样式的,等等。
实现
网上有很多样例,实现语言各式各样,react, vue, jquery, 纯js...等等,遗憾的是都是上图上代码,代码里都是各种嵌套的if else, 和不明意义的数字常量。今天想说说实现的思路。
既然定义为组件,就要有个接口,有输入输出。一个3*3的九宫格,我们把九宫格中去除中间的启动按钮之外的方块定义为奖池,抽奖就是从奖池方块列表中选定一个产生,选不中则未中。这里,奖池列表作为组件的输入。
而点击抽奖到选定抽中或未中可以前端控制,也可以后端控制,这里我们考虑是后端控制的情况,也就是点击抽奖需要请求数据,在返回数据中获取抽奖结果。
而事实上抽奖组件功能比较独立,所以组件的输出,可以根据实际需要,自定义向外传递输出。
布局
首先我们遇到的就是布局的问题,一个3*3的九宫格,中间的按钮是启动按钮,其他的奖池方块是自然布局呢?还是按照跑马灯顺序布局?自然布局是指:
这种布局可以直接使用flexbox 绘制,当然,需要特殊处理下中间按钮。
跑马灯布局是指:
这种布局可以使用绝对定位,把元素的索引和他们的位置摆正对齐,放好。
从css实现上,没有什么差距,但是选择一种布局,直接决定后面的跑马灯的算法。
跑马灯
跑马灯效果的实现是给每个方块加遮罩,然后利用定时器控制遮罩的出现顺序。如果选择了上面第一种的自然布局,那么在移动遮罩时,遮罩一定不是顺序+1的移动,而是它的移动顺序的索引和方块的索引之间有一个映射的关系,也就是遮罩要按照0,1,2,4,7,6,5,3的顺序去循环移动。model层要始终维护这份映射关系。
如果选择了上面第二种跑马灯布局,那么遮罩只需要按照顺序+1移动就可以了。但是一旦使用这种布局,就倾向于跑马灯是按照顺时针来动画的,如果动画改变,布局需要重新修改。当然不修改也可以,但是会比较麻烦。
减速
跑马灯效果通常是先经过一段匀速运动,然后再经过一段减速运动,最后停在指定的方块上。匀速运动比较好实现,定时器的速度是常量即可,对于减速运动来说,有很多实现方法。减速过程是线性还是非线性,我们可以不同的实现。如果是简单一点,可以计算步数,匀速将速度减下来,效果好一点我们可以借助贝塞尔函数。
关于贝塞尔函数的数学原理等,可以自行百度。我们主要是利用了三阶贝塞尔函数的实现,自定义曲线的曲率,然后和减速运动结合起来。
当然,既然用到了数学的函数曲线,我们可以任意定义速度曲线,js Math方法去生成一个函数曲线(例如Math.pow()),从而得到减速过程的速度。
组件和外部通信
这里说一下抽奖组件怎么和外部通信的问题。如果是react实现的话,通过props 回调,或者结合 redux 等都能较为方便的实现通信。如果是vue实现的话,可以通过广播,通知外部。
在小程序中,可以通过结合triggerEvent + setData + properties observer 实现。小程序自定义组件内部通过triggerEvent触发外部,外部setData修改数据,而自定义组件内部properties observer发现数据变化,从而拿到新的数据。
没有代码。嗯。一千个人应该有一千种实现方法,希望学习更为简洁的思路。欢迎拍砖。