一、原型依据

在我这个项目中小程序端所需要实现的只有红包雨的下落动画和通屏背景图的兼容,关于红包点击金额的计算是由后端实现的。首先来看下需要实现的效果图。





二、实现代码

首先是第一次进入的页面,在这个页面的时候会进行静默登录。静默登录成功的话会显示当前剩余次数,否则在点击开始的时候回跳转登录页面。不论是否登录成功都会去调用活动信息监测的接口,判断当前是否有可以参加的活动。在点击开始后进行三秒钟的倒计时,倒计时结束则转入下一个界面实现红包雨的下落。

<!--RedEnvelopes/pages/RedEnvelopes/index/index.wxml-->
<nav-bar navbar-data='{{nvabarData}}'></nav-bar>
<view style='padding-top: {{height}}px;' wx:if="{{ready}}"><block wx:if="{{!showTime}}"><view class="start" bindtap="participateIn" wx:if="{{readyTime != 0}}"><view class="start-aperture"><view class="start-aperture-bg"><text class="start-aperture-txt" wx:if="{{!showTime}}">开始</text><text class="start-aperture-txt" wx:else>{{readyTime}}</text></view></view></view></block><block wx:else><view class="start" wx:if="{{readyTime != 0}}"><view class="start-aperture"><view class="start-aperture-bg"><text class="start-aperture-txt" wx:if="{{!showTime}}">开始</text><text class="start-aperture-txt" wx:else>{{readyTime}}</text></view></view></view></block><view class="remaTimes" wx:if="{{showLogin && !showTime}}">今日剩余次数: {{RemainingTimes}}/{{MaxSharingAwardTimes}}</view><view class="rule" wx:if="{{!showTime}}"><view class="rule-top"><text class="iconfont iconxiangxia2"></text></view><view class="rule-body"><view class="rule-body-one"><view class="rule-body-title">规则:</view><view class="rule-body-html">{{ActivityRules}}</view></view></view></view>
</view>
<!-- 红包雨组件 -->
<block wx:if="{{readyTime == 0}}"><sol-packet-rain visible="{{visible}}" createSpeed="{{createSpeed}}" time="{{time}}" min="{{min}}" max="{{max}}" bind:finish="success"></sol-packet-rain>
</block>
// RedEnvelopes/pages/RedEnvelopes/index/index.js
const api = require('../../../../server/api.js');
const http = require('../../../../server/request.js');
const appJs = require('../../../../utils/uselogn.js');
const app = getApp()
let readyTimer = null
Page({/*** 页面的初始数据*/data: {// 导航头组件所需的参数nvabarData: {showCapsule: 1, //是否显示左上角图标   1表示显示    0表示不显示title: '抢红包', //导航栏 中间的标题white: true, // 是就显示白的,不是就显示黑的。address: api.pictureServer + '/res/shopImg/RedEnvelopes.png' // 加个背景 不加就是没有},// 导航头的高度height: app.globalData.height * 2 + 20,readyTime: 3,showTime: false,btnText: '获取验证码',phone: '',VerificationCode: '',unbind: false,showLogin: true,ready: false,tell: '',redId: '', //抢红包活动标识BackgroundMap: '', //活动背景图ThemeMap: '', //活动主题图ActivityRules: '', //活动规则ShareDescription: '', //分享描述ShareIcon: '', //分享图标MaxSharingAwardTimes: '0',RemainingTimes: '0'},onLogin: appJs.userLogin,/*** 生命周期函数--监听页面加载*/onLoad: function(options) {if (options.refer && options.ShareMemberId) {let SharedMemberId = wx.getStorageSync('CustomerService').MemberId;let parm = {Id: options.refer,ShareMemberId: options.ShareMemberId,SharedMemberId: SharedMemberId}this.getRedEnvelopeLotteryShare(parm)let white = 'nvabarData.white'this.setData({[white]: false})}},/*** 生命周期函数--监听页面初次渲染完成*/onReady: function() {},/*** 生命周期函数--监听页面显示*/onShow: function() {this.setData({ready: true,showTime: false,readyTime: 3})this.onLogin(this.authCallback, this.authCallback1);},authCallback() {let that = this// 获取当前时间抢红包信息that.getGrabRed();that.setData({showLogin: true})},authCallback1() {let that = this// 获取当前时间抢红包信息that.getGrabRed();that.setData({showLogin: false})},/*** 生命周期函数--监听页面隐藏*/onHide: function() {},/*** 生命周期函数--监听页面卸载*/onUnload: function() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh: function() {},/*** 页面上拉触底事件的处理函数*/onReachBottom: function() {},/*** 用户点击右上角分享*/onShareAppMessage: function() {},// 开始准备倒计时cultdown: function() {let _this = thislet {readyTime} = this.datareadyTimer = setInterval(function() {if (--readyTime <= 0) {clearInterval(readyTimer)// 显示红包雨_this.run();}_this.setData({readyTime: readyTime})}, 1000)},start: function() {if (!this.data.showLogin) {setTimeout(() => {wx.navigateTo({url: '/pages/member/loginAndRegister/loginAndRegister',})}, 500)return}if (this.data.RemainingTimes == 0){return}if (!this.data.showTime) {this.cultdown();this.setData({showTime: true})}},run: function() {let address = 'nvabarData.address'this.setData({visible: true,createSpeed: 5, // 速度time: 10, // 游戏时间min: 1, // 金币最小是0max: 1, // 金币最大是10[address]: this.data.ThemeMap || api.pictureServer + '/res/shopImg/RedEnvelopes1.png'})clearInterval(this.readyTimer);},// 结束success(e) {let that = this;let redId = this.data.redId;console.log('bind:finish', e.detail)let RedEnvelopeNum = e.detail;// 中奖let parm = {Id: redId,RedEnvelopeNum: RedEnvelopeNum}http.requestLoading('/api/services/app/RedEnvelope/ExcuteGrabRedEnvelope', parm, '', 'POST').then(res => {if (res.data.Result.Code === 0) {console.log(res)that.setData({visible: false //  隐藏界面})wx.navigateTo({url: '/RedEnvelopes/pages/RedEnvelopes/WinningRedEnvelope/WinningRedEnvelope?Id=' + res.data.Result.Data.Id + '&PartId=' + res.data.Result.Data.PartId + '&ShareDescription=' + that.data.ShareDescription + '&ShareIcon=' + that.data.ShareIcon})}})},// 立即参与participateIn() {let that = this;let redId = this.data.redId;let parm = {Id: redId}http.requestLoading('/api/services/app/RedEnvelope/GetGrabRedEnvelopeAppSingle', parm, '', 'GET').then(res => {if (res.data.Result.Code === 0) {console.log(res)if (redId != res.data.Result.Data.Id) {wx.showToast({title: '当前活动信息不符',icon: 'none'})} else {that.setData({redId: res.data.Result.Data.Id})// 验证会员是否可以参加该活动let parms = {Id: res.data.Result.Data.Id,RedEnvelopeNum: 0}if (!this.data.showLogin) {setTimeout(() => {wx.navigateTo({url: '/pages/member/loginAndRegister/loginAndRegister',})}, 500)return}http.requestLoading('/api/services/app/RedEnvelope/DrawGrabRedEnvelope', parms, '', 'POST').then(res => {if (res.data.Result.Code === 0) {that.setData({ready: true})that.start();}})}}})},// 获取当前时间抢红包信息getGrabRed: function(redId) {let that = this;let address = 'nvabarData.address'http.requestLoading('/api/services/app/RedEnvelope/GetGrabRedEnvelopeAppSingle', {}, '', 'GET').then(res => {if (res.data.Result.Code === 0) {console.log(res)that.setData({redId: res.data.Result.Data.Id,BackgroundMap: res.data.Result.Data.BackgroundMap,ThemeMap: res.data.Result.Data.ThemeMap, //活动主题图ActivityRules: res.data.Result.Data.ActivityRules, //活动规则ShareDescription: res.data.Result.Data.ShareDescription, //分享描述ShareIcon: res.data.Result.Data.ShareIcon, //分享图标[address]: res.data.Result.Data.BackgroundMap})if (that.data.showLogin) {that.getRemainingTimes();}}})},// 分享得额外抽奖次数getRedEnvelopeLotteryShare(parm) {http.requestLoading('/api/services/app/RedEnvelope/RedEnvelopeLotteryShare', parm, '', 'POST').then(res => {if (res.data.Result.Code === 0) {console.log(res)}})},// 获取总次数getRemainingTimes() {let that = this;let parm = {Id: this.data.redId}http.requestLoading('/api/services/app/RedEnvelope/GetRemainingTimes', parm, '', 'GET').then(res => {if (res.data.Result.Code === 0) {console.log(res)that.setData({MaxSharingAwardTimes: res.data.Result.Data.MaxSharingAwardTimes,RemainingTimes: res.data.Result.Data.RemainingTimes})}})}
})
/* RedEnvelopes/pages/RedEnvelopes/index/index.wxss */
page {position: relative;
}.login {width: 626rpx;height: 554rpx;background: #fff;border-radius: 10rpx;position: absolute;top: 50%;left: 0;right: 0;margin: auto;
}.login-center {width: 501rpx;height: 138rpx;border-bottom: 1rpx solid #e2e2e2;margin: auto;display: flex;align-items: center;
}.userIpt {font-size: 28rpx;font-family: Source Han Sans CN Regular, Source Han Sans CN Regular-Regular;font-weight: 400;color: #999;
}.posion {position: relative;
}.zc_view {position: absolute;right: 0;top: 40rpx;width: 170rpx;height: 52rpx;line-height: 52rpx;margin-left: 12rpx;border: 1rpx solid #e2e2e2;text-align: center;font-size: 24rpx;border-radius: 10rpx;
}.login-state {font-size: 40rpx;font-family: Microsoft YaHei Regular, Microsoft YaHei Regular-Regular;font-weight: 400;color: #333;text-align: center;margin-top: 146rpx;
}.login-ready {font-size: 30rpx;font-family: Microsoft YaHei Regular, Microsoft YaHei Regular-Regular;font-weight: 400;color: #333;text-align: center;margin-top: 89rpx;
}.login-btn {width: 299rpx;height: 74rpx;background: #fc7297;border-radius: 37rpx;box-shadow: 0rpx 2rpx 6rpx 0rpx rgba(3, 0, 6, 0.1);margin: auto;margin-top: 65rpx;display: flex;align-items: center;justify-content: center;
}.btn-txt {font-size: 30rpx;font-family: Microsoft YaHei Regular, Microsoft YaHei Regular-Regular;font-weight: 500;color: #fff;
}/* 开始按钮 */.start {position: absolute;top: 45%;padding: 0 250rpx;
}.start-aperture {width: 250rpx;height: 250rpx;background: rgb(247, 136, 44, 0.29);border-radius: 50%;display: flex;align-items: center;justify-content: center;
}.start-aperture-bg {width: 210rpx;height: 210rpx;background: linear-gradient(0deg, #f0510b 0%, #ffc350 100%);border-radius: 50%;display: flex;align-items: center;justify-content: center;
}.start-aperture-txt {font-size: 60rpx;font-family: Source Han Sans CN Medium, Source Han Sans CN Medium-Medium;font-weight: 500;color: #fff;
}/* 规则 */.rule {position: absolute;left: 0;right: 0;top: 73%;
}.rule-top {width: 160rpx;height: 36rpx;background: rgb(237, 216, 255, 0.4);border-top-left-radius: 20rpx;border-top-right-radius: 20rpx;margin: auto;text-align: center;
}.iconxiangxia2 {font-size: 28rpx;color: #fff;
}.rule-body {width: 660rpx;/* height: 208rpx; */background: rgb(237, 216, 255, 0.4);border-radius: 20rpx;margin: auto;padding: 38rpx 0 38rpx 38rpx;
}
.rule-body-one{display: flex;
}
.rule-body-title {display: inline-block;width: 81rpx;font-size: 26rpx;font-family: Source Han Sans CN Normal, Source Han Sans CN Normal-Normal;color: #fff;
}.rule-body-html {display: inline-block;width: 417rpx;font-size: 26rpx;font-family: Source Han Sans CN Normal, Source Han Sans CN Normal-Normal;color: #fff;
}
/* 新 */
.remaTimes{position: absolute;left: 0;right: 0;top: 65%;font-size: 30rpx;font-family: Microsoft YaHei Regular, Microsoft YaHei Regular-Regular;font-weight: 400;color: #fff;text-align: center;margin-top: 20rpx;
}

在当前的页面中引入了两个组件,第一个是实现自定义导航栏的,因为在当前的界面中背景图的要求是全屏的。第二个组件就是红包雨的下落是参考网上一大佬的红包雨实现的。

//app.js
// 引入请求文件
App({onLaunch: function(e) {//获取设备顶部窗口的高度(不同设备窗口高度不一样,根据这个来设置自定义导航栏的高度)wx.getSystemInfo({success: res => {this.globalData.height = res.statusBarHeight}})this.getSystemInfo();},globalData: {share: false, // 分享默认为falseheight: 0, // 顶部高度systemInfo: {} //设备信息},getSystemInfo: function() {var info = util.getSystemInfoSync();var iphoneX = "";if (info) {var statusBarHeight = info.statusBarHeight;var model = info.model;var windowHeight = info.windowHeight;var totalTopHeight = 68;model.indexOf("iPhone X") !== -1 ?((totalTopHeight = 94), (iphoneX = "iphone-x")) :-1 !== model.indexOf("iPhone") ? (totalTopHeight = 64) : -1 !== model.indexOf("MI 8") && (totalTopHeight = 88);var titleBarHeight = totalTopHeight - statusBarHeight;this.globalData.systemInfo = Object.assign({}, info, {statusBarHeight,titleBarHeight,totalTopHeight,iphoneX,windowHeight})}}
})

app.js中针对不同的设备的导航条的高度进行了处理,使之能够实现适配。

<!--componets/shoppingMall-components/navbar/navbar.wxml-->
<view class='nav-wrap' style='height: {{height*2 + 20}}px;'><!-- 导航栏背景图片 --><image class="backgroundimg" src="{{navbarData.address}}" bindload="imgLoaded" style="width:{{imageWidth}}px;height:{{imageHeight}}px" /><!-- // 导航栏 中间的标题 --><view class='nav-title' wx:if='{{!navbarData.white}}' style='line-height: {{height*2 + 44}}px;'>{{navbarData.title}}</view><view class='nav-title' wx:else='{{!navbarData.white}}' style='line-height: {{height*2 + 44}}px; color:#ffffff'>{{navbarData.title}}</view><view style='display: flex; justify-content: space-around;flex-direction: column'><!-- // 导航栏  左上角的返回按钮 --><!-- //  其中wx:if='{{navbarData.showCapsule}}' 是控制左上角按钮的显示隐藏,首页不显示 --><view class='nav-capsule' style='height: {{height*2 + 44}}px;' wx:if='{{navbarData.showCapsule}}'><!-- //左上角的返回按钮,wx:if='{{!share}}'空制返回按钮显示 --><!-- //从分享进入小程序时 返回上一级按钮不应该存在 --><!-- navbarData.white是控制按钮颜色的,因为背景有深浅色,返回按钮自己找图片 --><view bindtap='_navback' wx:if='{{showBack}}'><text class="iconfont iconxiangzuo"></text></view><view bindtap='_navbackTo' wx:else><text class="iconfont iconxiaochengxushouye"></text></view></view></view>
</view>
<!-- 导航栏下面的背景图片 -->
<image class="backgroundimg" src="{{navbarData.address}}" bindload="imgLoaded" style="width:{{imageWidth}}px;height:{{imageHeight}}px" />
// componets/shoppingMall-components/navbar/navbar.js
const app = getApp()
const util = require('../../../utils/indexPage.js');
Component({/*** 组件的属性列表*/properties: {navbarData: {//navbarData   由父页面传递的数据,变量名字自命名type: Object,value: {},observer: function(newVal, oldVal) {}}},options: {styleIsolation: 'apply-shared'},/*** 组件的初始数据*/data: {height: '',//默认值  默认显示左上角navbarData: {showCapsule: 1},imageWidth: wx.getSystemInfoSync().windowWidth, // 背景图片的高度imageHeight: '', // 背景图片的长度,通过计算获取showBack:false},attached: function() {// 获取是否是通过分享进入的小程序this.setData({share: app.globalData.share})let pages = getCurrentPages();if (pages.length > 1){this.setData({showBack: true})}// 定义导航栏的高度   方便对齐this.setData({height: app.globalData.height})},/*** 组件的方法列表*/methods: {// 返回上一页面_navback() {wx.navigateBack()},_navbackTo(){util.indexPage();},// 计算图片高度imgLoaded(e) {// console.log(e, wx.getSystemInfoSync())this.setData({imageHeight: e.detail.height *(wx.getSystemInfoSync().windowHeight / e.detail.height)})}//返回到首页// _backhome() {//   wx.switchTab({//     url: '/pages/index/index'//   })// }}
})
/* componets/shoppingMall-components/navbar/navbar.wxss *//* 顶部要固定定位   标题要居中   自定义按钮和标题要和右边微信原生的胶囊上下对齐 */.nav-wrap {/* display: none; */position: fixed;width: 100%;top: 0;background: #fff;color: #000;z-index: 9999999;background: #000;overflow: hidden;
}/* 背景图 */.backgroundimg {position: absolute;z-index: -1;
}/* 标题要居中 */.nav-title {position: absolute;text-align: center;max-width: 400rpx;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;top: 0;left: 0;right: 0;bottom: 0;margin: auto;font-size: 36rpx;color: #2c2b2b;font-weight: 450;
}.nav-capsule {display: flex;align-items: center;margin-left: 30rpx;width: 140rpx;justify-content: space-between;height: 100%;
}.back-pre {width: 36rpx;height: 40rpx;margin-top: 4rpx;padding: 10rpx;
}.nav-capsule {width: 36rpx;height: 40rpx;margin-top: 3rpx;
}.iconxiangzuo{font-size: 41rpx;color: #fff;
}.iconxiaochengxushouye {font-size: 41rpx;color: #fff;
}

在自定义导航栏中针对不同的进入情况对左上角的按钮做不同的处理,当是通过扫码或分享进入的时候左上角是返回首页的小房子,通过其他页面跳转过来时则是返回上一级的箭头。

<view wx:if="{{visible}}" class="red-envelope-popup"><view class="container flex-center"><view bind:tap="handleClose" catch:touchmove="handleScrollTouch" class="close-bg"></view><block><block wx:if="{{showStatus===2}}"><view class="rain-wrapper flex-column"><view class="canvas-wrapper"><view class='score-change' animation="{{scoreAni}}">+{{showChangeScore}}</view><canvas disableScroll binderror="canvasIdErrorCallback" bindtouchstart="handleClickRain" canvasId="rain-canvas" style="width: 100vw; height: 100vh;z-index: 9999999;"></canvas></view></view></block><block wx:if="{{showStatus===3}}"><view class="result-wrapper flex-column-center"><block><view class="group-content flex-column-center" bindtap="handleClose"></view></block></view></block></block></view>
</view>
const innerAudioContext = wx.createInnerAudioContext()
const api = require('../../../server/api.js');
const APP = getApp()
let readyTimer = null
let rainTimer = null
let redEnvelopes = []
let animation = null
const minWidth = 30 // 红包图片最小宽度
const maxWidth = 40 // 红包图片最大宽度
Component({properties: {// 是否开始展示游戏visible: {type: Boolean,value: false},// 游戏时间time: {type: Number,value: 10},//  速度createSpeed: {type: Number,value: 5},// 单个最小金额min: {type: Number,value: 0},// 单个最大金额max: {type: Number,value: 3}},data: {showRainTotalTime: 10, // 红包雨时间showStatus: 1, // 红包雨状态:1:准备倒计时,2:正在红包雨,3:红包雨结束windowWidth: 375,windowHeight: 555,rainResult: {},loading: false,showScore: 0,showChangeScore: 0,scoreStyle: ''},ready: function() {// 重置redEnvelopes = []// clearTimeout(readyTimer) clearTimeout(rainTimer)this.cancelCustomAnimationFrame(animation)// 开始准备倒计时this.showRain()const {windowWidth,windowHeight} = APP.globalData.systemInfothis.data.windowWidth = windowWidththis.data.windowWidth = windowHeight},detached: function() {// readyTimer && clearInterval(readyTimer)rainTimer && clearInterval(rainTimer)animation && this.cancelCustomAnimationFrame(animation)},methods: {// 展示红包雨界面showRain: function() {let _this = this// 显示红包雨this.setData({showStatus: 2})// 初始化红包雨this.initRain()// 倒计时进度条this.ininProgress()// 红包雨倒计时let showRainTotalTime = this.data.timerainTimer = setInterval(function() {if (--showRainTotalTime <= 0) {clearInterval(rainTimer)if (animation) {// 结束_this.showRainResult()_this.cancelCustomAnimationFrame(animation)}}_this.setData({showRainTotalTime});}, 1000);},// 倒计时进度条ininProgress() {const {time} = this.dataconst animation = wx.createAnimation({duration: time * 1000})animation.translateX(-120).step()this.setData({progressAni: animation.export()})},//分数动画animationOfScore(x, y) {const position = wx.createAnimation({duration: 0})position.left(x).top(y).step()this.setData({scoreAni: position.export()})const animation = wx.createAnimation({duration: 300,timingFunction: 'ease'})animation.opacity(1).step()setTimeout(function() {animation.opacity(0).step()this.setData({scoreAni: animation.export()})}.bind(this), 10)},// 关闭handleClose: function() {this.triggerEvent("finish", this.data.showScore)},// 显示结果showRainResult: function() {// 结束动画this.cancelCustomAnimationFrame(animation)this.setData({showStatus: 3,rainResult: {amount: 100}});},// 红包下落函数customRequestAnimationFrame: function(e) {let _this = thislet timer = setTimeout(function() {e.call(_this), clearTimeout(timer);}, 1000 / 60)return timer},// 清除红包下落函数cancelCustomAnimationFrame: function(e) {e && (clearTimeout(e), animation = null)},// 开始下落doDrawRain: function() {const context = this.contextconst {windowWidth,windowHeight} = this.datacontext.clearRect(0, 0, windowWidth, windowHeight)for (let n = 0; n < redEnvelopes.length; n += 1) {const i = redEnvelopes[n] // 红包const {x,y,vx,vy,width,height,open} = iconst img = open ? this.openEnvelopeImg : this.redEnvelopeImgconst imgWidth = open ? width + 20 : widthconst imgHeight = open ? height + 25 : heightif (x < 0) {x += 50;} else if (x > 750) {x -= 50;}context.drawImage(img, x, y, imgWidth, imgHeight)i.x += vxi.y += vyi.y >= windowHeight && (i.y = 0, i.open = false)i.x + width <= 0 && (i.x = windowWidth - width, i.open = false)}context.draw()// 下落函数animation = this.customRequestAnimationFrame(this.doDrawRain);},// 随机数randNum: function(min, max) {return Math.floor(min + Math.random() * (max - min));},// 准备红包雨下落initRainDrops: function() {const {windowWidth,windowHeight,createSpeed,max,min} = this.datafor (let n = 0; n < 10; n += 1) {const startX = Math.floor(Math.random() * windowWidth)// 优化位置,防止红包越界现象,保证每个红包都在屏幕之内if (startX < 0) {startX += 50;} else if (startX > windowWidth) {startX -= 50;}const startY = Math.floor(Math.random() * windowHeight)// 红包图片宽度大小30~40const width = this.randNum(minWidth, maxWidth)// 宽度为红包高度的百分之八十const height = Math.floor(width / .8)// 速度const vy = 1 * Math.random() + createSpeed// 红包金额const score = this.randNum(min, max + 1)redEnvelopes.push({x: startX,y: startY,vx: -1, // x轴速度vy: vy, // y轴速度score: score,width: width,height: height,open: false});}this.doDrawRain();},// 点击红包事件handleClickRain: function(e) {let touch = e.touches[0]let touchX = touch.xlet touchY = touch.ylet _this = thisfor (let o = 0; o < redEnvelopes.length; o += 1) {let i = redEnvelopes[o],rainX = i.x,rainY = i.y,width = i.width,height = i.height,gapX = touchX - rainX,gapY = touchY - rainY;if (gapX >= -20 && gapX <= width + 20 && gapY >= -20 && gapY <= height + 20) {_this.animationOfScore(touchX, touchY)innerAudioContext.play()i.open = true;let score = _this.data.showScore + i.score_this.setData({showScore: score,showChangeScore: i.score})break;}}},// 初始化 canvasinitRain: function() {this.context = wx.createCanvasContext("rain-canvas", this)this.redEnvelopeImg = "./images/red-packet-rain.png",this.openEnvelopeImg = "./images/red-packet-rain-open.png"// 初始化红包雨this.initRainDrops()// 音效this.audioOfClick()},handleScrollTouch: function() {},audioOfClick() {innerAudioContext.autoplay = falseinnerAudioContext.src = 'https://imgs.solui.cn/weapp/dianji.mp3'innerAudioContext.onPlay(() => {})innerAudioContext.onError(res => {})},}
});
.flex {display: -ms-flexbox;display: flex
}
.flex-center {display: -ms-flexbox;display: flex;-ms-flex-direction: column;flex-direction: column;-ms-flex-align: center;align-items: center;-ms-flex-pack: center;justify-content: center;width: 100%;height: 100%
}
.flex-column {display: -ms-flexbox;display: flex;-ms-flex-direction: column;flex-direction: column
}
.flex-column-center {display: -ms-flexbox;display: flex;-ms-flex-direction: column;flex-direction: column;-ms-flex-align: center;align-items: center
}
.flex-column-sb {display: -ms-flexbox;display: flex;-ms-flex-direction: column;flex-direction: column;-ms-flex-align: center;align-items: center;-ms-flex-pack: justify;justify-content: space-between
}
.flex-column-c {display: -ms-flexbox;display: flex;-ms-flex-direction: column;flex-direction: column;-ms-flex-align: center;align-items: center;-ms-flex-pack: center;justify-content: center
}
.flex-row {display: -ms-flexbox;display: flex;-ms-flex-align: center;align-items: center
}
.flex-row-center {display: -ms-flexbox;display: flex;-ms-flex-align: center;align-items: center;width: 100%
}
.flex-content-center {display: -ms-flexbox;display: flex;-ms-flex-pack: center;justify-content: center;width: 100%
}
.flex-column-right {display: -ms-flexbox;display: flex;-ms-flex-direction: column;flex-direction: column;-ms-flex-align: end;align-items: flex-end
}
.flex-column-left {display: -ms-flexbox;display: flex;-ms-flex-direction: column;flex-direction: column;-ms-flex-align: start;align-items: flex-start
}
.flex-sb {display: -ms-flexbox;display: flex;-ms-flex-align: center;align-items: center;-ms-flex-pack: justify;justify-content: space-between
}
.flex-sa {display: -ms-flexbox;display: flex;-ms-flex-align: center;align-items: center;-ms-flex-pack: distribute;justify-content: space-around
}
.flex-c {display: -ms-flexbox;display: flex;-ms-flex-align: center;align-items: center;-ms-flex-pack: center;justify-content: center
}
.flex-sb-start {display: -ms-flexbox;display: flex;-ms-flex-pack: justify;justify-content: space-between
}
.flex-start {display: -ms-flexbox;display: flex;-ms-flex-pack: start;justify-content: flex-start
}
.flex-end {display: -ms-flexbox;display: flex;-ms-flex-pack: end;justify-content: flex-end
}
.flex-center-end {display: -ms-flexbox;display: flex;-ms-flex-align: center;align-items: center;-ms-flex-pack: end;justify-content: flex-end
}
.flex-end-center {display: -ms-flexbox;display: flex;-ms-flex-align: end;align-items: flex-end;-ms-flex-pack: center;justify-content: center
}
.flex-end-sb {display: -ms-flexbox;display: flex;-ms-flex-align: end;align-items: flex-end;-ms-flex-pack: justify;justify-content: space-between
}
.flex-end-start {display: -ms-flexbox;display: flex;-ms-flex-align: end;align-items: flex-end
}
.red-envelope-popup {position: fixed;top: 0;left: 0;right: 0;bottom: 0;z-index: 999;/* background: rgba(0,0,0,.8) */
}
.red-envelope-popup .close-bg {position: absolute;top: 0;left: 0;right: 0;bottom: 0
}
.red-envelope-popup .reminder-wrapper {position: relative;width: 750rpx;height: 716rpx;color: #fff;box-sizing: border-box
}
.red-envelope-popup .reminder-wrapper .title {font-size: 60rpx;color: #fffdc5
}
.red-envelope-popup .reminder-wrapper .time {margin-top: 100rpx;font-size: 240rpx;color: #fffdc5
}
.red-envelope-popup .rain-wrapper {/* width: 100%; *//* height: 100%; *//* background-image: url(https://imgs.solui.cn/weapp/redBacketBG.jpg); *//* background-size: 100% 100%; *//* background-repeat: no-repeat */
}
.red-envelope-popup .rain-wrapper .time-info {position: absolute;top: 80rpx;left: 45rpx;font-size: 24rpx;color: #fff
}
.red-envelope-popup .rain-wrapper .time-info .progress-wrapper {position: relative;height: 16rpx;width: 240rpx;margin-left: 20rpx;margin-right: 20rpx;border-radius: 16rpx;background: #fff;overflow: hidden
}
.red-envelope-popup .rain-wrapper .time-info .progress-wrapper .progress {position: absolute;top: 0;left: 0;bottom: 0;width: 240rpx;border-radius: 16rpx;background: #fe2e00;z-index: 1
}
.red-envelope-popup .rain-wrapper .time-info .total-score {font-size: 40rpx
}
.red-envelope-popup .rain-wrapper .canvas-wrapper {position: relative
}
.red-envelope-popup .rain-wrapper .canvas-wrapper .score-change {position: absolute;width: 50rpx;height: 50rpx;font-size: 40rpx;color: #fffdc5;opacity: 0
}
.red-envelope-popup .result-wrapper {width: 100%;height: 100%;display: -ms-flexbox;display: flex;-ms-flex-pack: center;justify-content: center
}
.red-envelope-popup .result-wrapper .group-content {position: relative;width: 550rpx;height: 700rpx;/* background-image: url(https://imgs.solui.cn/weapp/l-rain-gold@2x.png); */background-image: url(http://hmspimg.afarsoft.com/static/crmmicroapp/res/shopImg/Demolition.png);background-size: 100% 100%;background-repeat: no-repeat
}
.red-envelope-popup .result-wrapper .group-content .result-title {margin-top: 50rpx;font-size: 40rpx;color: #fff
}
.red-envelope-popup .result-wrapper .group-content .money-wrapper {margin-top: 56rpx;color: #fff
}
.red-envelope-popup .result-wrapper .group-content .money-wrapper .money {font-size: 120rpx
}
.red-envelope-popup .result-wrapper .group-content .money-wrapper .unit {position: relative;top: 34rpx;font-size: 32rpx
}
.red-envelope-popup .result-wrapper .group-content .result-btn {margin-top: 158rpx;width: 300rpx;height: 70rpx;background-color: #fff9e8;text-align: center;line-height: 70rpx;border-radius: 40rpx;font-size: 30rpx;color: #b10000
}

在这个组件中,在attached这个生命周期的时候进是红包下落的方法开始执行。在创建红包横向坐标的时候需要对坐标进行优化防止红包出现在屏幕外。

<!--RedEnvelopes/pages/RedEnvelopes/WinningRedEnvelope/WinningRedEnvelope.wxml-->
<view wx:if="{{showMode}}"><image src="{{url}}" class="winning"></image><view class="congratulations" wx:if="{{NotWinning}}">很遗憾!</view><view class="congratulations" wx:else>恭喜您!</view><view class="prize" wx:if="{{NotWinning}}">您本次没有中奖。</view><view class="prize" wx:else>您一共获得<block wx:if="{{RedEnvelope.AmountCount}}">{{RedEnvelope.AmountCount}}个红包</block><block wx:if="{{RedEnvelope.AmountCount && RedEnvelope.CouponCount}}">,</block><block wx:if="{{RedEnvelope.CouponCount}}">{{RedEnvelope.CouponCount}}张优惠券</block></view><block wx:for="{{RedEnvelope.CouponWinInfoList}}" wx:key="index"><view class="prize-details"><view class="prize-quan"><image src="{{quan}}" class="quan"></image><text class="quan-txt" wx:if="{{item.UseType == 0}}">{{item.DiscountAmount}}元</text><text class="quan-txt" wx:else>{{item.DiscountRate}}折</text><text class="quan-type coupon">优惠券</text></view><view class="prize-name">{{item.CouponName}}</view><view class="prize-num">数量:{{item.Num}}</view></view></block><block wx:for="{{RedEnvelope.AmountWinInfoList}}" wx:key="index"><view class="prize-details"><view class="prize-quan"><image src="{{quan}}" class="quan"></image><text class="quan-txt">{{item.RedEnvelopeAmount}}元</text><text class="quan-type cash">现金</text></view><view class="prize-name">{{item.RedEnvelopeAmount}}元现金红包</view><view class="prize-num">数量:{{item.Num}}</view></view></block><view class="share"><view class="share-left">邀请好友参加活动</view><view class="share-left lower">每天可获得<text class="share-num" wx:if="{{NotWinning}}">{{MaxSharingAwardTimes}}次</text><text class="share-num" wx:else>{{RedEnvelope.MaxSharingAwardTimes}}次</text>分享奖励</view><button class="share-btn" open-type="share"><text class="share-txt">去分享</text></button></view>
</view>
// RedEnvelopes/pages/RedEnvelopes/WinningRedEnvelope/WinningRedEnvelope.js
const api = require('../../../../server/api.js');
const http = require('../../../../server/request.js');
Page({/*** 页面的初始数据*/data: {url: api.pictureServer + '/res/shopImg/winning.png',quan: api.pictureServer + '/res/shopImg/redCoupon.png',Id: '', //抢红包活动标识PartId: '', //抢红包参与信息标识RedEnvelope: {},ShareDescription: '', //分享描述ShareIcon: '', //分享图标NotWinning: true, //未中奖showMode: false},/*** 生命周期函数--监听页面加载*/onLoad: function(options) {console.log(options)this.setData({Id: options.Id,PartId: options.PartId,ShareDescription: options.ShareDescription,ShareIcon: options.ShareIcon,})let parm = {Id: options.Id,PartId: options.PartId}this.getGrabRedEnvelopeWinInfo(parm);},/*** 生命周期函数--监听页面初次渲染完成*/onReady: function() {},/*** 生命周期函数--监听页面显示*/onShow: function() {},/*** 生命周期函数--监听页面隐藏*/onHide: function() {},/*** 生命周期函数--监听页面卸载*/onUnload: function() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh: function() {},/*** 页面上拉触底事件的处理函数*/onReachBottom: function() {},/*** 用户点击右上角分享*/onShareAppMessage: function() {let ShareMemberId = wx.getStorageSync('CustomerService').MemberIdreturn {title: this.data.ShareDescription,// desc: '快来抢红包雨',imageUrl: this.data.ShareIcon,path: '/RedEnvelopes/pages/RedEnvelopes/index/index?refer=' + this.data.Id + '&ShareMemberId=' + ShareMemberId,success: function(res) {// 转发成功wx.showToast({title: '分享成功',icon: 'none'})},fail: function(res) {// 转发失败}}},// 拆开红包得到中奖信息getGrabRedEnvelopeWinInfo: function(parm) {let that = this;http.requestLoading('/api/services/app/RedEnvelope/GetGrabRedEnvelopeWinInfo', parm, '', 'GET').then(res => {if (res.data.Result.Code === 0) {console.log(res)that.setData({NotWinning: false,RedEnvelope: res.data.Result.Data,showMode: true})} else if (res.data.Result.Code === 1) {//未中奖that.setData({NotWinning: true,MaxSharingAwardTimes: res.data.Result.Data.MaxSharingAwardTimes,showMode: true})}})}
})
/* RedEnvelopes/pages/RedEnvelopes/WinningRedEnvelope/WinningRedEnvelope.wxss */
page {width: 100%;background: rgb(254, 177, 198, 0.2);
}
/* 礼品 */
.winning {width: 286rpx;height: 294rpx;display: block;margin: 20rpx auto;
}.congratulations {font-size: 46rpx;font-family: Adobe Heiti Std R, Adobe Heiti Std R-R;color: #ff3005;text-align: center;
}.prize {font-size: 28rpx;font-family: Adobe Heiti Std R, Adobe Heiti Std R-R;color: #fc7297;text-align: center;margin: 38rpx auto;
}
/* 奖励内容 */
.prize-details {width: 668rpx;height: 150rpx;background: #fff;border-radius: 10rpx;display: block;margin: auto;padding: 38rpx 0 38rpx 38rpx;margin-bottom: 20rpx;position: relative;
}.prize-quan {width: 177rpx;position: relative;
}.quan {width: 177rpx;height: 74rpx;
}.quan-txt {position: absolute;top: 20rpx;left: 30rpx;font-size: 28rpx;font-family: Source Han Sans CN Regular, Source Han Sans CN Regular-Regular;font-weight: 400;color: #fff;
}.quan-type {width: 19rpx;font-size: 20rpx;font-family: Source Han Sans CN Regular, Source Han Sans CN Regular-Regular;font-weight: 400;text-align: left;color: #fff;line-height: 22rpx;
}.coupon {position: absolute;top: 7rpx;right: 16rpx;height: 63rpx;
}.cash {position: absolute;top: 15rpx;right: 16rpx;height: 39rpx;
}.prize-name {font-size: 30rpx;font-family: Source Han Sans CN Regular, Source Han Sans CN Regular-Regular;font-weight: 400;color: #333;position: absolute;top: 37rpx;left: 244rpx;
}.prize-num {font-size: 24rpx;font-family: Source Han Sans CN Regular, Source Han Sans CN Regular-Regular;font-weight: 400;color: #333;position: absolute;top: 86rpx;left: 244rpx;
}
/* 分享 */
.share {width: 668rpx;height: 150rpx;background: #f77b7b;border-radius: 10rpx;margin: 40rpx auto;position: relative;
}.share-left {font-size: 30rpx;font-family: Source Han Sans CN Normal, Source Han Sans CN Normal-Normal;color: #fff;position: absolute;top: 32rpx;left: 47rpx;
}.lower {position: absolute;top: 73rpx;left: 47rpx;
}.share-num {font-size: 38rpx;
}.share-btn {width: 170rpx;height: 49rpx;background: linear-gradient(0deg, #fa8d35 0%, #ffc341 100%);border-radius: 25rpx;display: flex;justify-content: center;align-items: center;position: absolute;top: 55rpx;right: 35rpx;
}.share-txt {font-size: 30rpx;font-family: Source Han Sans CN Normal, Source Han Sans CN Normal-Normal;font-weight: 500;color: #fff;
}

在查看红包界面中没有太多需要主要的东西,主要实现分享即可。

<!--RedEnvelopes/pages/RedEnvelopes/WinningRecord/WinningRecord.wxml-->
<wxs module="func">module.exports = {strSplit: function(str, con) {return str.split(con);}}
</wxs>
<view><view class="calendar_container container"><view class="select" bindtap="dateClick"><view>{{showStartDate||showEndDate?showStartDate+'-'+showEndDate:'全部'}}</view></view><i class="iconfont iconrili" bindtap="dateClick"></i></view><view class="container"><block wx:for="{{DrawRecordlist}}" wx:key="{{index}}"><view class="DrawRecordItem"><view class="item0"><view class="item_txt">{{item.CreateTime}}</view></view><view class="item1"><block wx:for="{{item.HongBaoWinningRecordList}}" wx:key="{{index}}"><view class="item_txt dib mr-5">{{item.RedEnvelopeAmount}}元红包<text class="lookCode lookCodes">X{{item.HongBaoNum}}</text></view></block><block wx:for="{{item.CouponWinningRecordList}}" wx:key="{{index}}"><view class="item_txt dib mr-5">{{item.CouponName}}<text class="lookCode">X{{item.CouponNum}}</text></view></block></view></view></block></view><CalendarComponent id="calendar-component" startDate="{{startDate}}" endDate="{{endDate}}" bind:myevent="getDate"></CalendarComponent>
</view>
// RedEnvelopes/pages/RedEnvelopes/WinningRecord/WinningRecord.js
// 引入URL
const http = require('../../../../server/request.js');
//调用公共js对象以便调用其方法
var app = require('../../../../utils/uselogn.js'); //获取应用实例
var util = require('../../../../utils/util.js') //公用方法
const api = require('../../../../server/api.js');
var pageNum = 1;
Page({/*** 页面的初始数据*/data: {//组件数据componentData: null,// 仅支持 年-月-1startDate: "1960/1/1",endDate: new Date().getFullYear() + "/" + (new Date().getMonth() + 1) + "/" + new Date().getDate(),pictureServer: api.pictureServer,tabIndex: 0,skinStyle: "",userDaysAward: [], //用户签到奖励signInReward: [], //连续签到奖励showStartDate: "", //显示用的showEndDate: "", //显示用的time1: "",time2: "",DrawRecordlist: [],ActivityId: ""},onLogin: app.userLogin,/*** 生命周期函数--监听页面加载*/onLoad: function(options) {pageNum = 1;//创建自定义组件实例this.setData({componentData: this.selectComponent('#calendar-component')})this.setData({skinStyle: wx.getStorageSync("skin")})if (options.ActivityId) {this.setData({ActivityId: options.ActivityId})}this.onLogin(this.callback)},/*** 生命周期函数--监听页面初次渲染完成*/onReady: function() {},/*** 生命周期函数--监听页面显示*/onShow: function() {},/*** 生命周期函数--监听页面隐藏*/onHide: function() {},/*** 生命周期函数--监听页面卸载*/onUnload: function() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh: function() {},/*** 页面上拉触底事件的处理函数*/onReachBottom: function() {pageNum++;this.getLotteryRecord();},/*** 用户点击右上角分享*/onShareAppMessage: function() {},callback() {this.getLotteryRecord(this.data.time1,this.data.time2)},/*** tab切换*///获取会员的中奖纪录信息getLotteryRecord(s, e) {http.requestLoading("/api/services/app/RedEnvelope/MemberWinningRecord", {"StartDate": s ? s + " 00:00:00" : '',"EndDate": e ? e + " 23:59:59" : '',"page": pageNum,"rows": 10}, "", "POST").then(res => {if (res.data.Result.Code === 0) {if (!res.data.Result.Rows) {wx.showToast({title: '无更多数据了',icon: "none"})return}this.setData({DrawRecordlist: this.data.DrawRecordlist.concat(res.data.Result.Rows)})}})},showClick(e) {const {index} = e.currentTarget.datasetconst data = this.data.signInRewarddata[index].show = !data[index].showthis.setData({signInReward: data})},//显示组件dateClick() {this.data.componentData.createAni()},//获取开始结束getDate(e) {const startDate = e.detail[0]const endDate = e.detail[1]this.setData({showStartDate: startDate.replace(/-/g, '/'),showEndDate: endDate ? endDate.replace(/-/g, '/') : "",time1: startDate,time2: endDate ? endDate : "",DrawRecordlist: []})this.getLotteryRecord(startDate, endDate)}
})
/* RedEnvelopes/pages/RedEnvelopes/WinningRecord/WinningRecord.wxss */page {background-color: #f5f6f7;
}.container {padding: 0 40rpx;box-sizing: border-box;
}/* 选择日历 */.calendar_container {width: 100%;height: 86rpx;display: flex;align-items: center;
}.select {width: 320rpx;height: 58rpx;background: #fff;border-radius: 10rpx;display: flex;align-items: center;justify-content: space-between;color: #999;font-size: 24rpx;padding-left: 20rpx;padding-right: 10rpx;
}.iconrili {margin-left: 22rpx;font-size: 32rpx;color: rgba(205, 205, 205, 0.97);
}.DrawRecordItem {margin-top: 16rpx;background: #fff;border-radius: 10rpx;
}.item1 {padding: 0 22rpx 22rpx 22rpx;width: 100%;background: #fff;border-radius: 10rpx;
}.lookCode {font-size: 24rpx;font-family: Source Han Sans CN Normal, Source Han Sans CN Normal-Normal;font-weight: 400;color: #4ace8c;
}.lookCodes {color: #fc7297;
}.item0 {padding: 0 16rpx 0 26rpx;width: 100%;height: 66rpx;background: #fff;border-radius: 10rpx;display: flex;justify-content: space-between;align-items: center;
}.item_txt {font-size: 24rpx;font-family: Source Han Sans CN Normal, Source Han Sans CN Normal-Normal;font-weight: 400;color: #666;
}.mr-5 {margin-right: 20rpx;
}

在中奖纪录查看的页面中使用的自己封装的日历组件。仅是为了复合项目的要求,在此就不做展示了。
红包雨整个流程到此就结束了。

小程序中关于红包雨的实现相关推荐

  1. uniappp小程序做一个红包雨的功能

    uniappp小程序做一个红包雨的功能 需求: 首先,您需要在页面中添加一个画布元素,用于绘制红包雨效果. 在页面加载完成后,使用JavaScript中的setInterval()方法定时执行绘制红包 ...

  2. 小程序大转盘红包雨营销组件

    前言 商城没几个营销活动能叫商城吗? 所以就来几个组件吧,写的不好轻踩,对你有帮助记得给个小星星哦 直接上链接github链接 运行例子 git clone https://github.com/su ...

  3. 微信小程序中使用emoji表情相关说明

    本帖将聚合一些跟emoji表情有关的知识:前端传过来的昵称和备注信息一定要经过严格的正则表达式过滤,放置出现XSS等攻击,另外emoji字体表情库应该使用base64_encode编码,拿信息的时候b ...

  4. 小程序怎么发红包?api接口指南

    小程序怎么发红包?#小程序开发# 分享一种方式供大家参考,使用第三方API红包代发接口. 官方要求: 按微信官方规则,要实现小程序给用户派发红包需要有一个具备相关权限的商户号,再按微信官方接口文档开发 ...

  5. php转换emoji表情为图片输出小程序,微信小程序中使用emoji表情相关说明

    本帖将聚合一些跟emoji表情有关的知识:相关文章:"i爱记账" 小程序后端开发小结 第7条经验前端传过来的昵称和备注信息一定要经过严格的正则表达式过滤,放置出现XSS等攻击,另外 ...

  6. 微信小程序如何发红包?

    敲重点:若商户号有开通商家转账到零钱(原企业付款到零钱)接口权限的,推荐小程序可以直接调用此接口变通实现红包功能,若开发者无法提供含(小程序红包.现金红包.商家转账到零钱)任一接口权限的商户号,可以使 ...

  7. 微信小程序如何发红包

    文章开头先提一点,如果小程序可以开通企业支付接口,那推荐直接使用企业支付接口来实现红包的功能,小程序链接公众号通过公众号的红包接口来实现是非常坑爹的一件事. 微信企业支付接口:https://pay. ...

  8. 微信小程序中的tabBar设置

    我们先来看一份图,这个设置在官方文档中已经写得很清楚了,我只是做一个总结 注:我写注释是为了方便说明,在小程序中的json文件中是不能用注释的 这个tabBar属于全局属性,因此就在全局配置文件app ...

  9. 六一:如何在Datawhale开源学习小程序中管

    我们的组队学习马上就要开营了,本次组队学习与以往不同的是小程序中增加了队伍管理的功能. 为了方便大家组队,Datawhale的 六一同学 为大家准备了在Datawhale开源学习小程序中队伍管理的教程 ...

最新文章

  1. idea里maven设置本地仓库报错原因
  2. python开发桌面软件-python适合windows的桌面应用程序开发吗?
  3. OVER(PARTITION BY)函数用法
  4. C语言实现hashset算法(附完整源码)
  5. 4.6 Kaggle房价预测
  6. 【BZOJ1797】[AHOI2009]最小割(网络流)
  7. 工作流 activity 视频教程 + redis 视频教程 百度网盘分享地址
  8. 数学建模学习笔记(十二)——奇异值分解
  9. paip.复制文件 文件操作 api的设计uapi java python php 最佳实践
  10. Netsparker
  11. bootstrap 下拉列表获取_Bootstrap框架下下拉菜单的实现(代码示例)
  12. 不删除分区的情况下怎么合并分区?如何合并磁盘分区
  13. 【android】高仿京东商城App,集成react-native热更功能
  14. 广工Anyview数据结构2021-C语言版--第一章
  15. 《点石成金》(持续更新)
  16. Core Telephony
  17. 【Python】动量策略回测(日内高频数据)
  18. SCI期刊Cover Letter写法模板
  19. win11 / win10 彻底删除系统“快速访问”中自动添加的文件夹 - 不再自动添加
  20. Github上优秀的.NET Core开源项目的集合

热门文章

  1. rstudio 修改代码间距_R语言入门:使用RStudio的基本操作
  2. 2048小游戏lua
  3. RCNN,Fast RCNN, Faster RCN解析
  4. 操作Python列表Ⅰ
  5. 拆解「千言数据集:文本相似度」竞赛第一背后的故事
  6. Richard Hamming ``You and Your Research''
  7. OCI--学习OCI编程
  8. 【Axure教程】拖动排序——扣款顺序
  9. 猿创征文|收到谷歌开发者大会正式邀请(Java学生的自学之路)
  10. 消防安全监测模块,筑牢工厂消防安全屏障