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


三、系統(tǒng)分析
1、頁面結(jié)構(gòu)
頁面主體部分由2部分組成。其中 canvas 是整個轉(zhuǎn)盤繪制的核心載體,通過 JavaScript 獲取該元素的繪圖上下文后,我們可以使用 Canvas API 進(jìn)行圖形繪制和動畫控制。result 中顯示抽獎結(jié)果。
<div class="main"> <canvas id="canvas" width="400" height="400"></canvas> <div class="result" id="result"></div> </div>
2、核心功能實現(xiàn)
2.1 初始化參數(shù)與數(shù)據(jù)
首先定義了一些基礎(chǔ)參數(shù)和數(shù)據(jù),這些變量分別表示獎項列表、對應(yīng)的顏色、當(dāng)前旋轉(zhuǎn)角度、旋轉(zhuǎn)狀態(tài)標(biāo)志以及轉(zhuǎn)盤中心坐標(biāo)和半徑等。
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 繪制轉(zhuǎn)盤
繪制轉(zhuǎn)盤的過程主要是遍歷所有獎項,計算每個扇形的角度范圍并依次繪制,每一塊扇形都根據(jù)當(dāng)前旋轉(zhuǎn)角度進(jìn)行調(diào)整,確保動畫過程中扇形位置正確更新。
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 動畫旋轉(zhuǎn)與抽獎邏輯
當(dāng)用戶點擊轉(zhuǎn)盤中心時,觸發(fā)抽獎動畫,這里實現(xiàn)了平滑減速的旋轉(zhuǎn)動畫,通過三次貝塞爾曲線模擬 “ease-out” 緩動效果,讓旋轉(zhuǎn)看起來更自然。旋轉(zhuǎn)結(jié)束時,通過角度計算確定指針指向的具體獎項。
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);
}
四、擴(kuò)展建議
- 增加音效:可以在旋轉(zhuǎn)開始和結(jié)束時播放音效,增強(qiáng)體驗感受
- 自定義獎項:提供配置面板讓用戶自定義獎項名稱和數(shù)量
- 記錄抽獎歷史:保存每次抽獎的結(jié)果,方便統(tǒng)計分析
- 限制抽獎次數(shù):加入每日抽獎次數(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>幸運大轉(zhuǎ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>幸運大轉(zhuǎ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 = ['一等獎', '二等獎', '三等獎', '四等獎', '五等獎', '六等獎'];
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)盤
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>
到此這篇關(guān)于使用JavaScript+HTML實現(xiàn)一個可交互的幸運大轉(zhuǎn)盤的文章就介紹到這了,更多相關(guān)JavaScript HTML幸運大轉(zhuǎn)盤內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js 復(fù)制功能 支持 for IE/FireFox/mozilla/ns
js 復(fù)制功能 支持 for IE/FireFox/mozilla/ns...2007-11-11
JavaScript判斷變量是否為undefined的兩種寫法區(qū)別
這篇文章主要是對JavaScript判斷變量是否為undefined的兩種寫法區(qū)別進(jìn)行了詳細(xì)的介紹,需要的朋友可以過來參考下,希望對大家有所幫助2013-12-12
JS實現(xiàn)樹形結(jié)構(gòu)與數(shù)組結(jié)構(gòu)相互轉(zhuǎn)換并在樹形結(jié)構(gòu)中查找對象
這篇文章介紹了JS實現(xiàn)樹形結(jié)構(gòu)與數(shù)組結(jié)構(gòu)相互轉(zhuǎn)換并在樹形結(jié)構(gòu)中查找對象的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06
淺析document.createDocumentFragment()與js效率
對于循環(huán)批量操作頁面的DOM有很大幫助!利用文檔碎片處理,然后一次性append,并且使用原生的javascript語句操作2013-07-07
?javascript數(shù)組中的lastIndexOf方法
這篇文章主要介紹了?javascript數(shù)組中的lastIndexOf方法,該方法可返回一個指定的字符串值最后出現(xiàn)的位置,在一個字符串中的指定位置從后向前搜索,下文詳細(xì)內(nèi)容需要的小伙伴可以參考一下2022-03-03
js實現(xiàn)多行文本框統(tǒng)計剩余字?jǐn)?shù)功能
本文主要介紹了js實現(xiàn)多行文本框統(tǒng)計剩余字?jǐn)?shù)功能的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-03-03
js window.onload 加載多個函數(shù)和追加函數(shù)詳解
本篇文章主要是對js window.onload 加載多個函數(shù)和追加函數(shù)進(jìn)行了介紹,需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01

