个人主页:个人主页
推荐专栏:小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏
!从个人到商业的全套开发教程
,实打实的干货分享,确定不来看看? 】
作者简介:一个读过研、创过业,从全栈工程师转行做大数据开发的大厂人!
⭐️您的小小关注是我持续输出的动力!
⭐️
入门和进阶小程序开发,不可错误的精彩内容 :
- 《你真的会做小程序按钮吗?看了字节35K前端的样式设计,悟了》
- 《来接私活吧?小程序接私活必备功能-婚恋交友【附完整代码】》
在众多的营销场景中,红包雨可以说是很百试不爽的套路了。对于红包雨,从表面来看就是简单地让用户点击并且给用户福利的形式。
但是,在设计红包雨的背后,前后端都可以深挖到很多的技术栈。
在本文,先研究小程序端的红包雨特效以及用户的交互逻辑
.
其中主要交互功能包括:
transform
属性的使用【控制红包进行角度旋转】:值 | 描述 |
---|---|
rotate(angle) | 定义 2D 旋转,在参数中规定角度。 |
rotate3d(x,y,z,angle) | 定义 3D 旋转。 |
rotateX(angle) | 定义沿着 X 轴的 3D 旋转。 |
rotateY(angle) | 定义沿着 Y 轴的 3D 旋转。 |
rotateZ(angle) | 定义沿着 Z 轴的 3D 旋转。 |
动画animation
属性的使用【控制红包】:
推荐学习:通过示例深入学习animation属性
(2)JavaScript定时器的使用
Math.random
生成红包随机旋转的角度,使得红包掉落的效果更加自然。再使用定时器功能使得红包动画循环播放。 startRedPacket() {
let win = 0
// 获取屏幕宽度
uni.getSystemInfo({
success: function(res) {
win = res.windowWidth
}
});
// let win = document.documentElement.clientWidth || document.body.clientWidth
let rotate = (parseInt(Math.random() * 90 - 45)) + "deg" // 旋转角度
let w = (Math.random() * 90) + 120
let durTime = parseInt(Math.random() * 1.5) + 2.5 + 's' // 时间
let left = parseInt(Math.random() * win)
if (left < 0) {
left = 0
} else if (left > (win - w)) {
left = (win + 65)
}
this.liParams.push({
left: left + 'rpx',
width: w + 'rpx',
transforms: 'rotate(' + rotate + ')',
durTime: durTime,
status: 0 // 0 默认 1 中奖 2 未中奖
})
setTimeout(() => {
// 多少时间结束
clearTimeout(this.timer)
return false
}, this.duration * 1000)
// 红包密度
this.timer = setTimeout(() => {
this.startRedPacket()
}, 300)
},
if (this.selectedNum >= 3 && item.status == 0) {
item.status = 2
// return false
}
if (this.clickNum == this.randomNum) {
if (item.status == 0 && this.selectedNum < 3) {
this.randomNum = Math.ceil(Math.random() * 6)
this.clickNum = 0
this.selectedNum++
item.status = 1 // 随机出现只中奖
this.acquisitionNum++
}
} else {
item.status = 2
}
代码中通过status
属性实现红包中奖状态的控制:
参数值 | 中奖情况 |
---|---|
0 | 空红包 |
1 | 未中奖 |
2 | 已中奖 |
<template>
<view class="rainBox">
<view class="countDown" v-if="secondMask">
<img class="second" v-show="second==3" src="https://img-blog.csdnimg.cn/9a535df6885e49a597087f3db7145640.png">
<img class="second" v-show="second==2" src="https://img-blog.csdnimg.cn/223a369d90744e06ac772eac1479f5cc.png">
<img class="second" v-show="second==1" src="https://img-blog.csdnimg.cn/a2a5909e79314903855b3774bad7ec11.png">
view>
<view v-if="!secondMask">
<view class="redNum">
<text class="icon">{{duration}}stext>
<text class="winnum">当前抢到红包:<text class="redmoney">{{acquisitionNum}}text>元text>
view>
<view class="red_packet" id="red_packet">
<view v-for="(item, index) in liParams">
<view class="package" :style="{ left: item.left,width:item.width, height:item.width, animationDuration: item.durTime, webkitAnimationDuration: item.durTime}"
:data-index="index" @webkitAnimationEnd="removeDom" @click="tap(item)">
<text :style="{ width:item.width, height:item.width,transform: item.transforms, webkitTransform: item.transforms}"
:class="[{ 'defaul':item.status==0},{'success':item.status==1},{'fail':item.status==2}]">text>
view>
view>
<view v-if="show1==true" @click="show = false">
<view class="warp flexcenter">
<view class="rect" @tap.stop :style="{background: 'url(https://ctyh88.oss-cn-shenzhen.aliyuncs.com/static/puzzle/tanchuhongbao.png)no-repeat center center / 100%'}">
<view class="rectcenter">
<view class="rt-money">{{acquisitionNum}}元view>
<view class="rt-money1">已存余额view>
view>
<view class="btnredbg" @click="show1 = false">我知道了view>
<view class="iconfil">
<u-icon name="close-circle-fill" color="#b39b8f" size="40" class="iconfil" @click="show1 = false">u-icon>
view>
view>
view>
view>
view>
view>
view>
template>
<script>
export default {
// components: { noPrize },
data() {
return {
second: 3, // 倒计时
secondMask: true, //倒计时弹层
liParams: [], // 红包数组
timer: null,
duration: 10, // 持续时间
selectedNum: 0, // 选中红包个数,不超过3个
clickNum: 0, // 点击的次数
randomNum: Math.ceil(Math.random() * 6), // 1~6 随机数
couponArr: [],
acquisitionNum: 0, // 获得红包个数
show1: false, // 获取红包弹窗
}
},
created() {
this.countDownFn()
},
methods: {
// 5秒倒计时
countDownFn() {
let self = this
let timer = setInterval(() => {
if (self.second == 0) {
self.secondMask = false
clearInterval(timer)
self.startRedPacket()
self.countDownFn20()
} else {
self.second--
}
}, 1000)
},
// 20秒倒计时
countDownFn20() {
let self = this
let timer = setInterval(() => {
if (self.duration == 0) {
clearInterval(timer)
this.show1 = true
console.log('结束')
} else {
self.duration--
}
}, 1000)
},
tap(item) {
this.clickNum++
if (this.selectedNum >= 3 && item.status == 0) {
item.status = 2
// return false
}
if (this.clickNum == this.randomNum) {
if (item.status == 0 && this.selectedNum < 3) {
this.randomNum = Math.ceil(Math.random() * 6)
this.clickNum = 0
this.selectedNum++
item.status = 1 // 随机出现只中奖
this.acquisitionNum++
}
} else {
item.status = 2
}
},
startRedPacket() {
let win = 0
// 获取屏幕宽度
uni.getSystemInfo({
success: function(res) {
win = res.windowWidth
}
});
let rotate = (parseInt(Math.random() * 90 - 45)) + "deg" // 旋转角度
let w = (Math.random() * 90) + 120
let durTime = parseInt(Math.random() * 1.5) + 2.5 + 's' // 时间
let left = parseInt(Math.random() * win)
if (left < 0) {
left = 0
} else if (left > (win - w)) {
left = (win + 65)
}
this.liParams.push({
left: left + 'rpx',
width: w + 'rpx',
transforms: 'rotate(' + rotate + ')',
durTime: durTime,
status: 0 // 0 默认 1 中奖 2 未中奖
})
setTimeout(() => {
// 多少时间结束
clearTimeout(this.timer)
return false
}, this.duration * 1000)
// 红包密度
this.timer = setTimeout(() => {
this.startRedPacket()
}, 300)
},
removeDom(e) {
this.package1=true
this.package=false
// let target = e.currentTarget
// document.querySelector('#red_packet').removeChild(target)
}
}
}
script>
<style lang="less" scoped>
// 红包弹窗
.warp {
width: 100%;
height: 100vh;
overflow: hidden;
.rect {
width: 672rpx;
height: 840rpx;
margin: auto;
.rectcenter {
height: 554rpx;
text-align: center;
.rt-money {
font-size: 65rpx;
font-weight: 800;
padding: 80rpx 0;
color: rgba(238, 195, 24, 1);
}
.rt-money1 {
font-size: 30rpx;
font-family: PingFang SC;
font-weight: 500;
color: rgba(238, 195, 24, 1);
padding: 20rpx 0;
}
}
.btnredbg {
width:370rpx;
height:80rpx;
background:rgba(255,211,125,1);
border-radius:40rpx;
line-height: 80rpx;
margin: auto;
color: #F42335;
font-size: 32rpx;
text-align: center;
}
.iconfil{
width:77rpx;
margin: 60rpx auto;
}
}
}
.rainBox {
margin: 0;
padding: 0;
position: relative;
width: 100%;
height: 92vh;
overflow: hidden;
background: url(https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.888ppt.com%2F%2FUpload%2Fthumb%2F20191014%2F781a066454151825.jpg%21h400&refer=http%3A%2F%2Fpic.888ppt.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1670587612&t=6f6843e0025442cf321946c480f82cbc) no-repeat center;
background-size: 100%;
.winnum {
display: inline-block;
color: #fff;
font-size: 40rpx;
padding: 60rpx;
.redmoney {
color: #FED134;
font-weight: bold;
padding-right: 10rpx;
}
}
.icon {
display: inline-block;
width: 82rpx;
height: 82rpx;
background: #6C0A75;
font-size: 60rpx;
line-height: 92rpx;
text-align: center;
position: absolute;
top: 40rpx;
right: 40rpx;
z-index: 4;
background: url(https://img.51fanbei.com/h5/app/activity/redRain_08.png) no-repeat center;
background-size: 100% 100%;
color: #eec318;
font-weight: bolder;
i {
font-size: 24rpx;
}
}
}
.countDown {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 2;
overflow: hidden;
background: url(https://img.51fanbei.com/h5/app/activity/redRain_02.png) no-repeat center;
background-size: 100% 100%;
.second {
width: 340rpx;
height: 394rpx;
margin: 410rpx auto;
display: block;
// margin-top: 205px;
}
}
.red_packet {
text {
width: 250rpx;
height: 250rpx;
display: block;
&.defaul {
background: url(https://img1.baidu.com/it/u=936677898,247021280&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=749) no-repeat center;
background-size: 100% 100%;
}
&.fail {
background: url(https://img-blog.csdnimg.cn/fe18340b8fbe4a4a80694b1b2feabc2a.png) no-repeat center;
background-size: 100% 100%;
}
&.success {
background: url(https://img-blog.csdnimg.cn/1e957a43234f4175bc537fb9770de5ea.png) no-repeat center;
background-size: 100% 100%;
}
}
.package1 {
display: none;
}
.package {
position: absolute;
animation: all 3s linear;
top: -200rpx;
z-index: 3;
animation: aim_move 5s linear 1 forwards;
// &.package {
// }
}
a {
display: block;
}
}
@keyframes aim_move {
0% {
transform: translateY(0);
}
100% {
transform: translateY(120vh);
}
}
style>