223 lines
5.4 KiB
Vue
223 lines
5.4 KiB
Vue
<template>
|
|
<view
|
|
class="grid-lottery"
|
|
:style="{
|
|
height: gridLotteryHeight,
|
|
}"
|
|
>
|
|
<view class="lottery-list">
|
|
<view
|
|
v-for="(item, index) in formateList"
|
|
:key="item.id"
|
|
class="lottery-item"
|
|
:style="{
|
|
width: `${itemWidth}%`,
|
|
height: `${itemHeight}${itemHeightUnit}`,
|
|
transform: `translate(${place(index).x}%, ${place(index).y}${itemHeightUnit})`,
|
|
}"
|
|
>
|
|
<view class="relative">
|
|
<slot name="lottery-item" :data="item" :index="index" :currIndex="currIndex" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view
|
|
class="start-button"
|
|
:style="{
|
|
left: `${itemWidth}%`,
|
|
right: `${itemWidth}%`,
|
|
top: `${itemWidth}%`,
|
|
bottom: `${itemWidth}%`,
|
|
}"
|
|
@tap="onStart"
|
|
>
|
|
<slot name="start-button"></slot>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'GridLottery',
|
|
props: {
|
|
// 转盘转动间隔
|
|
interval: {
|
|
type: Number,
|
|
default: 50,
|
|
},
|
|
// 是否逐渐减速
|
|
isIntervalIncrease: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
// 减速开始时间
|
|
intervalIncreaseStartTime: {
|
|
type: Number,
|
|
default: 3000,
|
|
},
|
|
// 最小速度
|
|
maxInterval: {
|
|
type: Number,
|
|
default: 500,
|
|
},
|
|
// 奖项外框高度
|
|
itemHeight: {
|
|
type: Number,
|
|
default: 100,
|
|
},
|
|
// 高度单位
|
|
itemHeightUnit: {
|
|
type: String,
|
|
default: 'rpx',
|
|
},
|
|
// 总数必须为>=8 且被4整除
|
|
list: {
|
|
type: Array,
|
|
default() {
|
|
// return [
|
|
// {
|
|
// id: string,
|
|
// sort: number
|
|
// }
|
|
// ];
|
|
return []
|
|
},
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
transform: {
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
currIndex: 0,
|
|
startFlag: false,
|
|
}
|
|
},
|
|
computed: {
|
|
gridLotteryHeight({ itemHeight, formateList, colunmCount, itemHeightUnit }) {
|
|
return `${itemHeight * ((formateList.length - colunmCount * 2) / 2 + 2)}${itemHeightUnit}`
|
|
},
|
|
itemWidth() {
|
|
return 100 / this.colunmCount
|
|
},
|
|
colunmCount() {
|
|
const len = this.list.length
|
|
if (len > 8) {
|
|
return 3 + Math.floor((len - 8) / 4)
|
|
}
|
|
return 3
|
|
},
|
|
|
|
formateList() {
|
|
const len = this.list.length
|
|
if (len > 7 && (len - 8) % 4 !== 0) {
|
|
console.warn(
|
|
'请检查数组,总数必须为>=8 且大余8部分部分能被4整除,如长度为8、12、16...的数组',
|
|
)
|
|
}
|
|
const _list = this.list || []
|
|
const formateList = _list.sort((a, b) => a.sort - b.sort)
|
|
return formateList
|
|
},
|
|
},
|
|
methods: {
|
|
// 通过索引获取定位
|
|
place(index) {
|
|
const len = this.list.length
|
|
// 序号
|
|
const indexToOrder = index + 1
|
|
if (indexToOrder > this.colunmCount) {
|
|
// 去除上下行,总行数
|
|
const rowCount = (len - this.colunmCount * 2) / 2
|
|
// 非处于左侧
|
|
if (index > this.colunmCount + rowCount) {
|
|
// 处于右侧
|
|
if (indexToOrder > this.colunmCount * 2 + rowCount) {
|
|
return {
|
|
x: 0,
|
|
y: this.itemHeight * (rowCount * 2 + this.colunmCount * 2 - index),
|
|
}
|
|
}
|
|
// 处于底部
|
|
return {
|
|
x: 100 * (this.colunmCount * 2 + rowCount - indexToOrder),
|
|
y: this.itemHeight * (rowCount + 1),
|
|
}
|
|
}
|
|
// 处于右侧
|
|
return {
|
|
x: 100 * (this.colunmCount - 1),
|
|
y: this.itemHeight * (indexToOrder - this.colunmCount),
|
|
}
|
|
}
|
|
// 处于顶部
|
|
return {
|
|
x: 100 * index,
|
|
y: 0,
|
|
}
|
|
},
|
|
/**
|
|
* 开始抽奖
|
|
* @param {string|number} id 获奖id
|
|
* @param {number} minTime 转盘最小持续时间
|
|
*/
|
|
async start(id, minTime) {
|
|
if (this.startFlag) return
|
|
const len = this.formateList.length
|
|
const activeIndex = this.formateList.findIndex((item) => item.id === id)
|
|
if (activeIndex === -1) {
|
|
const errorText = `没有找到该中奖id ${id}`
|
|
console.error(errorText)
|
|
return this.$emit('onError', errorText)
|
|
}
|
|
let time = minTime
|
|
// 动画速度
|
|
let interval = this.interval
|
|
const startRotate = () => {
|
|
this.startFlag = true
|
|
clearTimeout(this.timer)
|
|
this.timer = setTimeout(() => {
|
|
// 抽奖结束
|
|
if (time < 1 && this.currIndex === activeIndex) {
|
|
this.startFlag = false
|
|
clearTimeout(this.timer)
|
|
return this.$emit('onEnd', this.formateList[activeIndex])
|
|
}
|
|
time -= interval
|
|
if (time < 3000 && this.isIntervalIncrease) {
|
|
// 调整动画速度
|
|
interval = interval >= this.maxInterval ? this.maxInterval : interval + 20
|
|
}
|
|
this.currIndex = this.currIndex >= len - 1 ? 0 : this.currIndex + 1
|
|
this.transform = this.place(this.currIndex)
|
|
return startRotate()
|
|
}, interval)
|
|
}
|
|
startRotate()
|
|
},
|
|
onStart() {
|
|
this.$emit('onStart')
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.grid-lottery {
|
|
position: relative;
|
|
.start-button {
|
|
position: absolute;
|
|
}
|
|
.lottery-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
.lottery-item {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
}
|
|
}
|
|
}
|
|
</style>
|