You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

react-roulette-pro组件如何控制中奖概率?避免高价值奖品过度发放

解决react-roulette-pro稀有度设置后的轮盘异常问题

你遇到的问题核心是修改prizeList后,原有的固定目标索引计算逻辑失效,加上列表生成方式不规范,导致轮盘无法正确定位目标奖品、滚动逻辑混乱。以下是针对性的解决方案:

问题根源拆解

  1. 原代码用随机重复数组生成prizeList,调整稀有度后列表结构完全变化,之前硬编码的prizeIndex = prizes.length *4 + winPrizeIndex完全不匹配新列表的索引。
  2. 直接修改列表项的出现次数后,可能出现目标奖品在列表中位置过于靠前,轮盘滚动距离过短导致视觉上“没转起来”,甚至触发组件的滚动逻辑异常。
  3. 重复触发start的toggle状态(轮盘未停时再次点击),会导致组件内部状态冲突,引发卡顿。

分步解决方案

1. 按稀有度可控生成奖品列表

给每个奖品配置权重(权重越高出现次数越多,稀有奖品权重低),然后根据权重生成有序的prizeList,避免随机重复带来的不可控性:

// 给奖品添加权重,比如第0个是高价值稀有奖品,权重设为1,其他设为5
const prizesWithWeight = [
    { image: 'https://i.ibb.co/6Z6Xm9d/good-1.png', weight: 1 }, // 稀有奖品
    { image: 'https://i.ibb.co/T1M05LR/good-2.png', weight: 5 },
    { image: 'https://i.ibb.co/Qbm8cNL/good-3.png', weight: 5 },
    { image: 'https://i.ibb.co/5Tpfs6W/good-4.png', weight: 5 },
    { image: 'https://i.ibb.co/64k8D1c/good-5.png', weight: 5 },
];

// 根据权重生成prizeList
const generatePrizeList = (prizes) => {
    let list = [];
    prizes.forEach(prize => {
        // 按权重重复添加奖品
        for(let i=0; i < prize.weight; i++){
            list.push({...prize});
        }
    });
    // 重复多轮,让轮盘可以滚动多圈
    const repeatedList = [...list, ...list, ...list, ...list];
    // 给每个项添加唯一id
    return repeatedList.map((prize) => ({
        ...prize,
        id: crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`
    }));
};

const prizeList = generatePrizeList(prizesWithWeight);

2. 动态计算目标奖品索引

不再硬编码索引,而是在prizeList中找到目标奖品的最后几个有效索引(确保轮盘至少滚动1-2圈再停下):

const WheelComponent = () => {
    const [start, setStart] = useState(false);
    const [isSpinning, setIsSpinning] = useState(false); // 新增状态控制滚动中不可点击
    const targetPrizeIndex = useMemo(() => {
        // 找到所有稀有奖品在prizeList中的索引
        const targetIndices = prizeList.reduce((acc, prize, index) => {
            if(prize.image === prizesWithWeight[0].image){
                acc.push(index);
            }
            return acc;
        }, []);
        // 返回最后一个索引(让轮盘滚动更长距离)
        return targetIndices[targetIndices.length - 1];
    }, []);

    const handleStart = () => {
        if(isSpinning) return;
        setIsSpinning(true);
        setStart(true);
    };

    const handlePrizeDefined = () => {
        console.log('🥳 中奖啦!🥳');
        setIsSpinning(false);
        setStart(false); // 重置start状态,准备下一次滚动
    };

    return (
        <div>
            <RoulettePro
                prizes={prizeList}
                prizeIndex={targetPrizeIndex}
                start={start}
                onPrizeDefined={handlePrizeDefined}
            />
            <button onClick={handleStart} disabled={isSpinning}>
                {isSpinning ? '滚动中...' : '开始抽奖'}
            </button>
        </div>
    );
};

3. 修复状态切换异常

新增isSpinning状态,在轮盘滚动过程中禁用按钮,避免重复触发start导致的状态冲突,彻底解决卡顿问题。

关键注意事项

  • 确保prizeList中的每个项id唯一:使用crypto.randomUUID(现代浏览器支持)或可靠的自定义生成函数,避免组件因key重复导致渲染异常。
  • 目标索引必须是prizeList中的有效索引:不要用超出列表长度的硬编码值,否则轮盘会无法定位。
  • 生成prizeList时重复多轮:让列表长度足够长,保证轮盘滚动时视觉上有“转多圈”的效果,同时避免目标索引过于靠前导致的滚动异常。

内容的提问来源于stack exchange,提问作者Gabi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 06:12:37