了解到要做的活动表现形式为*,首先当然是google,百度跑跑找找demo,看下人家是怎么实现的。
网上*的插件挺多的,实现原理也各不一样。
这里先给个网上找的*的效果demo: *效果demo,效果是这么个效果,但是咱们得了解原理吖,看了下源码,jQuery做的?,小程序内又不能用jQuery,咋操作呢?
剖析了一波,划重点(要考的):
css的 background-position属性是设置背景图像的起始位置,那么我们控制背景图在0-3秒的时间内显示不同的位置,再加上过渡动画就可以达到*旋转的效果
实现底部滚动停止后显示弹窗且跟后端返回的中奖码一致
先贴一段代码
AXML
<view class="box-1">
<view class="wheel-boxs">
<view class="box-list" a:for="{{boxStatus}}" a:key="index">
<view class="box-text" a:if="{{!isStart}}">{{item}}</view>
<!-- 核心代码 -->
<view class="box-image" style="background: url('https://qiniu-image.qtshe.com/20181113wheels.png');
background-position-y: {{isStart ? ((16 - item) * 100) + 1500 + 'rpx' : 0}}; background-size: 100% 100%;
transition-property: {{isStart ? 'all' : 'none'}}; transition-delay: {{(index + 1) * 100 + 'ms'}};
transition-duration: 3.5s;">
</view>
{{item}}
</view>
</view>
<view class="start-box">
<view class="timeStemp" a:if="{{remainTime <= 3600}}">开奖倒计时: {{formatTime}}</view>
<block a:else>
<form onSubmit="startDraw" report-submit="true" a:if="{{pageVo.remainCount !== 0}}" >
<button hover-class="none" formType="submit" class="animate-btn" />
</form>
<block a:else>
<view class="no-draw" a:if="{{status.down>今日次数已用尽,明日再来</view>
<view class="no-draw" a:else>请完成下方任务获取次数</view>
</block>
</block>
</view>
<view class="last-tips" a:if="{{remainTime > 3600}}">当前剩余 {{pageVo.remainCount || 0}} 次攒码机会</view>
</view>
ACSS
.wheel-boxs {
width: 680rpx;
padding: 0 80rpx;
margin-top: 16rpx;
}
.box-list {
width: 90rpx;
height: 100rpx;
background: url(https://qiniu-image.qtshe.com/20190829openseason_03png) no-repeat center center;
background-size: 100% 100%;
display: inline-block;
margin-right: 16rpx;
overflow: hidden;
}
.box-list:last-child {
margin-right: 0;
}
.box-tips {
width: 500rpx;
height: 54rpx;
background: #ffcc1a;
overflow: hidden;
background-size: 100% 100%;
margin: 20rpx auto;
color: #7d2f0e;
font-size: 24rpx;
text-align: center;
line-height: 54rpx;
margin-top: 36rpx;
border-radius: 54rpx;
}
.box-text {
width: 100%;
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-size: 44rpx;
color: #FFF;
font-weight: 600;
font-family: 'HYRunYuanJ'
}
.box-image {
height: 1500%;
}
.start-box {
width: 100%;
text-align: center;
margin: 16rpx 0 8rpx;
}
.start-box button {
width: 290rpx;
height: 76rpx;
background: url(https://qiniu-image.qtshe.com/20190829startDrawpng) no-repeat center center;
background-size: 290rpx 76rpx;
margin: 0 auto;
}
.timeStemp {
margin: 24rpx auto 0;
height: 80rpx;
padding: 0 32rpx;
line-height: 80rpx;
text-align: center;
color: #7d2f0e;
background: url(https://qiniu-image.qtshe.com/20190829currt.png) no-repeat left top;
background-size: 100% 100%;
border-radius: 30rpx;
font-size: 32rpx;
display: inline-block;
}
.no-draw {
height: 68rpx;
font-size: 30rpx;
text-align: center;
color: #FFF;
margin: 0 auto;
border-radius: 68rpx;
background: #9c9c9c;
max-width: 400rpx;
line-height: 68rpx;
}
.last-tips {
width: 100%;
text-align: center;
color: #000;
font-size: 24rpx;
}
.last-tips text {
color: #f8294a;
}
JS
var timer = null
var app = getApp()
Page({
data: {
season: 1, //当前是第几场
isStart: false, //是否开始抽奖
isDialog: false, //是否显示弹窗
dialogId: 1, //显示第几个弹窗
remainTime: 3600,
formatTime: '60分00秒',
pageVo: {}, //页面基础数据大对象
boxTips: '本场开学码将在9月30日 19:00截止领取', //页面中部文案显示
typeTips: '9月30日20点开奖哦!',
boxStatus: ['码', '上', '有', '红', '包'], //五个抽奖默认文案
results: [], //抽中的码
},
onLoad(options) {
//引用外部字体
my.loadFontFace({
family: "HYRunYuanJ",
source: 'url("https://qiniu-app.qtshe.com/HYRunYuanJ.ttf")'
})
my.showLoading({
content: '加载中'
})
},
onShow() {
},
//格式化时间
getdate(timeSteamp) {
timeSteamp = new Date(timeSteamp)
let m = timeSteamp.getMonth() + 1
let d = timeSteamp.getDate()
return (m < 10 ? "0" + m : m) + "月" + (d < 10 ? "0" + d : d) + "日"
},
//初始化页面数据
initData() {
let postData = {
id: 1
}
app.ajax(‘xxx’, postDat).then((res) => {
my.hideLoading()
if (res.success) {
this.setData({
pageVo: res.data
})
} else {
util.toast(res.msg || '团团开小差啦,请稍后再试')
}
}, () => {
my.hideLoading()
util.toast('团团开小差啦,请稍后再试')
})
},
//开始抽奖
startDraw(e) {
//收集一波formId
util.addFormId(e.detail.formId)
let postData = {
season: this.data.pageVo.season
}
my.showLoading({
content: '加载中'
})
app.ajax('xxx', postData).then((res) => { //发送请求给后端,拿到中奖码
my.hideLoading()
if (res.success) {
this.setData({
isStart: true,
results: res.data.result.split(','), //后端返回的中奖码
dialogId: res.data.special ? 3 : 2 //显示弹窗,如果有彩蛋显示第三个弹窗,如果没有显示第二个弹窗
})
var t = setTimeout(() => {
this.setData({
isStart: false,
isDialog: true, //显示弹窗
boxStatus: ['码', '上', '有', '红', '包'] //五个抽奖默认文案
})
clearTimeout(t)
this.initData()
}, 4000) //页面上内容滚动了4秒,这里延时4秒
} else {
util.toast(res.msg || '团团开小差啦,请稍后再试')
}
}, () => {
my.hideLoading()
util.toast('团团开小差啦,请稍后再试')
})
},
//倒计时日期格式化
timestamp(time) {
let min = Math.floor((time / 60) % 60)
let sec = Math.floor(time % 60)
min = min < 10 ? '0' + min : min
sec = sec < 10 ? '0' + sec : sec
this.setData({
formatTime: `${min}分${sec}秒`
})
},
//执行倒计时
countDown(item) {
clearInterval(timer)
timer = setInterval(() => {
if (item === 0) {
this.initData()
clearInterval(timer)
} else {
item -= 1
this.timestamp(item)
}
}, 1000)
},
onShareAppMessage() {
return {
title: '“码”上有红包!点我瓜分10万开学季礼金>>',
path: 'xxx',
bgImgUrl: 'https://qiniu-image.qtshe.com/20190829zfbshare.jpg'
}
},
onHide() {
clearInterval(timer)
},
onUnload() {
clearInterval(timer)
}
})
最后完整的实现效果在这里:
微信小程序版
点我查看完整的视频效果
支付宝小程序版(看着效果卡顿,真机正常)
点我查看完整的视频效果
注意两个点:
- 旋转的背景图是雪碧图。我这里用到的是这张(https://qiniu-image.qtshe.com/20181113wheels.png)可供参考
- 控制好图的位移单位,需要计算下,这样才可以让后端返回的值与你的图相匹配。我这里是15个icon所以计算方式如下
<view class="box-image" style="
background: url('https://qiniu-image.qtshe.com/20181113wheels.png');
background-position-y: {{isStart ? ((16 - item) * 100) + 1500 + 'rpx' : 0}};
background-size: 100% 100%;
transition-property: {{isStart ? 'all' : 'none'}};
transition-delay: {{(index + 1) * 100 + 'ms'}};
transition-duration: 3.5s;">
</view>
这里可以封装成自定义组件,传入图片以及数量即可。后面如果有再用到 我会封装下再发出来。
最后说下弹窗显示的图的匹配方法,根据图片大小计算,比较麻烦:
axml
<view class="ci-wrapper">
<view a:if="{{icontype ==='nomal'}}" class="icon-default icon-nomal" style=" background-position-y: {{(-24 - 117.86 * (typeId - 1)) + 'rpx'}};"></view>
<view a:else class="icon-default icon-fade" style=" background-position-y: {{(-20 - 110.73 * (typeId - 1)) + 'rpx'}}; "></view>
</view>
acss
.ci-wrapper {
}
.icon-default {
width: 70rpx;
height: 70rpx;
background-repeat: no-repeat;
}
.icon-nomal {
background-image: url(https://qiniu-image.qtshe.com/20181113wheels.png);
background-position-x: -17rpx;
background-size: 100rpx 1768rpx
}
.icon-fade {
background-image: url(https://qiniu-image.qtshe.com/20181113wheels_fade.png);
background-size: 110rpx 1661rpx;
background-position-x: -18rpx;
}
Js
Component({
props: {
icontype: 'nomal',
iconid: 1
},
data: {
nomalOrigin: {
x: -17,
y: -24
},
fadeOrigin: {
x: -17,
y: -24
},
typeId: 1
}
})
以上就是一个完整小程序的*实现方案,有什么优化点大家可以指出来,后续会整理一下出个文件拷贝就可使用。
因为支付宝端,暂无代码片段功能,所以写了个微信小程序版本的代码片段:https://developers.weixin.qq.com/s/1k5eSnmh7z72