动画动效之红包雨
思路
在页面顶部之上,放置固定数量的红包,每秒随机降落5-10个,降落时给每个红包添加各自的动画,红包降落到页面底部时消失,降落固定时间。
效果图
H5
1.生成红包
ViewModel.packetNum
是红包总数量,这里我是一共200个红包,每秒随机降落5-10个,一共20秒。
srcList
放置红包显示图片,可以多个,生成红包数据时,会随机从srcList中获取
packet.top
红包是定位在页面顶部之上的,所以下方packet.top是-80(样式布局中红包高度80,可以根据实际情况替换)。
packet.left
为了保证红包随机降落的位置不一样,所以定位时packet.left也是随机生成的,但是要保证在页面显示范围之内.
speed
生成随机掉落时间,保证每个掉落时间保持在3秒到5.5秒之间,时间可以根据实际需求进行修改。掉落时间不一样,但是掉落高度是一样的,这样就会让红包一快一慢,看着更加真实。
initList: function(callback) { var that = window; var ViewModel = that.activeRedData.ViewModel; //建立临时红包列表 var packetList = []; //建立临时红包图片数组 var srcList = [ViewModel.imgurl, ViewModel.imgurl]; //生成初始化红包 for (var i = 0; i < ViewModel.packetNum; i++) { // 生成随机位置(水平位置) var left = Math.random() * document.body.clientWidth - 20; // 优化位置,防止红包越界现象,保证每个红包都在屏幕之内 if (left < 0) { left += 20; } else if (left > document.body.clientWidth) { left -= 20; } // 建立临时单个红包 var packet = { src: srcList[Math.ceil(Math.random() * 2) - 1], top: -80, left: left, speed: Math.random() * 2500 + 1000 //生成随机掉落时间,保证每个掉落时间保持在3秒到5.5秒之间 } // 将单个红包装入临时红包列表 packetList.push(packet); } // 将生成的临时红包列表更新至页面数据,页面内进行渲染 ViewModel.packetList = packetList; activeRedMethods.renderList(callback); },
2.渲染红包
上面生成了红包数据packetList,然后在把packetList数据渲染到页面,这里就不贴代码了。(最后页面底部会放置所有代码)
3.设置计时器,控制每秒红包下落
showInter
20秒计时器,
showNum
当前掉落红包的个数
startAnimation
红包掉落动画方法,
setInter: function(callback){ var that = window; var ViewModel = that.activeRedData.ViewModel; var packetList = ViewModel.packetList; // 初始化动画执行当前索引 var tempIndex = 0; var time = 0; // 开始定时器,每隔1秒掉落一次红包 that.activeRedData.ViewModel.showInter = setInterval(function() { // 生成当前掉落红包的个数,5-10个 var showNum = Math.ceil(Math.random() * 10); if(showNum<5){ showNum = 5; } // 防止数组越界 if (time>=20) { // 20秒之后,清除定时器 clearInterval(that.activeRedData.ViewModel.showInter); if(callback){ callback(); } } else { for (var i = 0; i < showNum; i++) { var idx = tempIndex + i; activeRedMethods.startAnimation(idx); } tempIndex += showNum; } time += 1; }, 1000) },
4.设置红包下落动效
这里的布局是,每个红包都是一个div内有一个红包img。
所以这里红包下落动效就是外层div添加divanimation,从顶部平移到底部,
内存图片通过rotate旋转,通过speed下落时间控制旋转度数,每秒旋转360度。
startAnimation: function(idx){ var that = window; var ViewModel = that.activeRedData.ViewModel; var packetList = ViewModel.packetList; var cla = ".view" + idx; var anTime = parseInt(packetList[idx].speed) / 1000; var str = "divtranslate "+anTime+"s linear 1"; var cla1 = ".red-packet" + idx; var rotateNum = anTime * 360; var strNum = "rotate("+rotateNum+"deg)"; var strTime = anTime + "s linear" $(cla1).css("transform",strNum).css("transition",strTime); $(cla).css("animation",str); },
.divanimation { animation: divtranslate 3s linear 1; } @keyframes divtranslate { 0% { transform: translateY(0); } 100% { transform: translateY(100vh); } }
至此,红包雨开发完成。
5.代码
<!-- 红包雨 --> <div class="redpacketmaskview"></div> <div class="redpacketview"></div>
/* 红包雨 */ .redpacketmaskview { width: 100%; height: 100%; position: fixed; left: 0; top: 0; background-color: rgba(0, 0, 0, 1); z-index: 999; display: none; } .redpacketview { box-sizing: border-box; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; overflow: hidden; display: none; } .redview { width: 1.01rem; height: 1.21rem; } .red-packet { width: 1.01rem; height: 1.21rem; } .divanimation { animation: divtranslate 3s linear 1; } @keyframes divtranslate { 0% { transform: translateY(0); } 100% { transform: translateY(100vh); } }
var activeRedData = { ViewModel: { imgurl: "images/popup/redpacket.png", windowWidth: "", //窗口宽度 windowHeigh: "750", //窗口高度 packetList: [{}], //红包队列 packetNum: 200, //总共红包的数量 showInter: '' // 循环动画定时器 } } var activeRedMethods = { initList: function(callback) { var that = window; var ViewModel = that.activeRedData.ViewModel; //建立临时红包列表 var packetList = []; //建立临时红包图片数组 var srcList = [ViewModel.imgurl, ViewModel.imgurl]; //生成初始化红包 for (var i = 0; i < ViewModel.packetNum; i++) { // 生成随机位置(水平位置) var left = Math.random() * document.body.clientWidth - 20; // 优化位置,防止红包越界现象,保证每个红包都在屏幕之内 if (left < 0) { left += 20; } else if (left > document.body.clientWidth) { left -= 20; } // 建立临时单个红包 var packet = { src: srcList[Math.ceil(Math.random() * 2) - 1], top: -80, left: left, speed: Math.random() * 2500 + 1000 //生成随机掉落时间,保证每个掉落时间保持在3秒到5.5秒之间 } // 将单个红包装入临时红包列表 packetList.push(packet); } // 将生成的临时红包列表更新至页面数据,页面内进行渲染 ViewModel.packetList = packetList; activeRedMethods.renderList(callback); }, renderList: function(callback){ var that = window; var ViewModel = that.activeRedData.ViewModel; var packetList = ViewModel.packetList; var html = ''; for (var i = 0; i < packetList.length; i++) { var cla = "view" + i; var cla1 = "red-packet" + i; html += '<div class="redview '+cla+'" style="position:fixed;top:'+packetList[i].top+'px;left:'+packetList[i].left+'px;" onclick="activeRedMethods.onClickRedPacketBtn()">'; html += '<img class="red-packet '+cla1+'" src="'+packetList[i].src+'"/>'; html += '</div>'; } $(".redpacketview").html(html); activeRedMethods.setInter(callback); }, //计时器,设置动画 setInter: function(callback){ var that = window; var ViewModel = that.activeRedData.ViewModel; var packetList = ViewModel.packetList; // 初始化动画执行当前索引 var tempIndex = 0; var time = 0; // 开始定时器,每隔1秒掉落一次红包 that.activeRedData.ViewModel.showInter = setInterval(function() { // 生成当前掉落红包的个数,1-3个 var showNum = Math.ceil(Math.random() * 10); if(showNum<5){ showNum = 5; } // 防止数组越界 if (time>=20) { // 20秒之后,清除定时器 clearInterval(that.activeRedData.ViewModel.showInter); if(callback){ callback(); } } else { for (var i = 0; i < showNum; i++) { var idx = tempIndex + i; activeRedMethods.startAnimation(idx); } tempIndex += showNum; } time += 1; }, 1000) }, startAnimation: function(idx){ var that = window; var ViewModel = that.activeRedData.ViewModel; var packetList = ViewModel.packetList; var cla = ".view" + idx; var anTime = parseInt(packetList[idx].speed) / 1000; var str = "divtranslate "+anTime+"s linear 1"; var cla1 = ".red-packet" + idx; var rotateNum = anTime * 360; var strNum = "rotate("+rotateNum+"deg)"; var strTime = anTime + "s linear" $(cla1).css("transform",strNum).css("transition",strTime); $(cla).css("animation",str); }, //红包点击 onClickRedPacketBtn:function(){ //这里是点击红包的事件,处理自己的逻辑。 }, }
小程序
小程序这边整体思路和代码和H5基本一样,区别就是,小程序封装成组件,实现动画的方式通过小程序动画去实现的。
话不多说,直接上代码。
wxml
<view class="maskview"></view> <view class="redpacketview"> <block wx:for="{{packetList}}" wx:for-index="index" wx:for-item="items"> <view class="redview view{{index}}" style="position:fixed;top:{{items.top}}px;left:{{items.left}}px;"> <image class="red-packet red-packet{{index}}" src="{{items.src}}" > </image> </view> </block> </view>
wxss
page{ width: 100%; height: 100%; } .maskview { width: 100%; height: 100%; position: fixed; left: 0; top: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 999; } .redpacketview{ box-sizing: border-box; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; overflow: hidden; } .redview{ width: 96rpx; height: 131rpx; } .red-packet{ width: 96rpx; height: 131rpx; }
js
Component({ /** * 组件的属性列表 */ properties: {}, /** * 组件的初始数据 */ data: { imgurl: "https://www.jsdaima.com/Uploads/js/201811/1542602969/img/hb.png", windowWidth: "", //窗口宽度 windowHeigh: "", //窗口高度 packetList: [{}], //红包队列 packetNum: 200, //总共红包的数量 showInter: '' // 循环动画定时器 }, attached: function() { // 在组件实例进入页面节点树时执行 this.initList(); }, /** * 组件的方法列表 */ methods: { //初始化红包列表 initList: function() { var that = this; // 获取手机屏幕宽高 wx.getSystemInfo({ success: function(res) { that.setData({ windowWidth: res.windowWidth, windowHeigh: res.windowHeight, top: res.windowHeight - 100 //设置红包初始位置 }) } }) //建立临时红包列表 var packetList = []; //建立临时红包图片数组 var srcList = [this.data.imgurl, this.data.imgurl]; //生成初始化红包 for (var i = 0; i < that.data.packetNum; i++) { // 生成随机位置(水平位置) var left = Math.random() * that.data.windowWidth - 20; // 优化位置,防止红包越界现象,保证每个红包都在屏幕之内 if (left < 0) { left += 20; } else if (left > that.data.windowWidth) { left -= 20; } // 建立临时单个红包 var packet = { src: srcList[Math.ceil(Math.random() * 2) - 1], top: -80, left: left, speed: Math.random() * 2500 + 1000 //生成随机掉落时间,保证每个掉落时间保持在3秒到5.5秒之间 } // 将单个红包装入临时红包列表 packetList.push(packet); } // 将生成的临时红包列表更新至页面数据,页面内进行渲染 that.setData({ packetList: packetList }) that.setInter(); }, //计时器,设置动画 setInter: function(){ var that = this; var packetList = that.data.packetList; // 初始化动画执行当前索引 var tempIndex = 0; var time = 0; // 开始定时器,每隔1秒掉落一次红包 that.data.showInter = setInterval(function() { // 生成当前掉落红包的个数,1-3个 var showNum = Math.ceil(Math.random() * 10); if(showNum<5){ showNum = 5; } // 防止数组越界 if (time>=20) { // 20秒之后,清除定时器 clearInterval(that.data.showInter); } else { for (var i = 0; i < showNum; i++) { var idx = tempIndex + i; that.startAnimation(idx); } tempIndex += showNum; } time += 1; }, 1000) }, //开始动画 startAnimation: function(idx) { var that = this; var packetList = that.data.packetList; var cla = ".red-packet" + idx; var viewcla = ".view" + idx; var antime = parseInt(packetList[idx].speed); var list = [ { rotate: 0, }, { rotate: antime / 1000 * 360 } ]; this.animate(cla, list, antime, function() { this.clearAnimation(cla, {}, null) }.bind(this)) this.animate(viewcla, [ { translateY:0 }, { translateY: 800 }, ], antime, function() { this.clearAnimation(viewcla, {}, null) }.bind(this)) }, } })