使用JavaScript+HTML實(shí)現(xiàn)一個(gè)可交互的幸運(yùn)大轉(zhuǎn)盤(pán)
一、幸運(yùn)大轉(zhuǎn)盤(pán)
互動(dòng)元素在各類(lèi)數(shù)字化應(yīng)用中都是提升用戶(hù)體驗(yàn)的重要手段。抽獎(jiǎng)轉(zhuǎn)盤(pán)作為一種經(jīng)典且富有吸引力的互動(dòng)形式,廣泛應(yīng)用于各種場(chǎng)景。無(wú)論是電商平臺(tái)的促銷(xiāo)活動(dòng),還是企業(yè)年會(huì)的抽獎(jiǎng)程序、商場(chǎng)導(dǎo)購(gòu)的互動(dòng)展示,一個(gè)生動(dòng)有趣的轉(zhuǎn)盤(pán)都能有效吸引參與者的注意力,增加活動(dòng)的趣味性和參與度。本文將詳細(xì)介紹如何使用 HTML、CSS 和 JavaScript 來(lái)實(shí)現(xiàn)一個(gè)可交互的幸運(yùn)大轉(zhuǎn)盤(pán)。
二、效果演示
這個(gè)幸運(yùn)大轉(zhuǎn)盤(pán)的彩色扇形區(qū)域代表不同的獎(jiǎng)項(xiàng),中央有一個(gè)明顯的指針指示抽獎(jiǎng)結(jié)果,用戶(hù)點(diǎn)擊轉(zhuǎn)盤(pán)中心區(qū)域可觸發(fā)動(dòng)畫(huà)旋轉(zhuǎn),旋轉(zhuǎn)結(jié)束后會(huì)顯示抽中的獎(jiǎng)項(xiàng),使用緩動(dòng)函數(shù)使旋轉(zhuǎn)更加自然流暢。


三、系統(tǒng)分析
1、頁(yè)面結(jié)構(gòu)
頁(yè)面主體部分由2部分組成。其中 canvas 是整個(gè)轉(zhuǎn)盤(pán)繪制的核心載體,通過(guò) JavaScript 獲取該元素的繪圖上下文后,我們可以使用 Canvas API 進(jìn)行圖形繪制和動(dòng)畫(huà)控制。result 中顯示抽獎(jiǎng)結(jié)果。
<div class="main"> <canvas id="canvas" width="400" height="400"></canvas> <div class="result" id="result"></div> </div>
2、核心功能實(shí)現(xiàn)
2.1 初始化參數(shù)與數(shù)據(jù)
首先定義了一些基礎(chǔ)參數(shù)和數(shù)據(jù),這些變量分別表示獎(jiǎng)項(xiàng)列表、對(duì)應(yīng)的顏色、當(dāng)前旋轉(zhuǎn)角度、旋轉(zhuǎn)狀態(tài)標(biāo)志以及轉(zhuǎn)盤(pán)中心坐標(biāo)和半徑等。
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var prizes = ['一等獎(jiǎng)', '二等獎(jiǎng)', '三等獎(jiǎng)', '四等獎(jiǎng)', '五等獎(jiǎng)', '六等獎(jiǎng)'];
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 繪制轉(zhuǎn)盤(pán)
繪制轉(zhuǎn)盤(pán)的過(guò)程主要是遍歷所有獎(jiǎng)項(xiàng),計(jì)算每個(gè)扇形的角度范圍并依次繪制,每一塊扇形都根據(jù)當(dāng)前旋轉(zhuǎn)角度進(jìn)行調(diào)整,確保動(dòng)畫(huà)過(guò)程中扇形位置正確更新。
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 繪制指針
指針由三角形和圓形組合而成,使用漸變填充增加視覺(jué)層次感。
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 動(dòng)畫(huà)旋轉(zhuǎn)與抽獎(jiǎng)邏輯
當(dāng)用戶(hù)點(diǎn)擊轉(zhuǎn)盤(pán)中心時(shí),觸發(fā)抽獎(jiǎng)動(dòng)畫(huà),這里實(shí)現(xiàn)了平滑減速的旋轉(zhuǎn)動(dòng)畫(huà),通過(guò)三次貝塞爾曲線模擬 “ease-out” 緩動(dòng)效果,讓旋轉(zhuǎn)看起來(lái)更自然。旋轉(zhuǎn)結(jié)束時(shí),通過(guò)角度計(jì)算確定指針指向的具體獎(jiǎng)項(xiàng)。
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 緩動(dòng)
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);
}
四、擴(kuò)展建議
- 增加音效:可以在旋轉(zhuǎn)開(kāi)始和結(jié)束時(shí)播放音效,增強(qiáng)體驗(yàn)感受
- 自定義獎(jiǎng)項(xiàng):提供配置面板讓用戶(hù)自定義獎(jiǎng)項(xiàng)名稱(chēng)和數(shù)量
- 記錄抽獎(jiǎng)歷史:保存每次抽獎(jiǎng)的結(jié)果,方便統(tǒng)計(jì)分析
- 限制抽獎(jiǎng)次數(shù):加入每日抽獎(jiǎng)次數(shù)限制或積分兌換機(jī)制
五、完整代碼
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>幸運(yùn)大轉(zhuǎn)盤(pán)</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>幸運(yùn)大轉(zhuǎn)盤(pán)</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 = ['一等獎(jiǎng)', '二等獎(jiǎng)', '三等獎(jiǎng)', '四等獎(jiǎng)', '五等獎(jiǎng)', '六等獎(jiǎng)'];
var colors = ['#FFADAD', '#FFD6A5', '#FDFFB6', '#CAFFBF', '#9BF6FF', '#A0C4FF'];
var currentRotation = 0, isSpinning = false;
var cx = canvas.width / 2, cy = canvas.height / 2, r = 200;
// 繪制轉(zhuǎn)盤(pán)
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();
}
// 抽獎(jiǎng)
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 緩動(dòng)
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);
}
// 添加點(diǎn)擊事件
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>
到此這篇關(guān)于使用JavaScript+HTML實(shí)現(xiàn)一個(gè)可交互的幸運(yùn)大轉(zhuǎn)盤(pán)的文章就介紹到這了,更多相關(guān)JavaScript HTML幸運(yùn)大轉(zhuǎn)盤(pán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
JavaScript如何從listbox里同時(shí)刪除多個(gè)項(xiàng)目
一個(gè)頁(yè)面元素appendchild追加到另一個(gè)頁(yè)面元素的問(wèn)題
javascript獲取網(wǎng)頁(yè)寬高方法匯總
javascript實(shí)現(xiàn)簡(jiǎn)單計(jì)算器效果【推薦】
用innerHTML?。Ψ?hào)副值給文本框后會(huì)變成&amp;的方法

