• 抽奖动画 九宫格抽奖


    本文介绍九宫格抽奖功能的实现。

    1.需求

    功能很简单,来看看高保截图,如下图1
    image
    图1
    需求的功能点如下:

    1. 用户点击抽奖,九宫格四周的图片顺时针依次闪烁,空转几圈。
    2. 请求接口,等接口有返回后最后对应的奖品闪烁,其他奖品不闪烁。
    3. 登录后,正中间的抽奖这个小方格点亮,未登录是灰色,这一点和抽奖无关,本文不做介绍。
    4. 最后弹框弹出抽奖结果。

    2.整体思路

    图片闪烁,只要图片所在的dom的背景色和其他不一样就可以了,如上图1,5元话费的背景是红色的。顺时针依次闪烁的话只要不断切换小方格的样式,当然要按照次序来,需要给小方格排个序,并且正中间这个“抽奖”按钮不能放在排序中。
    关于依次闪烁,这里想到的是setTimeout方法,并且需要按照一定的顺序执行定时器,可以通过控制setTimeout(fn, timeout)的第二个参数来实现这个效果。

    3.实现过程

    3.1 布局

    这里把数据放在一个数组中,包含奖品图片,奖品ID,奖品中奖后的图片等。为了布局方便,把中间的抽奖按钮和两个谢谢合作也放在数据中。代码如下:

    luckyDrawList: [
        {
            id: 1,
            prizeId: "1008604",
            active: false,
            imgSrc: require('../../assets/images/blog/lucky1@3x.png'),
            prizeSrc: require("../../assets/images/blog/prize1@2x.png"),
            prizeName: "10元红包"
        },
        {
            id: 2,
            prizeId: "1008606",
            active: false,
            imgSrc: require('../../assets/images/blog/lucky2@3x.png'),
            prizeSrc: require("../../assets/images/blog/prize2@2x.png"),
            prizeName: "5元红包"
        },
        {
            id: 3,
            prizeId: "1008602",
            active: false,
            imgSrc: require('../../assets/images/blog/lucky3@3x.png'),
            prizeSrc: require("../../assets/images/blog/prize3@2x.png"),
            prizeName: "50元红包"
        },
        {
            id: 4,
            prizeId: "1008603",
            active: false,
            imgSrc: require('../../assets/images/blog/lucky4@3x.png'),
            prizeSrc: require("../../assets/images/blog/prize4@2x.png"),
            prizeName: "30元红包"
        },
        {
            id: 5,
            active: false,
            imgSrc: require('../../assets/images/blog/lucky5@3x.png'),
            disableImg: require('../../assets/images/blog/lucky52@2x.png')
        },
        {
            id: 6,
            active: false,
            imgSrc: require('../../assets/images/blog/lucky67@3x.png'),
            prizeName: "谢谢参与"
        },
        {
            id: 7,
            active: false,
            imgSrc: require('../../assets/images/blog/lucky67@3x.png'),
            prizeName: "谢谢参与"
        },
        {
            id: 8,
            prizeId: "1008601",
            active: false,
            imgSrc: require('../../assets/images/blog/lucky8@3x.png'),
            prizeSrc: require('../../assets/images/blog/prize8@2x.png'),
            prizeName: "100元京东超市红包"
        },
        {
            id: 9,
            prizeId: "1008605",
            active: false,
            imgSrc: require('../../assets/images/blog/lucky9@3x.png'),
            prizeSrc: require('../../assets/images/blog/prize9@2x.png'),
            prizeName: "一朵鲜花"
        }
    ]
    
    

    九宫格布局可以采用流行的flex布局,给容器设置宽度,高度,然后设置display: flex;并且align-content: space-between;每个格子设置固定宽度高度,这样也不需要设置margin,自然分布在容器中,代码如下:

    .lucky-draw-list {
        margin: 0 auto;
         530px;
        height: 519px;
        @include flex(row, normal, normal, wrap, space-between);
        .lucky-draw-item {
            position: relative;
             176px;
            height: 171px;
            border-radius: 20px;
            color: #fff;
            vertical-align: bottom;
            img {
                 160px;
                height: 157px;
            }
            .start-tips-times {
                position: absolute;
                left: 50%;
                transform: translateX(-50%);
                bottom: 40px;
                 100%;
                height: 28px;
                font-size: 22px;
                color: rgba(255, 255, 255, 1);
                span {
                    font-size: 28px;
                    text-decoration: underline;
                }
            }
        }
        .active {
            background: #ff5e06;
        }
    }
    
    

    3.2 顺时针旋转

    布局有了,就要开始让每个格子切换背景色了,这里有一个问题,如何让九宫格顺时针旋转。比较特殊的是,这里中间有一个格子不用切换,其他的需要切换,所以上面的奖品数据不能直接修改active属性来切换样式,需要用一个变量记住“顺时针”的顺序。代码如下:

    alongPointer: [1, 2, 3, 6, 9, 8, 7, 4]
    
    

    这样就可以alongPointer变量中每个元素的值和luckyDrawList中的id属性一一对应,可以看到alongPointer数组中没有5,这是中间的点击按钮。如下图2。
    image
    图2

    3.3 定时任务

    定时任务是这个动画的关键,可以把这个顺时针旋转抽象为按照顺序执行的一系列异步操作。Promise是一种异步操作解决方案,可以把这些异步操作放在Promise中,并给定它的等待时间。代码如下:

    startRoll() {
        let count = 5 * 8                           //8个奖品,先空转5圈
        let timeourID = null
        let tasks = []
        let exapsed = parseInt(1000 / 8)            //间隔时间
        const highlight = i =>
            new Promise((resolve, reject) => {
                this.timeourID = setTimeout(() => {
                    this.luckyDrawList.forEach(u => {
                        u.active = u.id === this.alongPointer[i % 8] //轮播
                    })
                    resolve()
                }, exapsed * i)
            })
        //添加异步任务
        for (let i = 0; i < count; i++) {
            tasks.push(highlight(i))
        }
        //完成所有异步任务,执行后续操作
        Promise.all(tasks).then(() => {
            clearTimeout(this.timeoutID)
        })
    }
    

    上面代码中u.active = u.id === this.alongPointer[i % 8]这里不断轮播,i的值的范围是0~40,8个奖品的id保存在this.alongPointer变量中,用0到40和i取余,得到的是5组0,1,2,3,4,5,6,7,最后就空转了5圈。如下图3
    image
    图3

    3.4 选中奖品

    最后完成空转之后就可以定位奖品了,这里用一个随机函数getRandomIntInclusive来生成奖品,它生成一个大于等于min,小于等于max的随机数。代码如下:

    //生成随机奖品
    getRandomIntInclusive(min, max) {
        min = Math.ceil(min)
        max = Math.floor(max)
        return Math.floor(Math.random() * (max - min + 1)) + min
    }
    

    所有异步任务完成之后,可以随机生成奖品了,使用Promise.all()来判断所有异步任务都执行完毕。代码如下:

    Promise.all(tasks).then(() => {
        clearTimeout(this.timeoutID)
        this.showLotteryResult()
    })
    //设置奖品
    showLotteryResult() {
      let randomIndex = this.getRandomIntInclusive(0, this.luckyDrawList.length - 1)
      let id = this.luckyDrawList[virtualIndex].id
      console.log(randomIndex, virtualIndex, id)
      this.luckyDrawList.forEach((u, i) => {
          u.active = u.id == id
      })
    }
    

    最后看看动画的效果,如下图4
    image
    图3

    3.5 对Promise的改进

    Promise的写法可以改进一下,使用async,await的方式来调用,这样代码看起来更加简洁。代码如下:

    startRoll() {
        let count = 5 * 8                           //8个奖品,先空转5圈
        let interval = parseInt(1000 / 8)           //切换时间间隔
        let sleep = time => new Promise((resolve) => {
            this.timeoutID = setTimeout(resolve, time)
        })
        let pushEvent = async(count, interval) => {
            for (let i = 0; i < count; i++) {
                await sleep(interval)
                this.luckyDrawList.forEach(u => {
                    u.active = u.id === this.alongPointer[i % 8]
                })
            }
        }
        pushEvent(count, interval).then(() => {
            clearTimeout(this.timeoutID)
            this.showLotteryResult()
        })
    }
    
    

    4. 总结

    本文介绍了九宫格抽奖的实现方式,涉及到的知识点有Promise,Promise.all,async/await/then等,这里还可以优化一个功能,就是让九宫格先慢后快,最后再快速切换,下次有时间再研究。

  • 相关阅读:
    PHP 产生唯一码的方法分析
    Nginx 缓存cache的5种方案
    Nginx 常见应用技术指南
    BigPipe 技术细节分析
    Nginx 配置负载均衡
    linux下调整java版本
    跨域cookie在IE与firefox下的不同
    css2.1中 firefox 与IE 对margintop的不同解释
    ADOQuery代替ClientDataSet做3Tier系统
    查询数据库中的表建个进度条
  • 原文地址:https://www.cnblogs.com/tylerdonet/p/16579746.html
Copyright © 2020-2023  润新知