起因:
公司每个月都要做推广活动,很多推广活动都需要抽奖,但是以前的抽奖的特效太简单,于是美工看到京东的年会抽奖机,我就不得不走向逆向仿制的道路上,经过三天的攻克,终于实现了抽奖效果。
分析:
水果抽奖机的动画效果是三个轮播图进行滚动到指定的位置。虽然是三个,但是只要破解一个,将其他动画依次延迟执行就可以了。
分析其中的一个轮播动画,就会发现就是我们平常写的轮播图,只不多这个轮播可以自己进行轮播,并且由快到慢,是一个减速运动,最后停止到指定的轮播图中。
本来想用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