zoukankan      html  css  js  c++  java
  • 来来来,实现一个简单的抽奖

    一回头, 12月中旬了。 最近项目忙,还被封闭了半个月。
    为了保持一个月1到2篇博客,月中了,就说说上次写的抽奖吧。

    这里讲的是九宫格抽奖,其实圆盘的那种都类似。
    在线demo地址
    在线代码地址

    逻辑

    1. 点击抽奖后立马开始动画效果
    2. 请求接口获得中奖结果
    3. 减慢动画效果, 命中奖品

    从上可以看出来,其实你中奖不中奖是服务来决定的,前台那一些绚丽的动画,就是给你带来快感的。

    这里我们要先封装一个抽奖的对象,把抽奖本身的一些逻辑分离出来,和DOM等等不要有半毛钱关系。

    定义Lottery
    对外方法:

    • start: 开始
    • stop: 结束
    • setPrize: 设置奖项
      对外公布事件
    • onStart: 当开始的时候
    • onUpdate: 当旋转一步
    • onEnded: 当结束
    • onError

    Lottery初始化参数

    • startIndex: 0
      初始位置
    • pits: 8
      当前位置
    • interval: 100
      初始间隔
    • rate: 1.5
      下一次时间间隔系数
    • cycle: 20
      动基本次数:即至少需要转动多少次再进入抽奖环节
    • getInterval: null
      自定义旋转间隔函数,定义后会使用该函数的返回值interval
    • onStart: null
      开始后的回调函数
    • onUpdate: null
      旋转一次的回调函数
    • onEnded: null
      结束后的回调函数
    • onError: null
      异常的回调函数, 比如转动次数已经达到设定值, 但是没有设置奖项

    内部参数

    • this.originOptions = options
      传入的配置项
    • this.options = $.extend({}, defaultOption, options)
      合并后的配置项
    • this.ticketId = null
      定时器id
    • this.prizeIndexes = null
      奖项索引位置
    • this.times = 0
      已经转动的次数
    • this.index = 0
      当前所在的位置
    • this.animatingResult = false
      是否在模拟结果
    • this.cycle = this.options.cycle
      实际的转动基本次数, 大于开始中奖
    • this.processing = false
      是否进行中
    • this.lastTime = null
      上次转动时间

    再额外提两个方

    • emit
      这个类似events的emit, 触发订阅事件
      Lottery.prototype.emit = function (type) {
            var params = slice.call(arguments);
            var restParams = params.slice(1);
            var type = params[0];
    
            var method = this['on' + type];
            if ($.isFunction(method)) {
                method.apply(this, restParams);
            }
        }
    
    
    • innerStart和next, 这里实现只要调用next,就会进入下一次计时器作业
        Lottery.prototype.innerStart = function (cb) {
            var that = this;
            var next = function () {
                that.next(function () {
                    cb && cb(next);
                })
            }
            next()
        }
    
        Lottery.prototype.next = function (cb) {
            var interval = this.getInterval();
            this.ticketId = setTimeout(cb, interval);
        }
    

    到这里其实,大家都基本明白了怎么实现了吧。
    Demo效果图

    在线demo地址
    在线代码地址

    附上Lottery源码

    (function () {
        var defaultOption = {
            startIndex: 0, // 初始位置
            pits: 8,  // 当前位置
            interval: 100, // 初始间隔
            rate: 1.5,  // 系数
            cycle: 20,  //转动基本次数:即至少需要转动多少次再进入抽奖环节
            getInterval: null // 自定义旋转间隔函数
            //onStart: null, // 当开始
            //onUpdate: null, // 旋转一次
            //onEnded: null,  // 结束
            //onError: null  // 异常, 比如转动次数已经达到设定值, 但是没有设置奖项
        };
    
        var slice = Array.prototype.slice;
    
        function Lottery(options) {
            this.originOptions = options;
            this.options = $.extend({}, defaultOption, options);
    
            // 定时器Id
            this.ticketId = null;
            // 奖项
            this.prizeIndexes = null;
            // 转动次数
            this.times = 0;
            // 当前位置
            this.index = 0;
            // 模拟结果
            this.animatingResult = false;
            // 实际的转动基本次数, 大于开始中奖
            this.cycle = this.options.cycle;
            // 进行中
            this.processing = false;
            // 上次转动时间
            this.lastTime = null;
        }
    
        Lottery.prototype.start = function () {
            if (this.processing) {
                return
            }
    
            this.processing = true;
            // 增加随机数
            this.cycle = this.options.cycle + Math.floor(Math.random() * 10);
    
            this.emit('Start', this, this.index, this.cycle);
    
            this.lastTime = Date.now();
            var that = this;
            this.innerStart(function (next) {
                if (that.animatingResult) {
                    that.times++;
                }
                that.index = (that.index + 1) % that.options.pits;
    
                var continu = that.judge();
                if (!continu) {
                    that.stop();
                    return
                }
    
                that.printInfo();
                that.emit('Update', that, that.index, that.times);
                next();
            })
        }
    
        Lottery.prototype.judge = function () {
            var cycle = this.cycle;
            var times = this.times;
    
            // 到达旋转次数
            if (times > cycle) {
                // 没有设置奖项
                if (!$.isArray(this.prizeIndexes)) {
                    this.emit('Error', this, 404, '未设置奖项');
                    return false;
                }
    
                if (this.prizeIndexes.indexOf(this.index) >= 0) {
                    this.emit('Ended', this, this.index, this.prizeIndexes);
                    return false;
                }
            }
            return true;
        }
    
        Lottery.prototype.emit = function (type) {
            var params = slice.call(arguments);
            var restParams = params.slice(1);
            var type = params[0];
    
            var method = this['on' + type];
            if ($.isFunction(method)) {
                method.apply(this, restParams);
            }
        }
    
    
    
        Lottery.prototype.stop = function () {
            this.clearJob();
            this.animatingResult = false;
            this.ticketId = null;
            this.prizeIndexes = null;
            this.times = 0;
            this.processing = false;
        }
    
        Lottery.prototype.getInterval = function () {
            const getIntervalFn = this.options;
            if ($.isFunction(getIntervalFn)) {
                return getIntervalFn(this, this.index, this.times, this.cycle);
            } else {
                return Math.floor(this.options.interval * Math.pow(this.options.rate, this.times / 10));
            }
        }
    
        Lottery.prototype.clearJob = function () {
            clearTimeout(this.ticketId);
        }
    
        Lottery.prototype.innerStart = function (cb) {
            var that = this;
            var next = function () {
                that.next(function () {
                    cb && cb(next);
                })
            }
            next()
        }
    
        Lottery.prototype.next = function (cb) {
            var interval = this.getInterval();
            this.ticketId = setTimeout(cb, interval);
        }
    
        Lottery.prototype.reset = function () {
            this.stop();
            this.options = $.extends({}, defaultOption, this.originOptions);
            this.index = 0;
        }
    
        Lottery.prototype.setPrize = function (prizeIndexes) {
            if (this.animatingResult) {
                return
            }
            this.prizeIndexes = prizeIndexes;
            // 设置值后, 开始模拟中奖
            this.animatingResult = true
        }
    
    
        Lottery.prototype.printInfo = function () {
            var now = Date.now();
            console.log('index:', this.index, 'times:', this.times, 'cycle:', this.cycle, 'interval:', now - this.lastTime);
            this.lastTime = now;
        }
    
        window.Lottery = Lottery
    
    })()
    
    
  • 相关阅读:
    POJ2976 Dropping tests 01分数规划
    POJ 2728 Desert King 01分数规划,最优比率生成树
    codeforces 675E Trains and Statistic 线段树+贪心统计
    codeforces 675D Tree Construction set
    UVA 11235Frequent values(RMQ)
    FZU 2105Digits Count(线段树 + 成段更新)
    HDU 4006The kth great number(K大数 +小顶堆)
    UVA 624CD(01背包输出 + 输出路径)
    HDU 1796How many integers can you find(容斥原理)
    HDU 1817Necklace of Beads(置换+Polya计数)
  • 原文地址:https://www.cnblogs.com/cloud-/p/10120626.html
Copyright © 2011-2022 走看看