Canvas打造三重玩法转盘:等分不同概率转盘抽奖(3)

本文介绍了如何使用Canvas在网页上创建一个具有可变概率分布的转盘抽奖游戏,通过控制各个奖项的概率和转盘的旋转,实现动态和随机的结果。
摘要由CSDN通过智能技术生成

前言

想象一下,你正置身于一个充满惊喜与刺激的世界,手中的转盘仿佛掌握着无尽的财富与好运。而今天,我们来看看这个转盘不同于一般的等分转盘,,每个区域都有自己独特的概率,为你带来不同的惊喜与奖励。

在这个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>

效果图展示

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炑焽

蓝海新风口,高薪稳定不内卷

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值