前言
想象一下,你正置身于一个充满惊喜与刺激的世界,手中的转盘仿佛掌握着无尽的财富与好运。而今天,我们来看看这个转盘不同于一般的等分转盘,,每个区域都有自己独特的概率,为你带来不同的惊喜与奖励。
在这个Canvas打造的转盘世界里,你可以看到红色、蓝色和绿色等多个主要区域,但它们的面积均等。但被转到的概率不同,红色概率可能占据了大半的转盘,而蓝色和绿色则相对较小。这样的设计,使得每次转动都充满了未知与期待。
效果图展示
一、Canvas绘制转盘
部分内容上一章已讲解,详情请看专栏上一章绘制转盘。
此次与均等分等概率转盘抽奖不同的事,每块概率出现均可控。
采取的方法是对const prizeProbabilities = [0.05, 0.05, 0.05, 0.05, 0.10, 0.2, 0.25, 0.25]; // 各个奖项的概率 进行map() 方法遍历将没数组内每一项依次相加,并加入0和1构成新的数组
['0', '0.05', '0.10', '0.15', '0.20', '0.30', '0.50', '0.75', '1.00']
故在从0~1随机一个数选择奖项,出现的随机数在新数组中概率不同,在通过返回奖项的索引来控制。
1、html、css部分内容
2、绘制转盘背景
3、转盘参数
// 转盘参数
const numSegments = 8; // 奖项数量
const anglePerSegment = Math.PI * 2 / numSegments; // 每个奖项的角度 Math.PI=π
const radius = 150; // 转盘半径
const centerX = canvas.width / 2; // 转盘中心X坐标
const centerY = canvas.height / 2; // 转盘中心Y坐标
const pointerLength = 50; // 指针长度
const prizeProbabilities = [0.05, 0.05, 0.05, 0.05, 0.10, 0.2, 0.25, 0.25]; // 各个奖项的概率
const prizeText = ['奖品1', '奖品2', '奖品3', '奖品4', '奖品5', '奖品6', '奖品7', '奖品8'];
const prizeColors = ['#ff0000', '#8e44ad', '#0000ff', '#32cd32', '#f0f', '#ff5733', '#90ee90', '#888888']; // 各奖项背景颜色
4、开始旋转转盘
// 开始旋转转盘
function startSpinning() {
let lastAngle = 0 // 根据概率计算最终的停止角度
if (sessionStorage.getItem('num')) {
// 使用getItem方法从sessionStorage中获取数据 , 注意要字符串转出num类型
lastAngle = Number(sessionStorage.getItem('num'));
}
let finalAngle = lastAngle;
let spins = 3; // 转盘旋转的圈数,至少转多少圈
let duration = 3000;
let startTime = null;
let elapsedTime = 0;
let isSpinning = true;
let calculateFinal = calculateFinalAngle()
let calculateFinalRandom = 7 - Number(calculateFinal.i)
let min = calculateFinalRandom / 8
let max = (calculateFinalRandom + 1) / 8
const randomNumber = Math.random() * (max - min) + min;
let endAngle = Math.floor((randomNumber + spins) * 360) + (360 - finalAngle); //最终旋转角度
function animate() {
if (!isSpinning) return; // 如果转盘已经停止,则退出动画循环
const now = Date.now();//获取当前时间的毫秒数
if (!startTime) startTime = now;
elapsedTime = now - startTime;
let percentage = Math.min(elapsedTime / duration, 1); // 计算动画进度百分比
let currentAngle = finalAngle + endAngle * percentage; // 计算当前角度
// 清除画布并绘制转盘和指针
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRoulette();//绘制转盘背景
rotateCanvas(currentAngle)//添上转盘旋转动画
// 如果动画未完成,则继续下一帧
if (elapsedTime < duration) {
requestAnimationFrame(animate);
} else {
stopSpinning(currentAngle);
}
}
function rotateCanvas(angle) {
var rotateDiv = document.getElementById('rouletteCanvas');
rotateDiv.style.transform = 'rotate(' + angle + 'deg)';
}
// 停止旋转并显示结果
function stopSpinning(StopAngle) {
isSpinning = false;
let pie = 360
let pie_span = 45
// 使用setItem方法将数据存储到sessionStorage中
// 存储的是字符串类型
sessionStorage.setItem('num', StopAngle % pie);
let x = (pie - StopAngle % pie) / pie_span// x=(360-StopAngle%360)/45
for (let i = 0; i < prizeText.length; i++) {
if (x - i >= 0 && x - i < 1) {
// 显示获得的奖品
alert(`恭喜您获得了:${prizeText[i]}`);
}
}
}
// 开始动画
animate();
}
5、随机选择一个奖项
// 随机选择一个奖项
function getRandomPrize(cumulativeProbabilities) {
const random = Math.random();
for (let i = 0; i < cumulativeProbabilities.length - 1; i++) {
if (random >= cumulativeProbabilities[i] && random < cumulativeProbabilities[i + 1]) {
return { i, random }; // 返回奖项的索引
}
}
// 如果随机数恰好等于1,返回最后一个奖项(虽然这种情况理论上不会发生)
return cumulativeProbabilities.length - 2;
}
6、根据概率计算最终角度
// 根据概率计算最终角度
function calculateFinalAngle() {
const cumulativeProbabilities = prizeProbabilities.reduce((acc, val, idx) => {
const sum = (acc[(acc.length - 1)] || 0) + val;
return idx === 0 ? [sum] : [...acc, sum];
}, []);
// map() 方法遍历 cumulativeProbabilities 数组中的每个元素,并使用 toFixed(2) 将每个元素转换为字符串,结果保留两位小数。
const formattedCumulativeProbabilities = cumulativeProbabilities.map(num => num.toFixed(2));
// 添加初始累积概率为0
formattedCumulativeProbabilities.unshift('0');
// 示例:随机选择一个奖项
const calculateFinal = getRandomPrize(formattedCumulativeProbabilities);
return calculateFinal
}
7、初始化转盘和指针
// 初始化转盘和指针
drawRoulette();
8、页面监听卸载
// 页面监听卸载
window.addEventListener('beforeunload', function () {
sessionStorage.clear();
});
二、完整代码
本章为绘制等分不同概率转盘,完整代码供学习参考,扩展已详细进行注解。Canvas三重玩法请关注本专栏,不容错过呦,快来关注吧!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>等分不同概率转盘抽奖</title>
<style>
.box {
width: 100%;
height: 550px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
canvas {
display: block;
margin: 20px auto;
transform-origin: center center;
}
svg {
position: absolute;
left: 50%;
top: 50%;
width: 70px;
height: 70px;
margin-left: -35px;
margin-top: -65px;
}
</style>
</head>
<body>
<div class="box">
<canvas id="rouletteCanvas" width="302" height="302"></canvas>
<svg t="1713059543990" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="1835" width="200" height="200">
<path
d="M263.577601 776.630449a242.559588 242.559588 0 0 1 138.417007-218.124973L483.818959 23.36268a27.485506 27.485506 0 0 1 54.283873 0l81.824351 535.142796a242.559588 242.559588 0 0 1 138.417006 218.124973 247.369551 247.369551 0 0 1-494.739102 0z"
p-id="1836" fill="#f4ad13"></path>
</svg>
<button onclick="startSpinning()">开始抽奖</button>
</div>
<script>
const canvas = document.getElementById('rouletteCanvas');
const ctx = canvas.getContext('2d');
// 转盘参数
const numSegments = 8;
const anglePerSegment = Math.PI * 2 / numSegments;
const radius = 150;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const pointerLength = 50;
const prizeProbabilities = [0.05, 0.05, 0.05, 0.05, 0.10, 0.2, 0.25, 0.25];
const prizeText = ['奖品1', '奖品2', '奖品3', '奖品4', '奖品5', '奖品6', '奖品7', '奖品8'];
const prizeColors = ['#ff0000', '#8e44ad', '#0000ff', '#32cd32', '#f0f', '#ff5733', '#90ee90', '#888888'];
// 绘制转盘背景
function drawRoulette() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < numSegments; i++) {
const startAngle = (i + 6) * anglePerSegment;
const endAngle = (i + 7) * anglePerSegment;
const x = centerX + Math.cos(startAngle) * radius;
const y = centerY + Math.sin(startAngle) * radius;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle, false);
ctx.lineTo(centerX, centerY);
ctx.closePath();
ctx.fillStyle = prizeColors[i];
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#666';
ctx.stroke();
const textAngle = startAngle + (endAngle - startAngle) / 2;
const textX = centerX + Math.cos(textAngle) * (radius - 40);
const textY = centerY + Math.sin(textAngle) * (radius - 40);
ctx.save();
ctx.translate(textX, textY);
ctx.rotate(textAngle);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#fff';
ctx.fillText(prizeText[i], 0, 0);
ctx.restore();
}
}
// 根据概率计算最终角度
function calculateFinalAngle() {
const cumulativeProbabilities = prizeProbabilities.reduce((acc, val, idx) => {
const sum = (acc[(acc.length - 1)] || 0) + val;
return idx === 0 ? [sum] : [...acc, sum];
}, []);
const formattedCumulativeProbabilities = cumulativeProbabilities.map(num => num.toFixed(2));
formattedCumulativeProbabilities.unshift('0');
const calculateFinal = getRandomPrize(formattedCumulativeProbabilities);
return calculateFinal
}
// 随机选择一个奖项
function getRandomPrize(cumulativeProbabilities) {
const random = Math.random();
for (let i = 0; i < cumulativeProbabilities.length - 1; i++) {
if (random >= cumulativeProbabilities[i] && random < cumulativeProbabilities[i + 1]) {
return { i, random };
}
}
return cumulativeProbabilities.length - 2;
}
// 开始旋转转盘
function startSpinning() {
let lastAngle = 0
if (sessionStorage.getItem('num')) {
lastAngle = Number(sessionStorage.getItem('num'));
}
let finalAngle = lastAngle;
let spins = 3;
let duration = 3000;
let startTime = null;
let elapsedTime = 0;
let isSpinning = true;
let calculateFinal = calculateFinalAngle()
let calculateFinalRandom = 7 - Number(calculateFinal.i)
let min = calculateFinalRandom / 8
let max = (calculateFinalRandom + 1) / 8
const randomNumber = Math.random() * (max - min) + min;
let endAngle = Math.floor((randomNumber + spins) * 360) + (360 - finalAngle);
function animate() {
if (!isSpinning) return;
const now = Date.now();
if (!startTime) startTime = now;
elapsedTime = now - startTime;
let percentage = Math.min(elapsedTime / duration, 1);
let currentAngle = finalAngle + endAngle * percentage;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRoulette();
rotateCanvas(currentAngle)
if (elapsedTime < duration) {
requestAnimationFrame(animate);
} else {
stopSpinning(currentAngle);
}
}
function rotateCanvas(angle) {
var rotateDiv = document.getElementById('rouletteCanvas');
rotateDiv.style.transform = 'rotate(' + angle + 'deg)';
}
// 停止旋转并显示结果
function stopSpinning(StopAngle) {
isSpinning = false;
let pie = 360
let pie_span = 45
sessionStorage.setItem('num', StopAngle % pie);
let x = (pie - StopAngle % pie) / pie_span
for (let i = 0; i < prizeText.length; i++) {
if (x - i >= 0 && x - i < 1) {
alert(`恭喜您获得了:${prizeText[i]}`);
}
}
}
animate();
}
drawRoulette();
window.addEventListener('beforeunload', function () {
sessionStorage.clear();
});
</script>
</body>
</html>