活动抽奖组件设计

一 项目背景

    略 公司内部资料以及安全红线 只讨论技术细节 不透漏相关交互和设计

二 交互以及视觉

    整体视觉:

    略

    抽奖模块:大概是以一个花朵的花瓣为抽奖背景,旋转的时候不同的花瓣点亮,省略视觉稿,只讨论技术实现细节

   

    交互:略

三 分析需要解决的问题

    当做一个没有做过的东西的时候,这时候应该分析下完成这个效果需要考虑的问题,然后分解分析,然后逐步实施,分析总结遇到过的问题,是否有没考虑到的。

    其实这种抽奖模块在现在的活动页中十分的常见,尤其是在活动页中。

    首先,整理了TODO List

  1.  需要写成组件的形式,方便复用,需要考虑组件需要的传递的参数等等。
  2. 后端是接口的形式,也就是说,用户点击抽奖的时候,已经获得了中奖信息,前端需要执行一下抽奖轮转的动画,这个动画的时间、转动速率、转动期间禁止需要重复点击、初始的指针位置都需要前端来控制。
  3. 状态管理,针对不同抽奖次数、区分是否登陆、是否在APP内等相应的状态进行处理,弹窗或者跳转相应的URL
  4. 前端埋点相关和数据上报相关。
  5. 样式问题,设计交互的思路实际上是一个九宫格抽奖,但是视觉样式却是偏向指针类的轮盘抽奖,逻辑层面暂定的是按照九宫格的思路去实现,但是需要样式方面进行调整
  6. 抽奖的结果也需要写成一个弹窗组件,并且需要定义传入的参数
  7. 异常展示,当接口调用失败、后端数据库库存不足等等需要相应的warning弹窗组件

    大致交互流程如下

 四 具体设计

        4.1 封装成公共组件,主要是对组件传递的参数进行定义,首先在首页需要调用抽奖组件,必传的有三个参数

            showLuckDraw // 是否展示抽奖模块,如果用户已经抽过奖,默认不展示此模块,直接展示抽奖结果

            userStatus        // 用户状态:首页接口中会返回此信息,需要传入到组件中

            drawStatus       // 抽奖状态:

            此外还需要给抽奖组件的子组件–抽奖结果组件传递参数

            showSpecial&&type

        props: {
            // 是否展示抽奖模块,如果用户已经抽过奖,默认不展示此模块,直接展示抽奖结果
            showLuckDraw: {
                type: Boolean
            },
            // 用户状态
            userStatus: {
                type: Number,
                required: true
            },
            // 抽奖状态 0: 跳转活动页 1: 需要通过活动增加抽奖次数 3: 不可抽奖
            drawStatus: {
                type: Number,
                required: true
            },
            // 是否已登陆 未登陆吊起登陆组件
            hasLogin: {
                type: Boolean,
                required: true
            },
            // 特殊弹窗 展示抽奖的结果
            showSpecial: {
                type: Boolean,
                default: false
            },
            // 向抽奖结果弹窗 传的值 显示对应的抽奖结果
            type: {
                type: Number,
                required: true,
            }
        },

    4.2 抽奖动画(核心逻辑)

        核心的思路是,当点击抽奖的时候,调用后端接口,并且设置转盘所需要的初始参数,包括如下

  • diff               速度累加变量,控制逐渐加快/逐渐减慢
  • star              开始按钮点击态,如果用户已经无法抽奖,直接禁用
  • speed          抽奖初始速度
  • isDisable     是否禁用开始抽奖按钮,防止重复抽奖,保证在在抽奖动画结束后才能再次抽奖,避免重复调用后端接口        

  • drawStatus  设置抽奖状态初始值

  • startTime     记录转动开始时间,用来控制动画时长

      同时,设置一个定时器,轮盘的背景图片依次切换来实现动画的效果,利用定期器的milliseconds 时间间隔进行速度控制,当达到预设的转动时间时,开始做减速运动,且如果后端返回的奖品id和前端设置的奖品id能对应上时,清除定时器,同时return中止函数,且重新设置转盘的状态值,完成抽奖动画。

    html 部分使用的简洁的jade,虽然刚开始上手有些不适应,但是用惯了之后,感觉十分简洁干净,不需要在大段的html代码里寻找calss等

