• 活动抽奖组件设计


    一 项目背景

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

    二 交互以及视觉

        整体视觉:

        略

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

       

        交互:略

    三 分析需要解决的问题

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

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

        首先,整理了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时间,最重要的,经常总结与反思,查找自身的不足,在繁忙的工作中提升自己。

    吾生有涯 而知也无涯矣
  • 相关阅读:
    java 泛型
    数据结构与算法分析java——线性表1
    常见链表题
    网络面试题集锦
    java 网络流 TCP/UDP
    java文件
    java IO流——字节流
    java IO流——字符流
    java集合框架——工具类
    java集合框架——Map
  • 原文地址:https://www.cnblogs.com/Sherlock09/p/11199980.html
Copyright © 2020-2023  润新知