| <!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: #4CAF50; |
| --secondary-color: #66BB6A; |
| --accent-color: #81C784; |
| --light-color: #E8F5E9; |
| --text-color: #2E7D32; |
| --shadow-color: rgba(76, 175, 80, 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%, #C8E6C9 100%); |
| color: var(--text-color); |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| padding: 1rem; |
| } |
| |
| .container { |
| max-width: 1200px; |
| width: 100%; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| gap: 1.5rem; |
| } |
| |
| header { |
| text-align: center; |
| margin-bottom: 0.5rem; |
| } |
| |
| 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; |
| } |
| |
| |
| .main-content { |
| display: flex; |
| width: 100%; |
| flex-direction: row; |
| gap: 2rem; |
| align-items: flex-start; |
| } |
| |
| .setup-panel { |
| background-color: white; |
| padding: 1.5rem; |
| border-radius: 1rem; |
| box-shadow: 0 10px 30px var(--shadow-color); |
| width: 100%; |
| max-width: 400px; |
| transition: all 0.3s ease; |
| flex-shrink: 0; |
| } |
| |
| .setup-panel.collapsed { |
| padding: 1rem; |
| max-width: 400px; |
| } |
| |
| .setup-panel.collapsed .form-content { |
| display: none; |
| } |
| |
| .setup-panel.collapsed .panel-title { |
| margin-bottom: 0; |
| } |
| |
| .panel-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 1.5rem; |
| cursor: pointer; |
| } |
| |
| .panel-title { |
| color: var(--primary-color); |
| margin: 0; |
| } |
| |
| .toggle-panel { |
| background: none; |
| border: none; |
| font-size: 1.5rem; |
| color: var(--accent-color); |
| cursor: pointer; |
| transition: transform 0.3s; |
| } |
| |
| .setup-panel.collapsed .toggle-panel { |
| transform: rotate(180deg); |
| } |
| |
| .form-row { |
| display: flex; |
| gap: 1rem; |
| margin-bottom: 1.5rem; |
| flex-wrap: wrap; |
| } |
| |
| .form-group { |
| flex: 1; |
| min-width: 150px; |
| } |
| |
| label { |
| display: block; |
| margin-bottom: 0.5rem; |
| font-weight: 500; |
| } |
| |
| input, select { |
| width: 100%; |
| padding: 0.8rem; |
| border: 2px solid #A5D6A7; |
| 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); |
| } |
| |
| .actions { |
| display: flex; |
| justify-content: center; |
| gap: 1rem; |
| flex-wrap: wrap; |
| } |
| |
| .wheel-container { |
| position: relative; |
| width: 100%; |
| max-width: 500px; |
| aspect-ratio: 1; |
| margin: 0 auto; |
| } |
| |
| .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: 1.5rem; |
| border-radius: 1rem; |
| box-shadow: 0 10px 30px var(--shadow-color); |
| text-align: center; |
| width: 100%; |
| height: 100%; |
| display: none; |
| position: absolute; |
| top: 0; |
| left: 0; |
| z-index: 10; |
| overflow: hidden; |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| } |
| |
| .result-display h2 { |
| color: var(--primary-color); |
| margin-bottom: 1rem; |
| } |
| |
| .result-number { |
| font-size: min(15vw, 8rem); |
| font-weight: bold; |
| color: var(--accent-color); |
| margin: 0.5rem 0; |
| position: relative; |
| transition: all 0.3s; |
| line-height: 1; |
| } |
| |
| .result-text { |
| font-size: max(1rem, min(3vw, 1.4rem)); |
| margin-bottom: 1.2rem; |
| } |
| |
| .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); |
| } |
| |
| .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; |
| } |
| |
| @keyframes flicker { |
| 0% { opacity: 1; } |
| 50% { opacity: 0.7; } |
| 100% { opacity: 1; } |
| } |
| |
| @keyframes shake { |
| 0% { transform: translateX(0); } |
| 25% { transform: translateX(-5px); } |
| 50% { transform: translateX(0); } |
| 75% { transform: translateX(5px); } |
| 100% { transform: translateX(0); } |
| } |
| |
| @keyframes jump { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.2); } |
| 100% { transform: scale(1); } |
| } |
| |
| @keyframes heartbeat { |
| 0% { transform: scale(1); } |
| 15% { transform: scale(1.15); } |
| 30% { transform: scale(1); } |
| 45% { transform: scale(1.15); } |
| 60% { transform: scale(1); } |
| 100% { transform: scale(1); } |
| } |
| |
| |
| @keyframes fall { |
| 0% { |
| transform: translateY(0) rotate(0deg); |
| opacity: 1; |
| } |
| 100% { |
| transform: translateY(400px) rotate(360deg); |
| opacity: 0; |
| } |
| } |
| |
| .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); |
| } |
| |
| |
| @media (max-width: 992px) { |
| .main-content { |
| flex-direction: column; |
| align-items: center; |
| } |
| |
| .setup-panel { |
| max-width: 100%; |
| order: 1; |
| } |
| |
| .wheel-container { |
| order: 0; |
| margin-bottom: 1.5rem; |
| } |
| } |
| |
| @media (max-width: 768px) { |
| h1 { |
| font-size: 2rem; |
| } |
| |
| .container { |
| gap: 1rem; |
| } |
| |
| .setup-panel, .result-display { |
| padding: 1.5rem; |
| } |
| |
| .result-number { |
| font-size: 3.5rem; |
| } |
| |
| .form-row { |
| flex-direction: column; |
| gap: 1rem; |
| } |
| |
| .form-group { |
| min-width: 100%; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <header> |
| <h1>命运抉择之轮</h1> |
| <p class="subtitle">掌控命运,抉择未来!</p> |
| </header> |
| |
| <div class="main-content"> |
| <div class="setup-panel"> |
| <div class="panel-header"> |
| <h2 class="panel-title">设置</h2> |
| <button class="toggle-panel">▲</button> |
| </div> |
| |
| <div class="form-content"> |
| <div class="form-row"> |
| <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">起始编号:</label> |
| <input type="number" id="start-number" min="0" value="1"> |
| </div> |
| </div> |
| |
| <div class="form-row"> |
| <div class="form-group"> |
| <label for="animation-speed">动画速度:</label> |
| <select id="animation-speed"> |
| <option value="slow">慢速 (更多悬念)</option> |
| <option value="normal" selected>正常</option> |
| <option value="fast">快速</option> |
| <option value="insane">超快 (刺激模式)</option> |
| </select> |
| </div> |
| </div> |
| |
| <div class="form-row"> |
| <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> |
| <option value="countdown">倒计时模式</option> |
| <option value="heartbeat">心跳紧张模式</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 id="mode-countdown" class="mode-description">紧张的倒计时,增加悬念感!</div> |
| <div id="mode-heartbeat" class="mode-description">伴随心跳,感受命运的紧张抉择!</div> |
| </div> |
| </div> |
| |
| <div class="actions"> |
| <button id="reset-btn" class="btn">保存修改/重新开始</button> |
| </div> |
| </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 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> |
| </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> |
| <p><strong>倒计时模式:</strong> 伴随紧张的倒计时动画,增加刺激感。</p> |
| <p><strong>心跳紧张模式:</strong> 伴随心跳动画,让结果更加扣人心弦。</p> |
| <p><strong>提示:</strong> 尝试不同的动画速度,获得最佳体验!</p> |
| </div> |
| |
| <script> |
| |
| const setupPanel = document.querySelector('.setup-panel'); |
| const togglePanelBtn = document.querySelector('.toggle-panel'); |
| const classSizeInput = document.getElementById('class-size'); |
| const startNumberInput = document.getElementById('start-number'); |
| const pickerModeSelect = document.getElementById('picker-mode'); |
| const animationSpeedSelect = document.getElementById('animation-speed'); |
| 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'), |
| countdown: document.getElementById('mode-countdown'), |
| heartbeat: document.getElementById('mode-heartbeat') |
| }; |
| |
| |
| const colors = [ |
| '#4CAF50', '#66BB6A', '#81C784', '#A5D6A7', |
| '#C8E6C9', '#7CB342', '#8BC34A', '#9CCC65', |
| '#AED581', '#C5E1A5', '#DCEDC8', '#43A047', |
| '#388E3C', '#2E7D32', '#689F38', '#558B2F' |
| ]; |
| |
| |
| let classSize = 30; |
| let startNumber = 1; |
| let mode = 'normal'; |
| let animationSpeed = 'normal'; |
| let isSpinning = false; |
| let segments = []; |
| let selectedSegment = null; |
| let secondSelectedSegment = null; |
| |
| function updateModeDescription() { |
| const selectedMode = pickerModeSelect.value; |
| |
| |
| Object.values(modeDescriptions).forEach(desc => { |
| if (desc) desc.style.display = 'none'; |
| }); |
| |
| |
| const descElement = document.getElementById(`mode-${selectedMode}`); |
| if (descElement) { |
| descElement.style.display = 'block'; |
| } |
| } |
| |
| 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 setupWheel() { |
| classSize = parseInt(classSizeInput.value) || 30; |
| startNumber = parseInt(startNumberInput.value) || 1; |
| mode = pickerModeSelect.value; |
| animationSpeed = animationSpeedSelect.value; |
| |
| if (classSize < 1) { |
| alert('班级人数必须大于0'); |
| return; |
| } |
| |
| |
| createWheel(); |
| |
| |
| resultDisplay.style.display = 'none'; |
| } |
| |
| function spin() { |
| if (isSpinning) return; |
| |
| isSpinning = true; |
| wheelContainer.classList.add('spinning'); |
| |
| |
| let spinDuration = 5000; |
| |
| switch (animationSpeed) { |
| case 'slow': |
| spinDuration = 8000; |
| break; |
| case 'fast': |
| spinDuration = 3000; |
| break; |
| case 'insane': |
| spinDuration = 1500; |
| break; |
| } |
| |
| |
| wheelInner.style.transition = `transform ${spinDuration/1000}s cubic-bezier(0.1, 0.05, 0.1, 1.0)`; |
| |
| |
| 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(); |
| }, spinDuration); |
| } |
| |
| 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; |
| |
| case 'countdown': |
| playCountdownEffect(finalResult); |
| break; |
| |
| case 'heartbeat': |
| playHeartbeatEffect(finalResult); |
| break; |
| |
| default: |
| showResult(finalResult); |
| break; |
| } |
| } |
| |
| function showResult(result) { |
| resultDisplay.style.display = 'flex'; |
| |
| resultNumber.textContent = result; |
| resultText.textContent = `恭喜 ${result} 号同学被选中!`; |
| |
| |
| if (result.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| |
| |
| 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) { |
| resultDisplay.style.display = 'flex'; |
| |
| resultNumber.textContent = `${result1} & ${result2}`; |
| resultText.textContent = `恭喜 ${result1} 号和 ${result2} 号同学被选中!`; |
| |
| |
| if (result1.toString().length + result2.toString().length > 4) { |
| resultNumber.style.fontSize = `min(10vw, 5rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } |
| |
| |
| 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; |
| |
| resultDisplay.style.display = 'flex'; |
| |
| resultNumber.textContent = fakeResult; |
| resultText.textContent = `恭喜 ${fakeResult} 号同学被选中!`; |
| |
| |
| if (fakeResult.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'shake 0.5s'; |
| resultText.textContent = "等等,发生了什么..."; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'none'; |
| resultNumber.offsetHeight; |
| resultNumber.style.animation = 'jump 0.5s'; |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `真正被选中的是 ${finalResult} 号同学!`; |
| |
| |
| if (finalResult.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'none'; |
| 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; |
| |
| resultDisplay.style.display = 'flex'; |
| |
| |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| |
| let count = 0; |
| const maxOscillations = 6; |
| const oscillationInterval = setInterval(() => { |
| count++; |
| const currentResult = count % 2 === 0 ? finalResult : altResult; |
| resultNumber.textContent = currentResult; |
| resultText.textContent = "究竟会是谁呢..."; |
| |
| |
| if (currentResult.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| |
| if (count >= maxOscillations) { |
| clearInterval(oscillationInterval); |
| |
| resultNumber.style.animation = 'jump 0.5s'; |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`; |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'none'; |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| } |
| }, 300); |
| } |
| function playMysteryEffect(finalResult) { |
| |
| const mysterySteps = 3; |
| const stepDelay = 600; |
| |
| resultDisplay.style.display = 'flex'; |
| |
| let step = 0; |
| resultText.textContent = "神秘数字即将揭晓..."; |
| |
| |
| resultNumber.style.animation = 'flicker 0.5s infinite'; |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| |
| const mysteryInterval = setInterval(() => { |
| step++; |
| |
| if (step < mysterySteps) { |
| |
| const randomNumber = Math.floor(Math.random() * classSize) + startNumber; |
| resultNumber.textContent = randomNumber; |
| |
| |
| if (randomNumber.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| } else { |
| |
| clearInterval(mysteryInterval); |
| resultNumber.style.animation = 'none'; |
| resultNumber.offsetHeight; |
| resultNumber.style.animation = 'jump 0.5s'; |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`; |
| |
| |
| if (finalResult.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'none'; |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| } |
| }, stepDelay); |
| } |
| function playCountdownEffect(finalResult) { |
| resultDisplay.style.display = 'flex'; |
| |
| resultNumber.textContent = "3"; |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| resultText.textContent = "倒计时开始..."; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'jump 0.5s'; |
| resultNumber.textContent = "2"; |
| setTimeout(() => { resultNumber.style.animation = 'none'; }, 500); |
| }, 1000); |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'jump 0.5s'; |
| resultNumber.textContent = "1"; |
| setTimeout(() => { resultNumber.style.animation = 'none'; }, 500); |
| }, 2000); |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'jump 0.5s'; |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`; |
| |
| |
| if (finalResult.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'none'; |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| }, 3000); |
| } |
| function playHeartbeatEffect(finalResult) { |
| resultDisplay.style.display = 'flex'; |
| |
| resultNumber.textContent = "?"; |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| resultText.textContent = "命运正在抉择..."; |
| |
| |
| resultNumber.style.animation = 'heartbeat 1s infinite'; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'none'; |
| resultNumber.offsetHeight; |
| resultNumber.style.animation = 'jump 0.5s'; |
| resultNumber.textContent = finalResult; |
| resultText.textContent = `命运选择了 ${finalResult} 号同学!`; |
| |
| |
| if (finalResult.toString().length > 2) { |
| resultNumber.style.fontSize = `min(12vw, 6rem)`; |
| } else { |
| resultNumber.style.fontSize = `min(15vw, 8rem)`; |
| } |
| |
| |
| const shine = document.querySelector('.shine'); |
| shine.style.opacity = '1'; |
| shine.style.animation = 'shine 2s 1'; |
| |
| |
| setTimeout(() => { |
| resultNumber.style.animation = 'none'; |
| shine.style.opacity = '0'; |
| shine.style.animation = 'none'; |
| }, 2000); |
| |
| |
| createConfetti(); |
| }, 4000); |
| } |
| function createConfetti() { |
| |
| const existingEffects = document.querySelector('.special-effects'); |
| if (existingEffects) { |
| existingEffects.remove(); |
| } |
| |
| |
| 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; |
| confetti.style.opacity = '1'; |
| confetti.style.position = 'absolute'; |
| |
| |
| 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.animation = `fall ${duration}s ease-in ${delay}s forwards`; |
| } |
| |
| |
| setTimeout(() => { |
| if (specialEffects && specialEffects.parentNode) { |
| specialEffects.parentNode.removeChild(specialEffects); |
| } |
| }, 5000); |
| } |
| |
| function reset() { |
| |
| setupPanel.classList.remove('collapsed'); |
| resultDisplay.style.display = 'none'; |
| |
| |
| wheelInner.style.transform = 'rotate(0deg)'; |
| |
| |
| isSpinning = false; |
| selectedSegment = null; |
| secondSelectedSegment = null; |
| |
| |
| setupWheel(); |
| } |
| |
| |
| function init() { |
| |
| setupWheel(); |
| |
| |
| togglePanelBtn.addEventListener('click', () => { |
| setupPanel.classList.toggle('collapsed'); |
| togglePanelBtn.textContent = setupPanel.classList.contains('collapsed') ? '▼' : '▲'; |
| }); |
| |
| resetBtn.addEventListener('click', reset); |
| spinBtn.addEventListener('click', spin); |
| spinAgainBtn.addEventListener('click', () => { |
| resultDisplay.style.display = 'none'; |
| }); |
| |
| pickerModeSelect.addEventListener('change', updateModeDescription); |
| |
| helpBubble.addEventListener('click', () => { |
| helpContent.style.display = 'block'; |
| }); |
| |
| closeHelp.addEventListener('click', () => { |
| helpContent.style.display = 'none'; |
| }); |
| |
| |
| updateModeDescription(); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', init); |
| </script> |
| </body> |
| </html> |