javascript实现水果抽奖机

起因:

公司每个月都要做推广活动,很多推广活动都需要抽奖,但是以前的抽奖的特效太简单,于是美工看到京东的年会抽奖机,我就不得不走向逆向仿制的道路上,经过三天的攻克,终于实现了抽奖效果。

分析:

水果抽奖机的动画效果是三个轮播图进行滚动到指定的位置。虽然是三个,但是只要破解一个,将其他动画依次延迟执行就可以了。
分析其中的一个轮播动画,就会发现就是我们平常写的轮播图,只不多这个轮播可以自己进行轮播,并且由快到慢,是一个减速运动,最后停止到指定的轮播图中。
本来想用swiper.js进行轮播的实现,但是swiper在loop模式和freeMode模式下有bug,不得不自力更生。
轮播很简单,无非是通过定时器不断的改变的位置,但是要注意要把第一张图片复制为最后一张图,这是为了让轮播更流畅,否则轮播的就会显得很尴尬,具体原理类似于摄影的视觉停留。但是使用setTimeout和setInterval定时器可以达到效果,但是W3C中推出了requestAnimateFrame这样更优秀的浏览器的定时效果。具体用法自定百度。
轮播解决以后要解决轮播的速度,推荐看一下tween.js的源码,或者其他人的分析,我这里使用的linear,就是匀速运动,之所以不适用easeInOut使用为它只针对一次轮播,会发现这样的轮播效果 —快平慢–快平慢—快平慢—…..,所以只能自己控制速度。怎沫控制速度哪?先看我的linear函数:

linear: function(t, b, c, d) {
    /*
    *tween是ImagesLoop的原型属性,表示图片容器运动曲线函数
    *@param  number t       当前的时间
    *@param  number b       当前的初始值
    *@param  number c       当前的改变的值
    *@param  number d       当前的改变值所用的时间
    *@return object object  运动曲线函数组合成的对象
    */
    return t*(c/d)+b;
}

其实公式很简单就是(时间*速度+初始值=当前的位置),而速度就是(改变的距离/所需的时间)。具体到这次的轮播就是(改变的距离=显示最后一张图片是top的值,而所需时间就是自定义时间段),所以我们可以改变自定义的时间段达到改变速度的目的。整体时间曲线是先加速到平缓再到减速,为了达到这个效果,我们制定轮播15次,时间段为500,前5次时间段每次*0.8,中间5次不变,最后5次*1.8。例如:

easeInOut: function(count = 0, duration = 10) {
        /*
        *easeInOut方法通过控制完成每次轮播的时间来控制轮播的速度
        *@param  number defalutCount        默认轮播的次数
        *@param  number count               当前轮播的次数
        */
        let percent = parseInt(this.DEFALUT_COUNT / 3);

        if (count == (this.DEFALUT_COUNT - 1)) {
            this.slideToIndex();
            return duration;
        }
        if (count < percent) {
            return duration * 0.9;
        } else if (count < (2 * percent)) {
            return duration;
        } else if (count < this.DEFALUT_COUNT) {
            return duration * 1.5;
        }
    }

最后要解决的问题是,滚动到指定位置。例如滚动到第5张图,意味着轮播容器最后top等于前4张高度之和乘以-1加上‘px’。因为我的每次的初始值是从0开始的(top=0),所以最后一次轮播我将top值调整到我的指定图片所需的top值。
结论:顺利完成水果机。
代码:

function ImagesLoop(obj) {
        if (!this.empty(obj) 
            || !this.empty(obj.slideObjs) 
            || !this.empty(obj.slideWrap)) {
            return ;
        }
        this.interval    = obj.interval  || 200;
        this.slideObjs   = obj.slideObjs || {};
        this.slideWrap   = obj.slideWrap || {};
        this.DEFALUT_COUNT   = obj.defaultCount || 15;
        this.slideObjNum = this.slideObjs.length;
        this.totalHeight = this.getTotalHeight(this.slideObjs.slice(0,(this.slideObjs.length - 2)));
        this.index = obj.index || 3;
        this.stopAnimate = false;
        this.print('ImagesLoop', {
            slideObjs:this.slideObjs, 
            slideWrap:this.slideWrap,
            totalHeight:this.totalHeight
        });
    }