<template lang="jade">
    .award-box(v-if="showLuckDraw")
        .award-bg
            .award(:class="'active' + (current)")
        button.start-btn(
            @click="start",
            :disabled="isDisable",
            :class="star ? 'star': '' "
            )
        .award-num 当前抽奖次数  
            i.award-n {{ currentNum = +this.drawStatus === 2 ? 1 : 0 }}   
            i 次
        special-dialog(:show-special="showSpecial", :type="id")
</template>
        
        move() {
                let timeout = setTimeout( () => {
                    this.current++; // 切换背景图,初始值可以随意设置
                    if ( this.current > 4) { // 因为只有5个奖品,所以当current 从0开始大于4的时候,重新加载第一张图片
                        this.current = 0;
                    }
                    // 若抽中的奖品id存在,且转动时间大于2秒,则开始减速转动
                    if ( this.award.id && ( Date.now() - this.startTime ) / 1000 > 2 ) {
                        this.speed += this.diff; // 转动减速
                        //若转动超过4秒,并且奖品id等于格子的奖品id,则停下来
                        // this.awards 是从后端获取的中奖结果,如果和前端对应的奖品id对应上的话则为中奖
                        if ( ( Date.now() - this.startTime ) / 1000 > 4 && this.award.id == this.awards[this.current].id ) {
                            clearTimeout( timeout );
                            this.star        = false;
                            this.isDisable   = false;
                            this.showSpecial = true;    // 调用抽奖结果组件
                            this.type        = this.id; // 将中奖结果赋值给type,显示对应的抽奖结果组件
                            setTimeout( () => {
                                console.log(this.award.id);
                            },0);
                            return; // 抽中奖后停止定时器
                        }
                    //若抽中的奖品不存在,则加速转动    
                    } else { 
                        // todo:如果后端接口200且返回的中奖结果是错的,这里可以处理下异常
                        this.speed -= this.diff;
                    }
                    this.move();
                }, this.speed );
            },

    4.3 状态管理,在调用move() 抽奖动画的之前,就对相应的用户状态做处理,未登陆的吊起登陆,没有次数的去获取次数,不可抽奖调用弹窗等等

    4.4 埋点这里在工具函数里封装了一个公共的函数,直接调用传参记录埋点信息,关于埋点的话有空再展开

    4.5 样式方面,遇到了一些问题

  • 最开始的想法是只改变花瓣的背景图,但是最后发现,图片是不规则的,是有角度的,难以定位准确。而且定位位置的计算在不同机型上有差异,所以采用整体图片的替换
  • 整体图片的替换又遇到一个闪烁问题:
    成因:因为当图片花瓣旋转时视觉上只是每个花瓣的变化,实际上是整张图片的变化,通过动态的切换calss来完成背景图片的替换。正因为这样,新的问题就产生了,当代码构建部署打包发不到线上时,静态资源一般会有单独的服务器,也就是说图片的资源请求会有一定延迟,这就导致了第一次加载点击抽奖的时候,页面花瓣转动会有闪烁,而第二次图片会走缓存,所以这个问题只出现一次,但是却是致命的
    解决方法:把url改为base64的Data URL,实际上就是利用base64编码把图片数据翻译成标准ASCII字符,Data URL是在本地直接绘制图片,不是从服务器加载,所以节省了HTTP连接,起到加速网页的作用,但是弊端就是IE8以下浏览器不支持这种方法(当然app内肯定是支持的,绝大部分手机也是支持的)。用这种方法会加重客户端的CPU和内存负担,总之有利有弊,而且不适合大图片,针对这个图片找UI替换了体积更小的图片,以减轻客户端的内存负担。

    4.6 针对中奖结果的弹窗,封装另一个组件,只需要传递type的参数就显示不同的中奖结果,类似的还有挂在到全局的弹窗组件,异常的时候及时抛出错误弹窗

五 总结和反思

    虽然说现在活动页比较多且交互复杂,但是可以尽可能的封装一些公共的组件,提供基本的骨架,只需要修改一些css就能快速完成一个功能。

    还有对于没有做过的东西要具体分析,拆解成步骤,一步一步的完成,同时预留好一定的buffer时间,最重要的,经常总结与反思,查找自身的不足,在繁忙的工作中提升自己。

猜你喜欢

转载自www.cnblogs.com/Sherlock09/p/11199980.html