阅读 872

九宫格抽奖中的程序设计

背景


上线一个九宫格抽奖活动,可以分享的细节较多,写一篇文章记录一下。

你肯定见过,各种各样的抽奖。概率问题对吧,但他们会通过一些紧张的过渡效果反馈给用户。例如

这时候我们本以为前端可以已一种上帝视角来控制抽奖概率,但是需求对过了之后才发现,中奖那个奖励是服务端接口返回的。

这样就比较尴尬,写一个虚假的慢慢滚动到指定礼物上的难度比直接前端随机可高多了。

基于这个起点出发,我打算做一个公共抽奖的方法出来,以此来快速搭建基于类似的业务场景。

顺边说一下,此项目运用了智能生成代码工具imgcook,效果也是非常的棒,这样我就只需要专注于业务

完整的开发流程在以前的文章中有详细步骤,

最后也会把源码贴到下方,已经发布至npm上。后面配上链接。

抽奖

设计稿大概长这样。

我们可以想象到,基础功能是需要点击效果沿着如图方向跑马灯。

这里可能有人提出疑问,为什么让我们用想象力开发呢?给你看。

好的,非常棒。今天忘带我西瓜刀来公司了。

因为UI并没有给我一个动画效果,我相信大家看到这个九宫格,默认就会想到跑马灯一样的跑,并且是顺时针跑。可能因为看见的太多了这种效果,所以大家不以为奇(成语很严谨,配上传送门)

从设计稿上来看,并且执行我们到想象力,需要的功能就是跑马灯的速度要从慢到快,然后从快到慢,最后慢慢停止到服务端事先返回到礼物上。

列一下我们拥有的条件

  • 服务端按顺序返回的礼物列表
  • 服务端返回的中奖礼物id
  • 设计稿(可以直接智能生成)

列一下我们需要做到的效果

  • 宫格跑马灯效果
  • 跑马灯速度要从慢到快,然后从快到慢
  • 跑马灯最后停下的地方为指定位置
  • 扩展一些功能(如中断、重新抽奖)

捋清楚思路我们开始设计具体开发思想。开发业务并不是上来就堆代码,而是进行系统的考虑和技术调研之后再进行开发。

研发步骤

智能生成代码imgcook

我们首先把sketch源文件进行输入,生成原始代码,需要扣除中间九宫格模块,因为那里我们需要定制化开发。

原理是通过sketch目录结构生成schema源码,再通过解析schema生成标准模块代码。

生成出来的样式大概为这个样子。再次附上传送门

ps:马赛克是我后期加的

抽奖九宫格布局

这里遇到一个问题,九宫格的跑马灯顺序是顺时针顺序,我们运用什么样的布局来布局九宫格就是个问题。

这里我们有两个假设。

flex布局

如果我们通过flex布局来设计盒子模型的话,九宫格的布局应该是这个顺序

此时,跑马灯顺时针跑的时候,相对应的数组索引闪烁为1,2,3,6,9,8,7,4, 1,...

是一个很不好找规律的排列。

数据结构设计上来说,我们更倾向于维护一个有规律的数据状态。例如:1,2,3...,有规律状态的话就可以对应循环Array索引的值,更方便的做一些处理。

但是,这条线并没有走断,我们还是有两种选择

  • 改变数组的顺序,使其顺时针跑马灯的顺序符合有序队列。
  • 单独维护跑马灯顺序数组,映射对照数组。

我认为这两种方法都不好,布局层面的问题尽量用布局解决。如果因为布局设计不合理,导致js需要弥补一些漏洞是一种非常不合适的选择,证明我们的设计并不合理,设计思想中包含了妥协。

flex布局得到得效果有限,我们继续思考第二种布局方案

定位布局

初步想一下,定位布局可以实现这样的布局,先看一下代码

我们通过控制className来控制position的位置,css中列出了每个奖品盒子的定位数据。

但是,这条线有一些问题

  • 如果不是九宫格,而是99宫格,需要定制99个position(难以维护,比如想调整gutter)
  • 如果需要调整礼物的位置,维护成本较大
  • 我们需要单独维护一套坐标数组。

相比较于第一个flex假设,核心的矛盾并没有解决,因为依然需要独立一套逻辑进行维护。这于我们最开始的设计思路相违背。

