vue实现滚动抽奖
Vue 实现滚动抽奖
基本思路
滚动抽奖通常通过动态改变奖品列表的显示位置,配合动画效果实现。核心是利用 Vue 的响应式数据和 CSS 过渡动画。
实现步骤
数据准备 在 Vue 的 data 中定义奖品列表和当前选中索引:
data() {
return {
prizes: ['奖品1', '奖品2', '奖品3', '奖品4', '奖品5'],
currentIndex: 0,
isRolling: false
}
}
模板结构 使用 v-for 渲染奖品列表,并通过样式控制显示区域:

<div class="lottery-container">
<div class="lottery-wheel" :style="{ transform: `translateY(${-currentIndex * 100}px)` }">
<div class="prize-item" v-for="(prize, index) in prizes" :key="index">
{{ prize }}
</div>
</div>
<button @click="startRoll" :disabled="isRolling">开始抽奖</button>
</div>
CSS 样式 设置固定高度的显示区域和滚动动画:
.lottery-container {
height: 100px;
overflow: hidden;
position: relative;
}
.lottery-wheel {
transition: transform 0.5s ease-out;
}
.prize-item {
height: 100px;
display: flex;
align-items: center;
justify-content: center;
}
抽奖逻辑 实现开始抽奖的方法,包含随机滚动和减速停止效果:

methods: {
startRoll() {
if (this.isRolling) return;
this.isRolling = true;
let speed = 100;
const duration = 3000;
const startTime = Date.now();
const roll = () => {
const elapsed = Date.now() - startTime;
const progress = elapsed / duration;
if (progress < 1) {
speed = 100 * (1 - progress);
this.currentIndex = (this.currentIndex + 1) % this.prizes.length;
setTimeout(roll, speed);
} else {
const finalIndex = Math.floor(Math.random() * this.prizes.length);
this.currentIndex = finalIndex;
this.isRolling = false;
}
};
roll();
}
}
高级优化
平滑停止 使用 easing 函数实现更自然的减速效果:
function easeOutQuart(t) {
return 1 - Math.pow(1 - t, 4);
}
// 在roll方法中修改progress计算
const progress = easeOutQuart(elapsed / duration);
多次循环 确保奖品至少滚动完整数圈:
const minCycles = 3;
const totalItems = minCycles * this.prizes.length + finalIndex;
奖品高亮 添加当前选中奖品的样式类:
<div class="prize-item" :class="{ 'active': index === currentIndex }">
注意事项
- 移动端需考虑 touch 事件支持
- 奖品数量变化时需要重置状态
- 长时间滚动可能导致性能问题,应设置最大持续时间
- 考虑添加音效增强用户体验
完整示例
export default {
data() {
return {
prizes: ['手机', '耳机', '优惠券', '谢谢参与', '积分'],
currentIndex: 0,
isRolling: false
}
},
methods: {
startRoll() {
if (this.isRolling) return;
this.isRolling = true;
const duration = 3000;
const startTime = Date.now();
const finalIndex = Math.floor(Math.random() * this.prizes.length);
const minCycles = 3;
const totalItems = minCycles * this.prizes.length + finalIndex;
const animate = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const easedProgress = this.easeOutQuart(progress);
this.currentIndex = Math.floor(easedProgress * totalItems) % this.prizes.length;
if (progress < 1) {
requestAnimationFrame(animate);
} else {
this.currentIndex = finalIndex;
this.isRolling = false;
}
};
requestAnimationFrame(animate);
},
easeOutQuart(t) {
return 1 - Math.pow(1 - t, 4);
}
}
}






