| <!DOCTYPE html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>阳光点名转盘</title> |
| <style> |
| :root { |
| --primary-color: #FF9E45; |
| --secondary-color: #FFB347; |
| --accent-color: #FF6B6B; |
| --light-color: #FFF8E6; |
| --text-color: #3D3D3D; |
| --shadow-color: rgba(255, 158, 69, 0.3); |
| } |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| } |
| |
| body { |
| background: linear-gradient(135deg, var(--light-color) 0%, #FFE8CC 100%); |
| color: var(--text-color); |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| padding: 2rem; |
| } |
| |
| .container { |
| max-width: 1200px; |
| width: 100%; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| gap: 2rem; |
| } |
| |
| header { |
| text-align: center; |
| margin-bottom: 1rem; |
| } |
| |
| h1 { |
| font-size: 2.5rem; |
| color: var(--primary-color); |
| text-shadow: 2px 2px 4px var(--shadow-color); |
| margin-bottom: 0.5rem; |
| } |
| |
| .subtitle { |
| font-size: 1.2rem; |
| color: var(--accent-color); |
| font-weight: 300; |
| } |
| |
| .setup-panel { |
| background-color: white; |
| padding: 2rem; |
| border-radius: 1rem; |
| box-shadow: 0 10px 30px var(--shadow-color); |
| width: 100%; |
| max-width: 500px; |
| transition: all 0.3s ease; |
| } |
| |
| .setup-panel.minimized { |
| padding: 1rem; |
| max-width: 300px; |
| cursor: pointer; |
| } |
| |
| .setup-panel.minimized .form-group { |
| display: none; |
| } |
| |
| .setup-panel.minimized .panel-title:after { |
| content: " (点击展开)"; |
| font-size: 0.8rem; |
| color: var(--accent-color); |
| } |
| |
| .panel-title { |
| text-align: center; |
| margin-bottom: 1.5rem; |
| color: var(--primary-color); |
| } |
| |
| .form-group { |
| margin-bottom: 1.5rem; |
| } |
| |
| label { |
| display: block; |
| margin-bottom: 0.5rem; |
| font-weight: 500; |
| } |
| |
| input, select { |
| width: 100%; |
| padding: 0.8rem; |
| border: 2px solid #FFD8B1; |
| border-radius: 0.5rem; |
| font-size: 1rem; |
| background-color: var(--light-color); |
| transition: all 0.3s; |
| } |
| |
| input:focus, select:focus { |
| outline: none; |
| border-color: var(--primary-color); |
| box-shadow: 0 0 0 3px var(--shadow-color); |
| } |
| |
| .btn { |
| background-color: var(--primary-color); |
| color: white; |
| border: none; |
| padding: 0.8rem 1.5rem; |
| border-radius: 0.5rem; |
| font-size: 1rem; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.3s; |
| box-shadow: 0 4px 10px var(--shadow-color); |
| } |
| |
| .btn:hover { |
| background-color: var(--accent-color); |
| transform: translateY(-2px); |
| box-shadow: 0 6px 15px var(--shadow-color); |
| } |
| |
| .btn-secondary { |
| background-color: var(--light-color); |
| color: var(--primary-color); |
| border: 2px solid var(--primary-color); |
| } |
| |
| .btn-secondary:hover { |
| background-color: var(--primary-color); |
| color: white; |
| } |
| |
| .actions { |
| display: flex; |
| justify-content: center; |
| gap: 1rem; |
| } |
| |
| .wheel-container { |
| position: relative; |
| width: 100%; |
| max-width: 600px; |
| aspect-ratio: 1; |
| margin: 2rem 0; |
| display: none; |
| } |
| |
| .wheel { |
| width: 100%; |
| height: 100%; |
| position: relative; |
| border-radius: 50%; |
| overflow: hidden; |
| box-shadow: 0 0 30px var(--shadow-color), 0 0 60px rgba(255, 214, 102, 0.6); |
| transition: box-shadow 0.3s; |
| } |
| |
| .wheel-inner { |
| width: 100%; |
| height: 100%; |
| border-radius: 50%; |
| position: relative; |
| transition: transform 5s cubic-bezier(0.1, 0.05, 0.1, 1.0); |
| } |
| |
| .segment { |
| position: absolute; |
| width: 50%; |
| height: 50%; |
| transform-origin: bottom right; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| overflow: hidden; |
| } |
| |
| .segment-content { |
| position: absolute; |
| left: 30px; |
| width: 80px; |
| text-align: center; |
| transform: rotate(90deg); |
| transform-origin: left; |
| font-weight: bold; |
| font-size: 0.9rem; |
| color: rgba(255, 255, 255, 0.9); |
| text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5); |
| overflow: hidden; |
| text-overflow: ellipsis; |
| white-space: nowrap; |
| } |
| |
| .wheel-pointer { |
| position: absolute; |
| top: -20px; |
| left: 50%; |
| transform: translateX(-50%); |
| width: 40px; |
| height: 60px; |
| background-color: var(--accent-color); |
| clip-path: polygon(50% 0%, 0% 100%, 100% 100%); |
| z-index: 10; |
| filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.2)); |
| } |
| |
| .wheel-center { |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| width: 15%; |
| height: 15%; |
| background: radial-gradient(circle, var(--light-color) 0%, var(--primary-color) 100%); |
| border-radius: 50%; |
| z-index: 5; |
| box-shadow: 0 0 15px var(--shadow-color); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| color: white; |
| font-weight: bold; |
| font-size: 1.2rem; |
| cursor: pointer; |
| } |
| |
| .wheel-center:hover { |
| box-shadow: 0 0 20px var(--accent-color); |
| } |
| |
| .wheel-center:after { |
| content: ""; |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| width: 70%; |
| height: 70%; |
| background-color: var(--accent-color); |
| border-radius: 50%; |
| z-index: -1; |
| } |
| |
| .result-display { |
| background-color: white; |
| padding: 2rem; |
| border-radius: 1rem; |
| box-shadow: 0 10px 30px var(--shadow-color); |
| text-align: center; |
| max-width: 500px; |
| width: 100%; |
| display: none; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .result-display h2 { |
| color: var(--primary-color); |
| margin-bottom: 1rem; |
| } |
| |
| .result-number { |
| font-size: 5rem; |
| font-weight: bold; |
| color: var(--accent-color); |
| margin: 1rem 0; |
| position: relative; |
| transition: all 0.3s; |
| } |
| |
| .result-text { |
| font-size: 1.2rem; |
| margin-bottom: 1.5rem; |
| } |
| |
| .shine { |
| position: absolute; |
| top: 0; |
| left: -100%; |
| width: 50%; |
| height: 100%; |
| background: linear-gradient( |
| 90deg, |
| rgba(255, 255, 255, 0) 0%, |
| rgba(255, 255, 255, 0.8) 50%, |
| rgba(255, 255, 255, 0) 100% |
| ); |
| z-index: 10; |
| animation: shine 2s infinite; |
| opacity: 0; |
| pointer-events: none; |
| } |
| |
| .confetti { |
| position: absolute; |
| width: 10px; |
| height: 10px; |
| background-color: var(--primary-color); |
| opacity: 0; |
| pointer-events: none; |
| } |
| |
| @keyframes shine { |
| 0% { |
| left: -100%; |
| opacity: 0; |
| } |
| 10% { |
| opacity: 1; |
| } |
| 50% { |
| left: 100%; |
| opacity: 1; |
| } |
| 51% { |
| opacity: 0; |
| } |
| 100% { |
| opacity: 0; |
| } |
| } |
| |
| .spinning .wheel { |
| box-shadow: 0 0 50px var(--accent-color), 0 0 100px rgba(255, 214, 102, 0.8); |
| } |
| |
| @media (max-width: 768px) { |
| h1 { |
| font-size: 2rem; |
| } |
| |
| .container { |
| gap: 1rem; |
| } |
| |
| .setup-panel, .result-display { |
| padding: 1.5rem; |
| } |
| |
| .result-number { |
| font-size: 3.5rem; |
| } |
| } |
| |
| .mode-description { |
| display: none; |
| margin-top: 0.5rem; |
| font-size: 0.85rem; |
| color: var(--accent-color); |
| font-style: italic; |
| } |
| |
| .special-effects { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| pointer-events: none; |
| z-index: 20; |
| } |
| |
| .flicker { |
| animation: flicker 0.2s infinite; |
| } |
| |
| @keyframes flicker { |
| 0% { opacity: 1; } |
| 50% { opacity: 0.7; } |
| 100% { opacity: 1; } |
| } |
| |
| .shake { |
| animation: shake 0.1s infinite; |
| } |
| |
| @keyframes shake { |
| 0% { transform: translateX(0); } |
| 25% { transform: translateX(-5px); } |
| 50% { transform: translateX(0); } |
| 75% { transform: translateX(5px); } |
| 100% { transform: translateX(0); } |
| } |
| |
| .jump { |
| animation: jump 0.5s; |
| } |
| |
| @keyframes jump { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.2); } |
| 100% { transform: scale(1); } |
| } |
| |
| .help-bubble { |
| position: fixed; |
| bottom: 20px; |
| right: 20px; |
| background-color: var(--primary-color); |
| color: white; |
| width: 50px; |
| height: 50px; |
| border-radius: 50%; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 1.5rem; |
| cursor: pointer; |
| box-shadow: 0 3px 10px var(--shadow-color); |
| transition: all 0.3s; |
| } |
| |
| .help-bubble:hover { |
| background-color: var(--accent-color); |
| transform: scale(1.1); |
| } |
| |
| .help-content { |
| position: fixed; |
| bottom: 80px; |
| right: 20px; |
| background-color: white; |
| padding: 1.5rem; |
| border-radius: 1rem; |
| width: 300px; |
| box-shadow: 0 10px 30px var(--shadow-color); |
| display: none; |
| z-index: 100; |
| } |
| |
| .help-content h3 { |
| color: var(--primary-color); |
| margin-bottom: 1rem; |
| } |
| |
| .help-content p { |
| margin-bottom: 0.8rem; |
| font-size: 0.9rem; |
| } |
| |
| .close-help { |
| position: absolute; |
| top: 10px; |
| right: 10px; |
| font-size: 1.2rem; |
| cursor: pointer; |
| color: var(--accent-color); |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <header> |
| <h1>阳光点名转盘</h1> |
| <p class="subtitle">让点名充满乐趣和惊喜!</p> |
| </header> |
| |
| <div class="setup-panel"> |
| <h2 class="panel-title">设置</h2> |
| <div class="form-group"> |
| <label for="class-size">班级人数:</label> |
| <input type="number" id="class-size" min="1" max="100" value="30"> |
| </div> |
| |
| <div class="form-group"> |
| <label for="start-number">起始编号 (默认从1开始):</label> |
| <input type="number" id="start-number" min="0" value="1"> |
| </div> |
| |
| <div class="form-group"> |
| <label for="picker-mode">选择模式:</label> |
| <select id="picker-mode"> |
| <option value="normal">正常模式</option> |
| <option value="fake-out">假动作模式</option> |
| <option value="oscillate">摇摆不定模式</option> |
| <option value="mystery">神秘模式</option> |
| <option value="double">双重选择模式</option> |
| </select> |
| <div id="mode-normal" class="mode-description">普通的转盘选择,公平公正。</div> |
| <div id="mode-fake-out" class="mode-description">即将停止时,转盘会突然改变方向!</div> |
| <div id="mode-oscillate" class="mode-description">在两个结果之间反复摇摆,到底会是谁呢?</div> |
| <div id="mode-mystery" class="mode-description">结果会短暂显示然后突然变化,充满惊喜!</div> |
| <div id="mode-double" class="mode-description">同时选出两个幸运儿!</div> |
| </div> |
| |
| <div class="actions"> |
| <button id="start-btn" class="btn">开始点名</button> |
| <button id="reset-btn" class="btn btn-secondary">重置</button> |
| </div> |
| </div> |
| |
| <div class="wheel-container"> |
| <div class="wheel-pointer"></div> |
| <div class="wheel"> |
| <div class="wheel-inner" id="wheel-inner"></div> |
| </div> |
| <div class="wheel-center" id="spin-btn">转!</div> |
| </div> |
| |
| <div class="result-display"> |
| <div class="shine"></div> |
| <h2>点名结果</h2> |
| <div class="result-number" id="result-number">?</div> |
| <p class="result-text" id="result-text">请点击转盘开始</p> |
| <button id="spin-again-btn" class="btn">再来一次</button> |
| </div> |
| </div> |
| |
| <div class="help-bubble">?</div> |
| <div class="help-content"> |
| <span class="close-help">×</span> |
| <h3>使用帮助</h3> |
| <p><strong>正常模式:</strong> 普通的点名选择,公平公正。</p> |
| <p><strong>假动作模式:</strong> 转盘减速后会有一次假动作,然后才会显示最终结果。</p> |
| <p><strong>摇摆不定模式:</strong> 在两个选项之间来回摇摆,增加悬念感。</p> |
| <p><strong>神秘模式:</strong> 先显示一个结果,然后突然改变!谁也猜不到最终会是谁。</p> |
| <p><strong>双重选择模式:</strong> 同时选出两名同学,适合小组活动。</p> |
| </div> |
| |
| <script> |
| |
| const setupPanel = document.querySelector('.setup-panel'); |
| const classSizeInput = document.getElementById('class-size'); |
| const startNumberInput = document.getElementById('start-number'); |
| const pickerModeSelect = document.getElementById('picker-mode'); |
| const startBtn = document.getElementById('start-btn'); |
| const resetBtn = document.getElementById('reset-btn'); |
| const wheelContainer = document.querySelector('.wheel-container'); |
| const wheelInner = document.getElementById('wheel-inner'); |
| const spinBtn = document.getElementById('spin-btn'); |
| const resultDisplay = document.querySelector('.result-display'); |
| const resultNumber = document.getElementById('result-number'); |
| const resultText = document.getElementById('result-text'); |
| const spinAgainBtn = document.getElementById('spin-again-btn'); |
| const helpBubble = document.querySelector('.help-bubble'); |
| const helpContent = document.querySelector('.help-content'); |
| const closeHelp = document.querySelector('.close-help'); |
| |
| |
| const modeDescriptions = { |
| normal: document.getElementById('mode-normal'), |
| 'fake-out': document.getElementById('mode-fake-out'), |
| oscillate: document.getElementById('mode-oscillate'), |
| mystery: document.getElementById('mode-mystery'), |
| double: document.getElementById('mode-double') |
| }; |
| |
| |
| let classSize = 30; |
| let startNumber = 1; |
| let mode = 'normal'; |
| let isSpinning = false; |
| let segments = []; |
| let selectedSegment = null; |
| let secondSelectedSegment = null; |
| |
| |
| const colors = [ |
| '#FF9E45', '#FFB347', '#FF8C42', '#FFC154', |
| '#FF6B6B', '#FF9671', '#FFA25B', '#FFD56B', |
| '#FFBB69', '#FFD166', '#FFC857', '#E9C46A', |
| '#F4A261', '#FF7F50', '#FF8966', '#FFBA7C' |
| ]; |
| |
| |
| function init() { |
| |
| startBtn.addEventListener('click', setupWheel); |
| resetBtn.addEventListener('click', reset); |
| spinBtn.addEventListener('click', spin); |
| spinAgainBtn.addEventListener('click', () => { |
| resultDisplay.style.display = 'none'; |
| wheelContainer.style.display = 'block'; |
| }); |
| |
| pickerModeSelect.addEventListener('change', updateModeDescription); |
| |
| helpBubble.addEventListener('click', () => { |
| helpContent.style.display = 'block'; |
| }); |
| |
| closeHelp.addEventListener('click', () => { |
| helpContent.style.display = 'none'; |
| }); |
| |
| |
| updateModeDescription(); |
| } |
| |
| function updateModeDescription() { |
| const selectedMode = pickerModeSelect.value; |
| |
| |
| Object.values(modeDescriptions).forEach(desc => { |
| desc.style.display = 'none'; |
| }); |
| |
| |
| const descElement = document.getElementById(`mode-${selectedMode}`); |
| if (descElement) { |
| descElement.style.display = 'block'; |
| } |
| } |
| |
| function setupWheel() { |
| classSize = parseInt(classSizeInput.value) || 30; |
| startNumber = parseInt(startNumberInput.value) || 1; |
| mode = pickerModeSelect.value; |
| |
| if (classSize < 1) { |
| alert('班级人数必须大于0'); |
| return; |
| } |
| |
| |
| createWheel(); |
| |
| |
| setupPanel.classList.add('minimized'); |
| wheelContainer.style.display = 'block'; |
| resultDisplay.style.display = 'none'; |
| |
| |
| setupPanel.addEventListener('click', function() { |
| if (this.classList.contains('minimized')) { |
| this.classList.remove('minimized'); |
| } |
| }); |
| } |
| |
| function createWheel() { |
| |
| wheelInner.innerHTML = ''; |
| segments = []; |
| |
| |
| const segmentAngle = 360 / classSize; |
| |
| for (let i = 0; i < classSize; i++) { |
| const segmentElement = document.createElement('div'); |
| segmentElement.className = 'segment'; |
| |
| |
| const rotation = i * segmentAngle; |
| segmentElement.style.transform = `rotate(${rotation}deg)`; |
| segmentElement.style.backgroundColor = colors[i % colors.length]; |
| |
| |
| const contentElement = document.createElement('div'); |
| contentElement.className = 'segment-content'; |
| const studentNumber = startNumber + i; |
| contentElement.textContent = studentNumber; |
| |
| segmentElement.appendChild(contentElement); |
| wheelInner.appendChild(segmentElement); |
| |
| |
| segments.push({ |
| element: segmentElement, |
| rotation, |
| value: studentNumber |
| }); |
| } |
| } |
| |
| function spin() { |
| if (isSpinning) return; |
| |
| isSpinning = true; |
| wheelContainer.classList.add('spinning'); |
| |
| |
| const minRotation = 1800; |
| const maxRotation = 3600; |
| |
| |
| const segmentAngle = 360 / classSize; |
| const randomOffset = Math.floor(Math.random() * classSize); |
| const finalSegmentIndex = randomOffset; |
| |
| |
| let totalRotation = minRotation + (maxRotation - minRotation) * Math.random(); |
| |
| |
| totalRotation += (finalSegmentIndex * segmentAngle); |
| |
| |
| wheelInner.style.transform = `rotate(${-totalRotation}deg)`; |
| |
| |
| selectedSegment = segments[finalSegmentIndex]; |
| |
| |
| if (mode === 'double') { |
| let secondIndex = (finalSegmentIndex + Math.floor(classSize / 2)) % classSize; |
| secondSelectedSegment = segments[secondIndex]; |
| } |
| |
| |
| setTimeout(() => { |
| processResult(); |
| }, 5000); |
| } |
| |
| function processResult() { |
| isSpinning = false; |
| wheelContainer.classList.remove('spinning'); |
| |
| let finalResult = selectedSegment.value; |
| let secondResult = secondSelectedSegment ? secondSelectedSegment.value : null; |
| |
| switch (mode) { |
| case 'fake-out': |
| playFakeOutEffect(finalResult); |
| break; |
| |
| case 'oscillate': |
| playOscillateEffect(finalResult); |
| break; |
| |
| case 'mystery': |
| playMysteryEffect(finalResult); |
| break; |
| |
| case 'double': |
| showDoubleResult(finalResult, secondResult); |
| break; |
| |
| default: |
| showResult(finalResult); |
| break; |
| } |
| } |
| |
| function showResult(result) { |
| wheelContainer.style.display = 'none'; |
| resultDisplay.style.display = 'block'; |
| |
| resultNumber.textContent = result; |
| resultText.textContent = `恭喜 ${result} 号同学被选中!`; |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| } |
| |
| function showDoubleResult(result1, result2) { |
| wheelContainer.style.display = 'none'; |
| resultDisplay.style.display = 'block'; |
| |
| resultNumber.textContent = `${result1} & ${result2}`; |
| resultText.textContent = `恭喜 ${result1} 号和 ${result2} 号同学被选中!`; |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| } |
| |
| function playFakeOutEffect(finalResult) { |
| |
| const fakeResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber; |
| |
| wheelContainer.style.display = 'none'; |
| resultDisplay.style.display = 'block'; |
| |
| resultNumber.textContent = fakeResult; |
| resultText.textContent = `恭喜 ${fakeResult} 号同学被选中!`; |
| |
| |
| setTimeout(() => { |
| resultNumber.classList.add('shake'); |
| resultText.textContent = "等等,发生了什么..."; |
| |
| |
| setTimeout(() => { |
| resultNumber.classList.remove('shake'); |
| resultNumber.classList.add('jump'); |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `真正被选中的是 ${finalResult} 号同学!`; |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.classList.remove('jump'); |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| }, 1000); |
| }, 1500); |
| } |
| |
| function playOscillateEffect(finalResult) { |
| |
| const altResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber; |
| |
| wheelContainer.style.display = 'none'; |
| resultDisplay.style.display = 'block'; |
| |
| let count = 0; |
| const maxOscillations = 6; |
| const oscillationInterval = setInterval(() => { |
| count++; |
| resultNumber.textContent = count % 2 === 0 ? finalResult : altResult; |
| resultText.textContent = "究竟会是谁呢..."; |
| |
| if (count >= maxOscillations) { |
| clearInterval(oscillationInterval); |
| |
| resultNumber.classList.add('jump'); |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`; |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.classList.remove('jump'); |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| } |
| }, 300); |
| } |
| |
| function playMysteryEffect(finalResult) { |
| |
| const mysterySteps = 3; |
| const stepDelay = 600; |
| |
| wheelContainer.style.display = 'none'; |
| resultDisplay.style.display = 'block'; |
| |
| let step = 0; |
| resultText.textContent = "神秘数字即将揭晓..."; |
| |
| |
| resultNumber.classList.add('flicker'); |
| |
| const mysteryInterval = setInterval(() => { |
| step++; |
| |
| if (step < mysterySteps) { |
| |
| const randomNumber = Math.floor(Math.random() * classSize) + startNumber; |
| resultNumber.textContent = randomNumber; |
| } else { |
| |
| clearInterval(mysteryInterval); |
| resultNumber.classList.remove('flicker'); |
| resultNumber.classList.add('jump'); |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`; |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.classList.remove('jump'); |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| } |
| }, stepDelay); |
| } |
| |
| function createConfetti() { |
| const specialEffects = document.createElement('div'); |
| specialEffects.className = 'special-effects'; |
| resultDisplay.appendChild(specialEffects); |
| |
| |
| for (let i = 0; i < 50; i++) { |
| const confetti = document.createElement('div'); |
| confetti.className = 'confetti'; |
| |
| |
| const left = Math.random() * 100 + '%'; |
| const top = -20 + 'px'; |
| |
| |
| const size = Math.random() * 8 + 5; |
| |
| |
| const color = colors[Math.floor(Math.random() * colors.length)]; |
| |
| |
| const shapes = ['circle', 'square', 'triangle']; |
| const shape = shapes[Math.floor(Math.random() * shapes.length)]; |
| |
| |
| confetti.style.left = left; |
| confetti.style.top = top; |
| confetti.style.width = size + 'px'; |
| confetti.style.height = size + 'px'; |
| confetti.style.backgroundColor = color; |
| |
| if (shape === 'circle') { |
| confetti.style.borderRadius = '50%'; |
| } else if (shape === 'triangle') { |
| confetti.style.clipPath = 'polygon(50% 0%, 0% 100%, 100% 100%)'; |
| } |
| |
| |
| specialEffects.appendChild(confetti); |
| |
| |
| const duration = Math.random() * 3 + 2; |
| const delay = Math.random() * 1.5; |
| |
| confetti.style.opacity = '1'; |
| confetti.style.animation = `fall ${duration}s ease-in ${delay}s forwards`; |
| |
| |
| if (!document.querySelector('#confetti-animation')) { |
| const styleSheet = document.createElement('style'); |
| styleSheet.id = 'confetti-animation'; |
| styleSheet.textContent = ` |
| @keyframes fall { |
| 0% { |
| transform: translateY(0) rotate(0deg); |
| opacity: 1; |
| } |
| 100% { |
| transform: translateY(${resultDisplay.clientHeight}px) rotate(360deg); |
| opacity: 0; |
| } |
| } |
| `; |
| document.head.appendChild(styleSheet); |
| } |
| } |
| |
| |
| setTimeout(() => { |
| if (specialEffects && specialEffects.parentNode) { |
| specialEffects.parentNode.removeChild(specialEffects); |
| } |
| }, 5000); |
| } |
| |
| function reset() { |
| |
| setupPanel.classList.remove('minimized'); |
| wheelContainer.style.display = 'none'; |
| resultDisplay.style.display = 'none'; |
| |
| |
| wheelInner.style.transform = 'rotate(0deg)'; |
| |
| |
| isSpinning = false; |
| selectedSegment = null; |
| secondSelectedSegment = null; |
| } |
| |
| |
| init(); |
| </script> |
| </body> |
| </html> |