1,请准备一个模版,像下面这样
<div class="content">
<div class="hide">
<div class="red-box" data-txt>
<img src="hongbao.png">
</div>
</div>
</div>
.content{position: relative;}
.red-box{position: absolute;}
.red-box img{width:50px; height:auto; cursor: pointer;}
.hide{display: none;}
2,请引入我这里是用jquery制作的,节点操作大同小异
<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
3,我们先做来模拟一下后台返回的数据组织方式吧
// 原始数据
let source = JSON.parse('[{"itemid":"3","lid":"9","fromtime":"1656572514","endtime":"1656579714","nums":"10","prize":"\u906e\u9633\u4f1e","thumb":"","probability":"100","wnums":"0"}]')
// 包装一下
var sourceArr = []
source.forEach(function(v){
let tmpObj = {
ft: v.fromtime * 1000, // 开始时间
et: v.endtime * 1000, // 结束时间 结束时间就是需要控制红包雨要下多久的参数呢
lid: v.lid // 活动id 这个id就是我们需要触发的抽奖活动的信息
}
sourceArr.push(tmpObj)
})
4,因为活动不是每天都有,我们必须有手段去判断今天的日期和活动的日期是否是匹配,非活动日期不做操作,具体请看下图
// 给Date类添加了一个新的实例方法format
// 首先是需要格式化日期
Date.prototype.format = function(fmt) {
let o = {
'M+': this.getMonth() + 1, // 月份
'd+': this.getDate(), // 日
'h+': this.getHours(), // 小时
'm+': this.getMinutes(), // 分
's+': this.getSeconds(), // 秒
'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
S: this.getMilliseconds() // 毫秒
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(this.getFullYear() + '').substr(4 - RegExp.$1.length)
)
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
)
}
}
return fmt
}
// 获取当天和活动日期的时间戳
let now = new Date().getTime()
let nowDate = new Date((new Date(now)).format('yyyy-MM-dd')).getTime() // 格式化为 2022-06-25 获取截止到 日 的时间戳
// 把活动时间单独放到一个taskDate数组,因为活动日期属于前置判断
// ft 为活动开始时间
taskDate.push(
new Date((new Date(v.ft)).format('yyyy-MM-dd')).getTime()
)
// 最后进行当日与活动日的判断即可
Array.from(new Set(taskDate)).includes(nowDate)
至此,关于某天需要执行活动的代码片段结束
5, 下面我们来说一下怎么来执行这个红包雨吧
// 启动多个定时任务
execTimeArr.forEach(function(execV) {
// 计算当前时间是否包含在开始-结束
if (now > execV.ft && now < execV.et) {
execTime = 0 // 立即执行
persistTime = execV.et - now // 已经开始执行那继续执行剩余时间
} else {
execTime = execV - now // 等待
persistTime = execV.et - execV.ft // 未开始则执行 结束 减去 开始的时间
}
let timer = setTimeout(function() {
task(persistTime, execV.lid) // 任务实体 传入需要执行的时间, 传入定时任务需要绑定的 抽奖活动id
}, execTime)
// 这是用来清理 TimeOut 的 队列
timerList.push({
time: execV, // 执行时间
timer: timer // 定时任务
})
})
// 好了,我们再来看下 task 如何工作吧
//启用定时器,循环创建红包雨
function task(persistTime, lid){
// 这个就是结束时间减去开始时间,得到一个次数,可以控制红包雨需要下多久
var count = parseInt(persistTime / 300);
timer = setInterval(function(){
if(count === 0){
clearInterval(timer);
}else{
count--
$('.time').text(count+'s');
createRedBox(lid); // 创建红包
}
}, 300);
}
//创建红包 lid 需要携带的红包id
function createRedBox(lid){
var $newNode = $redBox.clone(true); // 克隆节点
$newNode.attr('data-txt', Math.random()*100); // 分配文本
// 增加红包id的属性
$newNode.attr('data-lid', lid); // 这里是抽奖活动的id
// 随机显示
$newNode.css({
'z-index':zIndex++,
'left':getRandom(basePadding,maxLeftPX)+'px', // 其实这里换translate性能会好
'transform': 'rotate('+getRandom(-30,30)+'deg)' // 随机旋转
});
// 追加节点
$content.append($newNode);
redBoxSpeed($newNode, 5);
}
//设置红包移动速度及销毁时间
function redBoxSpeed($el,time){
// 执行动画
$el.animate({top:'800px',},time*1000,function(){
$el.remove(); // 结束后隐藏红包节点
});
}
// 好了,到此为止,关于红包的怎么生成,生成多少,怎么运动我们都定义好了。包括预留到红包上的id或者各类文本。后面都会用到
6,最后,我们给我们的红包上绑定一个点击事件吧
<div class="dialog" id="dialog">
<p class="title"></p>
</div>
// 这些资源都是可以按照你们自己的需求去做
<link rel="stylesheet" href="http://apps.bdimg.com/libs/jqueryui/1.10.4/css/jquery-ui.min.css">
<link rel="stylesheet" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css">
<script src="http://apps.bdimg.com/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
//将红包绑定事件
function bindEvent(){
$content.on('click','.red-box',function(){
let lid = $(this).data('lid')
// 这里可以是各类的异步操作,然鹅我们的重点不是他,所以我就打个样
// 此处弹窗要增加 ajax ,success 后弹出
$("#dialog").dialog({
autoOpen:false,
modal:true,
hide:{
effect: "explode",
duration: 1000
}
});
$('.title').text('活动id为'+lid);
$("#dialog").dialog('open');
});
}
7,到此,从数据处理到动画制作再到数据提交,我们的逻辑片段已经完成,那么将他们串连起来
// 这里就不再加html和引入的资源
// 给Date类添加了一个新的实例方法format
Date.prototype.format = function(fmt) {
let o = {
'M+': this.getMonth() + 1, // 月份
'd+': this.getDate(), // 日
'h+': this.getHours(), // 小时
'm+': this.getMinutes(), // 分
's+': this.getSeconds(), // 秒
'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
S: this.getMilliseconds() // 毫秒
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(this.getFullYear() + '').substr(4 - RegExp.$1.length)
)
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
)
}
}
return fmt
}
var sourceArr = []
// 测试数据
let source = JSON.parse('[{"itemid":"3","lid":"9","fromtime":"1656572514","endtime":"1656579714","nums":"10","prize":"\u906e\u9633\u4f1e","thumb":"","probability":"100","wnums":"0"}]')
source.forEach(function(v){
let tmpObj = {
ft: v.fromtime * 1000,
et: v.endtime * 1000,
lid: v.lid
}
sourceArr.push(tmpObj)
})
let $content = $('.content');
let $redBox = $('.red-box');
let redContentWidth = $content.width();
let redBoxWidth =$redBox.width();
let basePadding =30 ;
let maxLeftPX=redContentWidth-redBoxWidth-basePadding*2;
let config = {
time: sourceArr // 每天执行时间段
};
let now = new Date().getTime()
let nowDate = new Date((new Date(now)).format('yyyy-MM-dd')).getTime() // 格式化为 2022-06-25 获取截止到 日 的时间戳
let execTimeArr = []
// 取任务当日日期,组织数组
let taskDate = [];
config.time.forEach(function(v) {
taskDate.push(
new Date((new Date(v.ft)).format('yyyy-MM-dd')).getTime()
)
// 结束时间超过当前时间不计入不再执行
if (v.et > now) {
execTimeArr.push(v)
}
})
// 执行总任务
let timerList = [] // 定时器队列
// Array.from(new Set(taskDate)).includes(nowDate) 判断当天日期是否包含在后台返回的接口日期中
if (execTimeArr.length > 0 && Array.from(new Set(taskDate)).includes(nowDate)) {
// 启动多个定时任务
execTimeArr.forEach(function(execV) {
// 计算当前时间是否包含在开始-结束
if (now > execV.ft && now < execV.et) {
execTime = 0 // 立即执行
persistTime = execV.et - now // 已经开始执行那继续执行剩余时间
} else {
execTime = execV - now // 等待
persistTime = execV.et - execV.ft // 未开始则执行 结束 减去 开始的时间
}
let timer = setTimeout(function() {
task(persistTime, execV.lid) // 任务实体 传入需要执行的时间, 传入定时任务需要绑定的 抽奖活动id
}, execTime)
// 这是用来清理 TimeOut 的 队列
// 当 task 中 count === 0 逻辑 成立,获取timerList中lid对应的那一项将TimeOut清理
timerList.push({
lid: execV.lid, // 活动id
timer: timer // 定时任务
})
})
}
//启用定时器,循环创建红包雨
function task(persistTime, lid){
// count 由 总时间 / 300
var count = parseInt(persistTime / 300);
timer = setInterval(function(){
if(count === 0){
clearInterval(timer);
// 清理 TimeOut
if (timerList.length > 0) {
timerList.forEach(function(l) {
if (l.lid == lid) {
clearTimeout(l.timer)
}
})
}
}else{
count--
$('.time').text(count+'s');
createRedBox(lid);
}
}, 300);
}
var zIndex= 1;
var timer;
function bindEvent(){
//将红包绑定事件
$content.on('click','.red-box',function(){
let lid = $(this).data('lid')
// 此处弹窗要增加 ajax ,success 后弹出
$("#dialog").dialog({
autoOpen:false,
modal:true,
hide:{
effect: "explode",
duration: 1000
}
});
$('.title').text('活动id为'+lid);
$("#dialog").dialog('open');
});
}
//设置移动及旋转的距离,避免溢出屏幕
function getRandom(min,max) {
return Math.round(Math.random() * (max - min) + min);
}
//设置红包移动速度及销毁时间
function redBoxSpeed($el,time){
// 执行动画
$el.animate(
{
top:'800px',
},
time*1000,
function(){
$el.remove(); // 结束后隐藏红包节点
}
);
}
//创建红包 lid 需要携带的红包id
function createRedBox(lid){
var $newNode = $redBox.clone(true); // 克隆节点
$newNode.attr('data-txt', Math.random()*100); // 分配文本
// 增加红包id的属性
$newNode.attr('data-lid', lid);
// 随机显示
$newNode.css({
'z-index':zIndex++,
'left':getRandom(basePadding,maxLeftPX)+'px',
'transform': 'rotate('+getRandom(-30,30)+'deg)'
});
// 追加节点
$content.append($newNode);
redBoxSpeed($newNode, 5);
}
bindEvent()
8,效果在这:https://v.youku.com/v_show/id_XNTg4MzE2NzkyMA==.html