ImagesLoop.prototype = {
        empty: function(param) {   //检测参数是否为空
            if (!param) {
                throw new Error(param + 'Parameters can\'t empty!');
                return false;
            }
            return true;
        },
        getTotalHeight: function(slideObjs = []) {  //获取所有slide对象的所有高度的总和
            let totalHeight = 0;
            slideObjs.forEach((item) => {
                totalHeight += item.height;
            });
            return totalHeight;
        },
        print: function(fnName = 'fn', obj = {}) {  //打印所有的参数
            console.group(fnName);
            Object.keys(obj).forEach((key) => {
              console.log(key, obj[key]);
            });
            console.groupEnd('end'+fnName);
        },
        linear: function(t, b, c, d) {
            /*
            *tweenImagesLoop的原型属性,表示图片容器运动曲线函数
            *@param  number t       当前的时间
            *@param  number b       当前的初始值
            *@param  number c       当前的改变的值
            *@param  number d       当前的改变值所用的时间
            *@return object object  运动曲线函数组合成的对象
            */
            return t*(c/d)+b;
        },
        compatibleRequestAnimationFrame: function() {
            /*
            *compatibleRequestAnimationFrame方法通过对requestAnimationFrame进行兼容性处理
            */
            if (!window.requestAnimationFrame) {
                requestAnimationFrame = function(fn) {
                    setTimeout(fn, 17);
                };    
            }
            if(!window.requestAnimationFrame){
                let lastTime = 0;
                window.requestAnimationFrame = function(callback){
                    let currTime = new Date().getTime();
                    let timeToCall = Math.max(0,16.7-(currTime - lastTime));
                    let timer = window.setTimeout(function(){
                        callback(currTime + timeToCall);
                    },timeToCall);
                    lastTime = currTime + timeToCall;
                    return timer;
                }
            }
            if (!window.cancelAnimationFrame) {
                window.cancelAnimationFrame = function(timer) {
                    clearTimeout(timer);
                };
            }

            window.requestAnimationFrame = window.requestAnimationFrame 
                                          || window.mozRequestAnimationFrame 
                                          || window.webkitRequestAnimationFrame 
                                          || window.msRequestAnimationFrame
                                          || window.oRequestAnimationFrame;
            window.cancelAnimationFrame = window.cancelAnimationFrame
                                          || window.mozCancelAnimationFrame;
        },
        slideToIndex: function() {
            /*
            *slideToIndex方法指定轮播滑动到指定的图片
            *@param  void
            *@return void
            */
            let _slideObjs = this.slideObjs.slice(0, this.index);
            this.totalHeight = this.getTotalHeight(_slideObjs);
            this.stopAnimate = true;
        },
        animate: function() {
            /*
            *animate方法执行轮播的动画
            *@param  void
            *@return void
            */
            let _self = this,
                timer = null;
            this.compatibleRequestAnimationFrame();
            cancelAnimationFrame(timer);
            let duration = 400,
                startTime = new Date().getTime(),
                count = 0,
                position = 0,
                lastSlideObjHeight = _self.slideObjs[(_self.slideObjs.length - 1)].height;
            timer = requestAnimationFrame(function fn(timestamp) {
                let currentTime = new Date().getTime(),
                    currentDuration = currentTime - startTime;
                if (currentDuration >= duration) {
                    position = position - _self.totalHeight;
                    if (_self.stopAnimate) {
                        position = _self.totalHeight;
                    }
                    startTime = currentTime;
                    ++count;
                    duration = _self.easeInOut(count, duration);
                } else {
                    position = _self.linear(currentDuration, 0, _self.totalHeight, duration);
                }
                if (count >= _self.DEFALUT_COUNT) {
                    _self.slideWrap.style.top = -1 * _self.totalHeight + 'px';
                    cancelAnimationFrame(timer);
                    timer = null;
                    return;
                }
                _self.slideWrap.style.top = -1 * position + 'px';
                timer = requestAnimationFrame(fn);
            });
        },
        easeInOut: function(count = 0, duration = 10) {
            /*
            *easeInOut方法通过控制完成每次轮播的时间来控制轮播的速度
            *@param  number defalutCount        默认轮播的次数
            *@param  number count               当前轮播的次数
            */
            let percent = parseInt(this.DEFALUT_COUNT / 3);

            if (count == (this.DEFALUT_COUNT - 1)) {
                this.slideToIndex();
                return duration;
            }
            if (count < percent) {
                return duration * 0.9;
            } else if (count < (2 * percent)) {
                return duration;
            } else if (count < this.DEFALUT_COUNT) {
                return duration * 1.5;
            }
        },
        init: function() {
            /*
            *init方法用于当做启动动画
            *@param  void
            *@return void
            */
            this.animate();
        }
    }

vue代码:

export default {
        data() {
            return {
                images: [],
                loopSlideObjs: [],
                loopContainer: {},
                loopWrapObjs: [],
                animateStyle: {},
                cssRule: {},
                loopWrapNums: 3
            }
        },
        created() {
            this.init();
        },
        mounted() {
            this.initDomData();
        },
        methods: {
            init() {
                this.initData();
            },
            initData() {  //舒适化静态数据和对象
                this.initImagesData();
            },
            initImagesData() {  //初始化图片数据
                this.images = [
                    {'src': '../../static/imgs/waterfull/hy1.jpeg'},
                    {'src': '../../static/imgs/waterfull/hy2.jpg'},
                    {'src': '../../static/imgs/waterfull/hy3.jpg'},
                    {'src': '../../static/imgs/waterfull/hy4.jpg'},
                    {'src': '../../static/imgs/waterfull/hy5.jpg'},
                    {'src': '../../static/imgs/waterfull/hy6.jpg'},
                    {'src': '../../static/imgs/waterfull/hy7.jpg'},
                    {'src': '../../static/imgs/waterfull/hy8.jpg'},
                    {'src': '../../static/imgs/waterfull/hy9.jpg'},
                    {'src': '../../static/imgs/waterfull/hy10.jpg'},
                    {'src': '../../static/imgs/waterfull/hy1.jpeg'}
                ];
            },
            initDomData() {  //初始化dom数据
                this.initSlidesDomData();
                this.initLoopImagesAnimate(0);
                this.compatibleRequestAnimationFrame();
            },
            initSlidesDomData() {  //初始化轮播的每个slide的dom数据
                let liObjs = document.querySelectorAll('.loop-container .loop-wrap.loop-wrap0 .loop-slide') || this.$refs.loopSlides;
                this.loopWrapObjs = document.querySelectorAll('.loop-container .loop-wrap') || this.$refs.loopWrap;
                let obj     = {},
                    item    = {};
                this.loopContainer.totalHeight = 0;
                for (let i = 0, len = liObjs.length; i < len; i++) {
                    item = liObjs[i];
                    obj.index = i;
                    obj.height = item.offsetHeight;
                    this.loopContainer.totalHeight += obj.height;
                    this.loopSlideObjs.push(obj);
                    obj = {};
                    item = {};
                }
                this.print('initDomData', {
                    liObjs: liObjs,
                    loopWrapObjs:this.loopWrapObjs,
                    loopSlideObjs: this.thisloopSlideObjs,
                    loopContainer: this.loopContainer
                });
                obj     = null;
                item    = null;
                liObjs  = null;
            },
            initLoopImagesAnimate(index) {  //初始化轮播的动画
                let _self = this;
                this.startLoopImagesAnimate();
            },
            compatibleRequestAnimationFrame: function() {
                /*
                *compatibleRequestAnimationFrame方法通过对requestAnimationFrame进行兼容性处理
                */
                if (!window.requestAnimationFrame) {
                    requestAnimationFrame = function(fn) {
                        setTimeout(fn, 17);
                    };    
                }
                if(!window.requestAnimationFrame){
                    let lastTime = 0;
                    window.requestAnimationFrame = function(callback){
                        let currTime = new Date().getTime();
                        let timeToCall = Math.max(0,16.7-(currTime - lastTime));
                        let timer = window.setTimeout(function(){
                            callback(currTime + timeToCall);
                        },timeToCall);
                        lastTime = currTime + timeToCall;
                        return timer;
                    }
                }
                if (!window.cancelAnimationFrame) {
                    window.cancelAnimationFrame = function(timer) {
                        clearTimeout(timer);
                    };
                }

                window.requestAnimationFrame = window.requestAnimationFrame 
                                              || window.mozRequestAnimationFrame 
                                              || window.webkitRequestAnimationFrame 
                                              || window.msRequestAnimationFrame
                                              || window.oRequestAnimationFrame;
                window.cancelAnimationFrame = window.cancelAnimationFrame
                                              || window.mozCancelAnimationFrame;
            },
            startLoopImagesAnimate: function() {  //开始轮播图片动画
                let _self       = this,
                    num         = this.loopWrapObjs.length,
                    count       = 0,
                    duration    = 1000,
                    startTime   = new Date().getTime(),
                    timer       = null;
                timer = requestAnimationFrame(function fn() {
                    let currentTime     = new Date().getTime(),
                        currentDuration = currentTime - startTime;
                    if (count >= num) {
                        cancelAnimationFrame(timer);
                        timer = null;
                        return ;
                    }
                    if (currentDuration >= duration) {
                        _self.initImagesLoopObj(_self.loopWrapObjs[count]);
                        startTime = currentTime;
                        ++count;
                    }
                    timer = requestAnimationFrame(fn);
                });
            },
            initImagesLoopObj: function(loopWrapObj) {  //初始化轮播图片的对象
                let imagesLoop = new ImagesLoop({
                    interval: 100,
                    slideObjs: this.loopSlideObjs,
                    slideWrap: loopWrapObj,
                    index: 6
                });
                imagesLoop.init();
            },
            print: function(fnName = 'fn', obj = {}) {  //打印所有的参数
                console.group(fnName);
                Object.keys(obj).forEach((key) => {
                  console.log(key, obj[key]);
                });
                console.groupEnd('end'+fnName);
            }
        }
    }

猜你喜欢

转载自blog.csdn.net/babulongbo/article/details/79917441