起因:

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

分析:

水果抽奖机的动画效果是三个轮播图进行滚动到指定的位置。虽然是三个,但是只要破解一个,将其他动画依次延迟执行就可以了。
分析其中的一个轮播动画,就会发现就是我们平常写的轮播图,只不多这个轮播可以自己进行轮播,并且由快到慢,是一个减速运动,最后停止到指定的轮播图中。
本来想用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) {/**tween是ImagesLoop的原型属性,表示图片容器运动曲线函数*@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);}}}

javascript实现水果抽奖机相关推荐

  1. JavaScript案例之抽奖机

    JavaScript案例之抽奖机 源码: <!DOCTYPE html> <html lang="en"> <head><meta cha ...

  2. 利用JavaScript实现随机抽奖代码

    利用JavaScript实现随机抽奖代码 一.分析: 二.部分重要功能代码: 1.body页面: 2.通过点击事件来显示随机号码: 3.设置中奖号码: 4.判断是否中奖的方法 三.全部代码实现: 图片 ...

  3. 我的世界服务器抽奖系统怎么弄,我的世界自动识别货币抽奖机如何制作

    我的世界是一款很经典的沙盒类游戏,在游戏中红石和命令方块是这部作品的核心,可以制作很多装备和道具,下面给大家分享下我的世界自动识别货币抽奖机如何制作,希望对大家有所帮助. 自动识别货币抽奖机制作方法 ...

  4. Scratch之制作幸运抽奖机

    生活中许多问题的解决需要使用到随机数,比如抽奖,为了公平,必须是随机抽取.Scratch软件的"运算"指令组提供了一个可以产生随机数的指令.本篇文章通过制作"幸运抽奖机& ...

  5. JS写一个图片抽奖机

    1 首先把静态页面代码完成 <div class="box"><div class="box01"><div class=&quo ...

  6. 抽奖机(用随机数)2.0-python

    昨天,我们用除法做了一个原理超超超超超超简单的抽奖机,那今天,我们来用随机数,也就是调用random模块来写一个简单的抽奖机. 首先,要调用random模块,如下: import random 然后, ...

  7. uni-app - 九宫格老虎机抽奖机插件源码(支持服务端API接口控制最终中奖的奖品,自定义组件可随意配置和控制,带组件文档轻松 DIY 自己的营销页抽奖机)幸运抽奖圆形大转盘插件组件

    前言 如果您需要圆形大转盘抽奖机(如下图所示),请访问:这篇文章. 关于九宫格式老虎机宫格跳动抽奖,网上的大部分源码非常乱且无注释,根本无法改造, 本文提供的组件源码,代码干净整洁注释详细,并且配备超 ...

  8. java基础项目-抽奖机-模拟双色球-大乐透

    java基础项目-抽奖机-模拟双色球-大乐透: 创建了很多的方法逐个编译的,做的很新手 不足之处希望多多交流.谢谢. 需求分析: 欢迎界面 欢迎使用超级幸运抽奖机 选彩种(1.双色球:2.超级大乐透) ...

  9. 我的世界服务器信号下面是红,适用于服务器的红石抽奖机我的世界抽奖机电路图...

    循环电路开始运作了就带动计数器的工作. 然后计数器驱动显示器滚动,这个跟时钟电路是一样的.就是把不同信号强度的红石信号分割成单独的信号输出 最后就是随机发生装置,一个循环电路驱动发射器不停发射东西,然 ...

最新文章

  1. Spread for Windows Forms快速入门(10)---绑定到数据库
  2. Java线程之两种方法Runnable和Thread的区别
  3. 机器学习(一)——线性回归、分类与逻辑回归
  4. Python笔记-centos 7安装(升级)pyton到3.6.9及创建独立虚拟环境
  5. Java代码计算一元二次方程
  6. 使用Echarts制作散点图(Excel导入数据方式)
  7. SQL查询学生信息表中的学生记录
  8. CentOS7 原生 yum 源修改为阿里 yum 源
  9. 服务器raid的原理以及怎么恢复数据
  10. 2022苹果AppStore应用商店上传与APP上传流程必看(基础篇)
  11. 查看计算机ip地址配置信息的命令是,查看电脑配置指令大全
  12. Win10怎么卸载.net framework?
  13. php采集所有a标签,dedecms采集去除a标签代码
  14. linux运维工程师的要求,合格的Linux运维工程师应该具备的条件有哪些?
  15. mongodb高可用集群 3 ---分片与副本集结合
  16. 「极客中心」www.geekzl.com网站上线了
  17. 七年级计算机基础知识的试题,七年级信息技术试题
  18. 优艾智合机器人获软银领投新一轮近亿元人民币融资
  19. 富士康转移3000亿产能,iPhone的印度产能倍增,不再“赏饭吃”
  20. 力软快速开发平台推荐功能之表单应用

热门文章

  1. 一篇好文之Android数据库 SQLite全解析
  2. 李宏毅学习笔记35.GAN.06.Tips for Improving GAN
  3. ADO/ADO.NET
  4. p2p断开的两种情况:WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY(4)和WLAN_REASON_DEAUTH_LEAVING(3)
  5. php lumen和laravel,Laravel 还是 Lumen?
  6. 百度迁徙大数据整理[2020+2019同期]
  7. 企业——nginx的图片压缩、https模块、重写规则、盗链
  8. 计算机黑屏论文,在家写论文电脑突然黑屏自救方案,这几个方法可以解决95%以上的电脑问题...
  9. MYSQL 修改语句(数据)
  10. 飞鸽短信平台发送国际短信