增加新的抽奖页面

This commit is contained in:
2024-12-20 09:45:54 +08:00
parent 168665eebc
commit 908205200b
19 changed files with 728 additions and 0 deletions

View File

@@ -0,0 +1,386 @@
<template>
<view class="shell">
<view class="l">
<!-- 小灯列表 -->
<view class="lamp" v-for="(item,index) in lamps" :key="item.color" :style="{'background-color': item.color,'box-shadow': '0 0 5px '+item.color}"></view>
</view>
<view class="c">
<view class="HMSM">
<!-- 背景 -->
<view class="HMSM-display-bg">
<view v-for="(shaft,index) in shaftList" :key="index" class="box"></view>
</view>
<!-- 奖品列表 -->
<view class="HMSM-display">
<view v-for="(shaft,index) in shaftList" :key="index" class="HMSM-shaft"
:style="{transform: 'translate3d(0, '+translateY[index]+'%, 0)','transition-duration':duration+'ms'}"
:class="{'roll_animation':(rollState=='start')}">
<view v-for="(item,shaftIndex) in shaft" :key="item.HMSM_id">
<image :src="item.img"></image>
</view>
</view>
</view>
</view>
</view>
<view class="r">
<!-- 小灯列表 -->
<view class="lamp" v-for="(item,index) in lamps" :key="item.color" :style="{'background-color': item.color,'box-shadow': '0 0 5px '+item.color}"></view>
</view>
</view>
</template>
<script>
// 实现原理
// 就是一个长列表做一个translateY的位移然后加上延迟
// 只要这个列表足够长,设置好动画曲线,就能实现效果了
export default {
name: 'HM-slotMachine',
data() {
return {
// 低二/三列开始滚动的延迟
delay:0,
// 总的摇奖时间 单位毫秒
duration: 0,
// 摇奖方向 可选 up down
direction: 'up',
// 摇奖状态
rollState: 'stop',
// 奖品列表
prizeList: [],
// 位置
translateY: [0, 0, 0],
// 滚动列表
shaftList: [],
// 控制小灯切换的定时器
lampTimer:false,
// 小灯颜色
lamps:[
{color:'#97eefb'},
{color:'#fecc6a'},
{color:'#fb7c84'},
{color:'#a097ff'},
{color:'#fd0100'}
]
}
},
// #ifdef VUE3
emits: ['init','roll'],
// #endif
methods: {
// 初始化
init({
// 奖品列表
prizeList,
// 默认显示奖品
defaultResults=[],
// 滚动延迟 默认500ms
delay = 500,
// 滚动时间 从开始滚动到所有滚动结束 默认4000ms
duration = 4000,
// 滚动方向
direction = 'up'
}) {
// 校验传入参数
if(typeof delay !=='number'){
console.warn('delay参数应该传入整型');
delay = parseInt(delay)
delay = isNaN(delay)?500:delay;
}
if(typeof duration !=='number'){
console.warn('duration参数应该传入整型');
duration = parseInt(duration)
duration = isNaN(duration)?4000:duration;
}
if(typeof prizeList !=='object'){
return console.error('prizeList参数应该传入数组对象');
}
if(typeof defaultResults !=='object'){
return console.error('defaultResults参数应该传入数组');
}
if(direction !='up' && direction !='down'){
return console.error('direction参数应该传入"up"或者"down"');
}
// 校验完毕
this.direction = direction;
// 最低4秒
if (duration < 4000) {
duration = 4000
};
// 总时长要扣除延迟滚动那一部分时间
duration = duration - (delay*2);
this.prizeList = prizeList;
let shaftList = [];
shaftList.length = 3;
for (let i = 0; i < 3; i++) {
// 打乱顺序和扩充列表
shaftList[i] = this.shuffle(prizeList);
for (let j = 0, len = shaftList[i].length; j < len; j++) {
shaftList[i][j].HMSM_id = 'id_'+i+'_'+j;
}
}
this.shaftList = shaftList;
if(defaultResults.length==3){
this.setTranslateY(defaultResults);
setTimeout(()=>{
this.stop();
},50)
}{
if(this.direction == 'down'){
// 如果是向下滚动,则定位到最底部
let topY = (this.shaftList[1].length-1)*-100;
this.translateY.splice(0,3,topY,topY,topY);
}
}
this.$nextTick(function(){
this.duration = duration;
this.delay = delay;
})
},
// 摇奖
roll({
// 开奖结果
results = null,
// 开奖回调
success = null
}) {
if(this.rollState == 'start'){
return console.warn('正在抽奖哦!');
}
if (typeof results != 'object') {
return console.error('请传入正确的开奖结果参数results');
}
this.rollState = 'start';
// 滚动位置
let res = this.setTranslateY(results);
this.lampTimer&&clearInterval(this.lampTimer);
// 切换灯光
this.startSwitchLamp();
setTimeout(() => {
typeof success == "function" && success(res);
this.stop();
}, this.duration + 1000);
},
// 设定位置
setTranslateY(results){
let res = {
results: []
}
console.log("results: ",results);
for (let i = 0, len = results.length; i < len; i++) {
if(this.direction == 'up'){
// 倒序遍历奖品列表,找出开奖结果的最靠后的下标
for (let k = this.shaftList[i].length - 1; k >= 0; k--) {
if (this.shaftList[i][k].value == results[i]) {
res.results.push(JSON.parse(JSON.stringify(this.shaftList[i][k])));
// 延迟滚动
setTimeout(() => {
this.translateY.splice(i,1,k * -100)
}, i * this.delay)
break;
}
}
}else{
// 顺序遍历奖品列表,找出开奖结果的最靠前的下标
for (let k = 0,len = this.shaftList[i].length; k < len; k++) {
if (this.shaftList[i][k].value == results[i]) {
res.results.push(JSON.parse(JSON.stringify(this.shaftList[i][k])));
// 延迟滚动
setTimeout(() => {
this.translateY.splice(i,1,k * -100);
}, i * this.delay)
break;
}
}
}
}
return res;
},
// 摇奖停止
stop() {
this.rollState = 'stop';
this.lampTimer&&clearInterval(this.lampTimer);
this.$nextTick(function(){
let tolerance = (this.shaftList[0].length/this.prizeList.length-1)*this.prizeList.length*100; //公差
tolerance = this.direction == 'up'&&tolerance||-tolerance
// 停止滚动之后,位移到 最前面/最后面 对应位置,为下一次摇奖留下滚动空间,实现多次摇奖无缝滚动效果
for(let i=0,len=this.translateY.length;i<len;i++){
let topY = this.translateY[i] + tolerance;
this.translateY.splice(i,1,topY)
}
})
},
// 两边的小灯切换
startSwitchLamp(){
this.lampTimer = setInterval(()=>{
if(this.rollState != 'stop'){
// #ifndef APP-PLUS
if(this.direction == 'up'){
this.lamps.push(this.lamps.shift())
}else{
this.lamps.unshift(this.lamps.pop())
}
// #endif
// #ifdef APP-PLUS
if(this.direction == 'up'){
const tmp = this.lamps[0];
[this.lamps[0],this.lamps[1],this.lamps[2],this.lamps[3]] = [this.lamps[1],this.lamps[2],this.lamps[3],this.lamps[4]];
this.$nextTick(function(){
this.lamps[4] = tmp;
})
}else{
const tmp = this.lamps[4];
[this.lamps[1],this.lamps[2],this.lamps[3],this.lamps[4]] = [this.lamps[0],this.lamps[1],this.lamps[2],this.lamps[3]];
this.$nextTick(function(){
this.lamps[0] = tmp;
})
}
// #endif
}
},50)
},
// 打乱顺序
shuffle(arr) {
let tmpArr = JSON.parse(JSON.stringify(arr))
for (let i = 1; i < tmpArr.length; i++) {
const random = Math.floor(Math.random() * (i + 1));
[tmpArr[i], tmpArr[random]] = [tmpArr[random], tmpArr[i]];
}
// 保证最低有40个奖品列表保证滚动摇奖时候效果
// 如果抽奖总时长比较长时候请适当调大40
let len = Math.ceil(40/arr.length); //倍数
let tmpShaft = [];
while (len>0){
tmpShaft.push(...tmpArr);
len--;
}
return JSON.parse(JSON.stringify(tmpShaft));
}
}
}
</script>
<style lang="scss" scoped>
.shell{
width: 630rpx;
height: 352rpx;
background-image: linear-gradient(to right,#6543BC, #754EAE);
border-radius: 50rpx;
display: flex;
align-items: center;
.l,.r{
width: 36rpx;
height: 260rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
.lamp{
width: 14rpx;
height: 14rpx;
// box-shadow: 0 0 2px rgba($color: #000000, $alpha: 0.5);
border-radius: 50%;
}
}
.l{
padding-left: 10rpx;
}
.r{
padding-right: 10rpx;
}
.c{
width: 540rpx;
height: 270rpx;
background-color: #8461E9;
border: solid 1rpx #6443B6;
// box-shadow: 0 0 2px rgba($color: #000000, $alpha: 0.2);
border-radius: 30rpx;
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
.HMSM {
width: 510rpx;
height: 240rpx;
position: relative;
.HMSM-display-bg{
width: 100%;
height: 240rpx;
position: absolute;
left: 0;
top: 0;
z-index: 2;
display: flex;
flex-direction: row;
justify-content: space-between;
.box{
width: 160rpx;
height: 240rpx;
// background-color: #fff;
background-image: linear-gradient(to top,#E4DEFC, rgba(255,255,255,0));
box-shadow: 0 0 2px rgba($color: #fff, $alpha: 1);
border: solid 1px #9d82ea;
box-sizing: border-box;
&:first-child{
border-radius: 20rpx 0 0 20rpx;
}
&:last-child{
border-radius: 0 20rpx 20rpx 0;
}
}
}
.HMSM-display {
position: absolute;
left: 0;
top: 0;
z-index: 3;
width: 100%;
height: 240rpx;
overflow: hidden;
display: flex;
flex-direction: row;
justify-content: space-between;
border-radius: 20rpx;
.HMSM-shaft {
width: 160rpx;
transition-property:none;
transition-duration:0s;
&.roll_animation {
transition-property: transform;
transition-timing-function: cubic-bezier(0.5, 0, 0.1, 1);
}
>view {
width: 160rpx;
height: 240rpx;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background-color: #fff;
image{
width: 160rpx;
height: 240rpx;
}
}
}
}
}
}
}
</style>