JS水平轮盘开发问题:单次转动保留15个对象并添加Active类
水平轮盘功能优化问题
我现在有一个水平轮盘,打开页面启动时控制台会通过getRandomItem()输出15次轮盘启动相关信息。我在generateItems()里生成了15个轮盘对象,但不知道怎么实现单次转动轮盘时保留这15个对象,同时在start()函数里给最终选中的对象添加active类。相关代码如下:
原JS代码
const items = [ { name: 'flami', img: '', price: 100, priceCount: 0 }, { name: 'flami', img: '', price: 100, priceCount: 0 }, { name: 'flami', img: '', price: 100, priceCount: 0 }, { name: 'flami', img: '', price: 200, priceCount: 0 }, { name: 'flami', img: '', price: 250, priceCount: 0 }, ]; let balance = 2000; let countFlip = 0; function getRandomItem() { let item; while(!item) { item = items[Math.floor(Math.random() * items.length)]; if (item.price > balance * 0.20) { return getRandomItem(); } else { countFlip++; balance -= item.price; if (balance < 0) { console.log('Game over!'); return; } if (countFlip % 10 === 0) { balance += 2000; } console.log(`You got ${item.name} for ${item.price}. Your balance is now ${balance}. ${countFlip}`); } return item; } } let isSpinning = false; function generateItems() { if (isSpinning) { return; } isSpinning = true; document.querySelector('.list').innerHTML = ''; document.querySelector('.scope').innerHTML = `<ul class="list"></ul>`; const list = document.querySelector('.list') list.innerHTML = '' for (let i = 0; i < 15; i++){ item = getRandomItem() const li = document.createElement('li') li.classList.add('list_item') li.innerHTML = ` <img src ="${item.img}" alt="" /> ` list.append(li) } } generateItems() function start() { generateItems() const list = document.querySelector('.list') requestAnimationFrame(() => { list.style.left = '50%' list.style.transform = 'translate3d(-50%, 0, 0)' },0) const item = list.querySelectorAll('li')[7] list.addEventListener('transitionend', () =>{ item.classList.add('active') isSpinning = false; }) }
原HTML代码
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" rel="stylesheet"/> <div class="app"> <i class="pointer fa-solid fa-location-pin" style="font-size: 40px; color: rgb(163, 41, 41);"></i> <div class="scope"> <ul class="list"></ul> </div> <button onclick="start()" class="start">Start</button> </div>
问题分析与修改方案
核心问题是getRandomItem()每次调用都会扣减余额、输出日志,导致生成15个对象时触发15次消费逻辑。下面是针对性修改:
1. 拆分逻辑,分离候选生成与消费处理
把原getRandomItem()拆成两个函数:一个只生成符合条件的候选对象,不碰余额;另一个在转动结束后处理选中对象的消费。
2. 保留轮盘对象集合
新增全局变量存储当前轮盘的15个对象,确保单次转动过程中对象不变。
3. 修正选中与动画逻辑
确保动画结束后只处理一次消费,且正确给选中项加active类。
修改后的完整代码
JS代码
const items = [ { name: 'flami', img: '', price: 100, priceCount: 0 }, { name: 'flami', img: '', price: 100, priceCount: 0 }, { name: 'flami', img: '', price: 100, priceCount: 0 }, { name: 'flami', img: '', price: 200, priceCount: 0 }, { name: 'flami', img: '', price: 250, priceCount: 0 }, ]; let balance = 2000; let countFlip = 0; let isSpinning = false; let currentWheelItems = []; // 存储当前轮盘的15个对象 // 仅生成符合条件的候选对象,不处理余额 function getRandomCandidate() { let item; while (!item) { item = items[Math.floor(Math.random() * items.length)]; // 过滤超出余额20%的选项 if (item.price > balance * 0.20) { item = null; } } return item; } // 生成并渲染轮盘的15个候选对象 function generateWheelItems() { if (isSpinning) return; currentWheelItems = []; // 一次性生成15个候选 for (let i = 0; i < 15; i++) { currentWheelItems.push(getRandomCandidate()); } // 渲染到DOM const list = document.querySelector('.list'); list.innerHTML = ''; currentWheelItems.forEach(item => { const li = document.createElement('li'); li.classList.add('list_item'); li.innerHTML = `<img src="${item.img}" alt="${item.name}" />`; list.append(li); }); } // 转动结束后处理选中对象的余额逻辑 function processSelectedItem(selectedItem) { countFlip++; balance -= selectedItem.price; if (balance < 0) { console.log('Game over!'); return; } if (countFlip % 10 === 0) { balance += 2000; } console.log(`You got ${selectedItem.name} for ${selectedItem.price}. Your balance is now ${balance}. ${countFlip}`); } function start() { if (isSpinning) return; isSpinning = true; // 移除之前的active类 document.querySelectorAll('.list_item.active').forEach(el => el.classList.remove('active')); // 生成新的轮盘候选(如果想固定候选池,注释掉这行) generateWheelItems(); const list = document.querySelector('.list'); // 触发转动动画 requestAnimationFrame(() => { list.style.left = '50%'; list.style.transform = 'translate3d(-50%, 0, 0)'; }); // 选中中间的第7个元素(索引7) const selectedLi = list.querySelectorAll('li')[7]; const selectedItem = currentWheelItems[7]; // 动画结束后执行 const onTransitionEnd = () => { selectedLi.classList.add('active'); processSelectedItem(selectedItem); // 仅处理一次消费 isSpinning = false; list.removeEventListener('transitionend', onTransitionEnd); // 移除监听避免重复触发 }; list.addEventListener('transitionend', onTransitionEnd); } // 页面加载时初始化轮盘 generateWheelItems();
HTML代码(无需修改)
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" rel="stylesheet"/> <div class="app"> <i class="pointer fa-solid fa-location-pin" style="font-size: 40px; color: rgb(163, 41, 41);"></i> <div class="scope"> <ul class="list"></ul> </div> <button onclick="start()" class="start">Start</button> </div>
关键修改说明
- 拆分逻辑后,生成15个对象时不会扣余额,只有转动结束选中后才处理一次消费,控制台只会输出1次结果。
currentWheelItems确保单次转动过程中轮盘对象固定不变。- 移除了原代码中重复创建
<ul class="list">的冗余操作。 - 给
transitionend监听添加了移除逻辑,防止多次触发。
内容的提问来源于stack exchange,提问作者Twenix
相关产品推荐
相关产品推荐