所以我们拿着我们的分析结果继续进行假设。

grid布局

这里我们先“重新”普及一遍grid布局,为什么要重新普及呢,因为grid布局刚推出的时候,因为布局新奇很多人都去接触并使用到了具体业务中去,但是得到的效果并不是很好,因为很多兼容性问题的不兼容。

grid 是一个CSS简写属性,可以用来设置以下属性:

显式网格属性 grid-template-rows、grid-template-columns 和 grid-template-areas,
隐式网格属性 grid-auto-rows、grid-auto-columns 和 grid-auto-flow,
间距属性 grid-column-gap 和 grid-row-gap

grid布局兼容性现在是这样的

我们用grid网格布局来实现我们的九宫格布局,代码及效果如下

效果是符合我们预期的,并不用多维护一套逻辑来处理布局。

css的事情就让css来解决。

数据结构

贝塞尔曲线问题。

如上述我们有一个需求是这个样子的:需要跑马灯的速度由慢至快,由快至慢。

数据方面遇到的第一个问题就是类似缓动动画的问题,首先解释一下为什么是类似。因为我们最开始设计的时候是通过参数改变来控制当前闪烁的位置。如果由速度要求,那么只需要控制变量变化的“速率”即可。

什么是缓动动画呢,我们先来看一下缓动公式:

begin = begin + (end - begin) / 缓动系数

有一些同学可能比较熟悉,jquery的animate属性就是缓动动画,我们可以通过修改缓动系数来控制begin的速率。然而这样就可以达到一个缓动的效果。

贝塞尔曲线公式则为缓动公式。

缓动函数指定动画效果在执行时的速度,使其看起来更加真实。

现实物体照着一定节奏移动,并不是一开始就移动很快的。当我们打开抽屉时,首先会让它加速,然后慢下来。当某个东西往下掉时,首先是越掉越快,撞到地上后回弹,最终才又碰触地板。

在css中也经常用到了一些预置的缓动函数。例如:

我们经常运用这些函数来进行一些过渡效果的开发。在这片文章中我选择了二次贝塞尔曲线公式来实现我们的缓动跑马灯效果。

数学公式如下:

根据数学公式生成js函数,函数需要三个参数

  • 起始点坐标
  • 控制点坐标
  • 锚点坐标

函数如下:

// 配一个代码块的图

我们先预设一些必要参数及状态

我们需要点的缓动坐标曲线是这个样子的,曲线不用太猛烈,比较柔和就好

可以根据辅助工具算出相应点控制点坐标。这样我们就有了缓动函数的雏形

有了缓动函数之后我们就可以控制“速率”,进一步可以想象,当跑马灯速度慢下来后,如果缓动函数返回数据接近minSpeed的时候,我们判定当前位置是否为中奖位置,则可以做到下一个需求----停止到指定位置。

下面我们讲一下代码方面的设计思路。

schema设计

首先我们希望这个抽奖方法可以抽离成为公共方法,并且与业务耦合性不高。

所以组件需要的位置信息我们设计为注入形式。

可以设想,我们需要设计哪些api呢

  • init 配置参数函数
  • start 触发开始函数、并且有钩子函数。
  • end 结束回调函数
  • subcribe 订阅(虽然推荐注入,但是订阅依然需要开放)
  • hoc 高阶注入函数

api执行阶段也比较简单,执行前init配置参数,触发start方法,end结束回调返回当前endId,组件通过subcribe或者hoc来获取当前位置等信息。

这里我选择先设计用法,后设计源码。用法demo如下:

可以看到start方法中包含一个action的钩子,勾回了状态、结束位置等返回参数。

我这边具体实现的代码如下:

最终效果

最后我们形成的最终效果如下:

总结。

这样的一个抽奖功能其实并不难,如果只考虑如何实现业务而仓促堆写代码,我想那样从中无法获取任何提升。所以认真设计,认真总结。 最后给予自己一个疑问。

To be, or not to be: that is the question ---William Shakespeare

npm地址: www.npmjs.com/package/ext…

希望以后所沉淀的事物并非一定是“大的”事情,而是用心做的事情。

往期文章推荐

《提升用户体验之局部过渡》

《聊一聊如何通过UI智能生成代码提高团队成产力》

拜拜