使用JavaScript+HTML实现一个可交互的幸运大转盘
作者:bjzhang75
一、幸运大转盘
互动元素在各类数字化应用中都是提升用户体验的重要手段。抽奖转盘作为一种经典且富有吸引力的互动形式,广泛应用于各种场景。无论是电商平台的促销活动,还是企业年会的抽奖程序、商场导购的互动展示,一个生动有趣的转盘都能有效吸引参与者的注意力,增加活动的趣味性和参与度。本文将详细介绍如何使用 HTML、CSS 和 JavaScript 来实现一个可交互的幸运大转盘。
二、效果演示
这个幸运大转盘的彩色扇形区域代表不同的奖项,中央有一个明显的指针指示抽奖结果,用户点击转盘中心区域可触发动画旋转,旋转结束后会显示抽中的奖项,使用缓动函数使旋转更加自然流畅。


三、系统分析
1、页面结构
页面主体部分由2部分组成。其中 canvas 是整个转盘绘制的核心载体,通过 JavaScript 获取该元素的绘图上下文后,我们可以使用 Canvas API 进行图形绘制和动画控制。result 中显示抽奖结果。
<div class="main"> <canvas id="canvas" width="400" height="400"></canvas> <div class="result" id="result"></div> </div>
2、核心功能实现
2.1 初始化参数与数据
首先定义了一些基础参数和数据,这些变量分别表示奖项列表、对应的颜色、当前旋转角度、旋转状态标志以及转盘中心坐标和半径等。
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var prizes = ['一等奖', '二等奖', '三等奖', '四等奖', '五等奖', '六等奖'];
var colors = ['#FFADAD', '#FFD6A5', '#FDFFB6', '#CAFFBF', '#9BF6FF', '#A0C4FF'];
var currentRotation = 0, isSpinning = false;
var cx = canvas.width / 2, cy = canvas.height / 2, r = 200;
2.2 绘制转盘
绘制转盘的过程主要是遍历所有奖项,计算每个扇形的角度范围并依次绘制,每一块扇形都根据当前旋转角度进行调整,确保动画过程中扇形位置正确更新。
function drawWheel() {
var angle = 2 * Math.PI / prizes.length;
prizes.forEach((p, i) => {
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, r, i * angle + currentRotation, (i + 1) * angle + currentRotation);
ctx.closePath();
ctx.fillStyle = colors[i];
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(i * angle + angle / 2 + currentRotation);
ctx.fillStyle = '#B14113';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.fillText(p, r * 0.7, 5);
ctx.restore();
});
}
2.3 绘制指针
指针由三角形和圆形组合而成,使用渐变填充增加视觉层次感。
function drawPointer() {
var len = 80;
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0, -len);
ctx.lineTo(-8, 0);
ctx.lineTo(8, 0);
ctx.closePath();
var g = ctx.createLinearGradient(0, 0, 0, -len);
g.addColorStop(0, '#ff4757');
g.addColorStop(1, '#ff3838');
ctx.fillStyle = g;
ctx.fill();
ctx.strokeStyle = '#c44569';
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, 12, 0, Math.PI * 2);
var cg = ctx.createRadialGradient(0, 0, 0, 0, 0, 10);
cg.addColorStop(0, '#fff');
cg.addColorStop(1, '#ff6b6b');
ctx.fillStyle = cg;
ctx.fill();
ctx.strokeStyle = '#c44569';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
2.4 动画旋转与抽奖逻辑
当用户点击转盘中心时,触发抽奖动画,这里实现了平滑减速的旋转动画,通过三次贝塞尔曲线模拟 “ease-out” 缓动效果,让旋转看起来更自然。旋转结束时,通过角度计算确定指针指向的具体奖项。
function spin() {
if (isSpinning) return;
isSpinning = true;
document.getElementById('result').textContent = '';
var minRot = 3 * 2 * Math.PI, maxRot = 6 * 2 * Math.PI;
var addRot = Math.random() * (maxRot - minRot) + minRot;
var targetRotation = currentRotation + addRot;
var duration = 4000;
var start = performance.now();
function frame(t) {
var elapsed = t - start;
var progress = Math.min(elapsed / duration, 1);
// ease-out 缓动
var eased = 1 - Math.pow(1 - progress, 3);
currentRotation = startRotation + (targetRotation - startRotation) * eased;
draw();
if (progress < 1) {
requestAnimationFrame(frame);
} else {
var pointerAngle = 3 * Math.PI / 2;
var norm = (2 * Math.PI - currentRotation % (2 * Math.PI)) % (2 * Math.PI);
var hit = (norm + pointerAngle) % (2 * Math.PI);
var anglePer = 2 * Math.PI / prizes.length;
var idx = Math.floor(hit / anglePer) % prizes.length;
document.getElementById('result').textContent = `恭喜获得:${prizes[idx]}!`;
isSpinning = false;
}
}
var startRotation = currentRotation;
requestAnimationFrame(frame);
}
四、扩展建议
- 增加音效:可以在旋转开始和结束时播放音效,增强体验感受
- 自定义奖项:提供配置面板让用户自定义奖项名称和数量
- 记录抽奖历史:保存每次抽奖的结果,方便统计分析
- 限制抽奖次数:加入每日抽奖次数限制或积分兑换机制
五、完整代码
git地址:https://gitee.com/ironpro/hjdemo/blob/master/wheel/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>幸运大转盘</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #f5f5f5;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: #FF6B35;
color: white;
padding: 20px;
text-align: center;
}
.header h1 {
font-size: 28px;
font-weight: 500;
}
.main {
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
height: 500px;
}
#canvas {
width: 400px;
height: 400px;
border-radius: 50%;
box-shadow: 0 0 30px rgba(0, 0, 0, .3);
}
.result {
text-align: center;
font-size: 24px;
font-weight: bold;
margin-top: 20px;
color: #333;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>幸运大转盘</h1>
</div>
<div class="main">
<canvas id="canvas" width="400" height="400"></canvas>
<div class="result" id="result"></div>
</div>
</div>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var prizes = ['一等奖', '二等奖', '三等奖', '四等奖', '五等奖', '六等奖'];
var colors = ['#FFADAD', '#FFD6A5', '#FDFFB6', '#CAFFBF', '#9BF6FF', '#A0C4FF'];
var currentRotation = 0, isSpinning = false;
var cx = canvas.width / 2, cy = canvas.height / 2, r = 200;
// 绘制转盘
function drawWheel() {
var angle = 2 * Math.PI / prizes.length;
prizes.forEach((p, i) => {
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, r, i * angle + currentRotation, (i + 1) * angle + currentRotation);
ctx.closePath();
ctx.fillStyle = colors[i];
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(i * angle + angle / 2 + currentRotation);
ctx.fillStyle = '#B14113';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.fillText(p, r * 0.7, 5);
ctx.restore();
});
}
// 绘制指针
function drawPointer() {
var len = 80;
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0, -len);
ctx.lineTo(-8, 0);
ctx.lineTo(8, 0);
ctx.closePath();
var g = ctx.createLinearGradient(0, 0, 0, -len);
g.addColorStop(0, '#ff4757');
g.addColorStop(1, '#ff3838');
ctx.fillStyle = g;
ctx.fill();
ctx.strokeStyle = '#c44569';
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, 12, 0, Math.PI * 2);
var cg = ctx.createRadialGradient(0, 0, 0, 0, 0, 10);
cg.addColorStop(0, '#fff');
cg.addColorStop(1, '#ff6b6b');
ctx.fillStyle = cg;
ctx.fill();
ctx.strokeStyle = '#c44569';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
// 绘制
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawWheel();
drawPointer();
}
// 抽奖
function spin() {
if (isSpinning) return;
isSpinning = true;
document.getElementById('result').textContent = '';
var minRot = 3 * 2 * Math.PI, maxRot = 6 * 2 * Math.PI;
var addRot = Math.random() * (maxRot - minRot) + minRot;
var targetRotation = currentRotation + addRot;
var duration = 4000;
var start = performance.now();
function frame(t) {
var elapsed = t - start;
var progress = Math.min(elapsed / duration, 1);
// ease-out 缓动
var eased = 1 - Math.pow(1 - progress, 3);
currentRotation = startRotation + (targetRotation - startRotation) * eased;
draw();
if (progress < 1) {
requestAnimationFrame(frame);
} else {
var pointerAngle = 3 * Math.PI / 2;
var norm = (2 * Math.PI - currentRotation % (2 * Math.PI)) % (2 * Math.PI);
var hit = (norm + pointerAngle) % (2 * Math.PI);
var anglePer = 2 * Math.PI / prizes.length;
var idx = Math.floor(hit / anglePer) % prizes.length;
document.getElementById('result').textContent = `恭喜获得:${prizes[idx]}!`;
isSpinning = false;
}
}
var startRotation = currentRotation;
requestAnimationFrame(frame);
}
// 添加点击事件
canvas.addEventListener('click', function (e) {
var rect = canvas.getBoundingClientRect();
var x = e.clientX - rect.left - cx;
var y = e.clientY - rect.top - cy;
var distance = Math.sqrt(x * x + y * y);
if (distance < 30) {
spin();
}
});
draw();
</script>
</body>
</html>
到此这篇关于使用JavaScript+HTML实现一个可交互的幸运大转盘的文章就介绍到这了,更多相关JavaScript HTML幸运大转盘内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
