name / index.html
mistpe's picture
Update index.html
50c1b92 verified
<!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;
}
/* Layout for desktop */
.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布局 */
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; /* 设置行高为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); }
}
/* Fall animation for confetti */
@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 queries for responsive design */
@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>
// DOM Elements
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');
// Mode descriptions
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')
};
// Colors for the wheel segments (soft green palette)
const colors = [
'#4CAF50', '#66BB6A', '#81C784', '#A5D6A7',
'#C8E6C9', '#7CB342', '#8BC34A', '#9CCC65',
'#AED581', '#C5E1A5', '#DCEDC8', '#43A047',
'#388E3C', '#2E7D32', '#689F38', '#558B2F'
];
// Variables
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;
// Hide all descriptions
Object.values(modeDescriptions).forEach(desc => {
if (desc) desc.style.display = 'none';
});
// Show selected description
const descElement = document.getElementById(`mode-${selectedMode}`);
if (descElement) {
descElement.style.display = 'block';
}
}
function createWheel() {
// Clear previous wheel
wheelInner.innerHTML = '';
segments = [];
// Create segments
const segmentAngle = 360 / classSize;
for (let i = 0; i < classSize; i++) {
const segmentElement = document.createElement('div');
segmentElement.className = 'segment';
// Rotate and position segment
const rotation = i * segmentAngle;
segmentElement.style.transform = `rotate(${rotation}deg)`;
segmentElement.style.backgroundColor = colors[i % colors.length];
// Create content with student number
const contentElement = document.createElement('div');
contentElement.className = 'segment-content';
const studentNumber = startNumber + i;
contentElement.textContent = studentNumber;
segmentElement.appendChild(contentElement);
wheelInner.appendChild(segmentElement);
// Store segment data
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;
}
// Create wheel segments
createWheel();
// Show wheel, hide result display if it's shown
resultDisplay.style.display = 'none';
}
function spin() {
if (isSpinning) return;
isSpinning = true;
wheelContainer.classList.add('spinning');
// Get rotation speed based on animation speed setting
let spinDuration = 5000; // Default duration (normal speed)
switch (animationSpeed) {
case 'slow':
spinDuration = 8000;
break;
case 'fast':
spinDuration = 3000;
break;
case 'insane':
spinDuration = 1500;
break;
}
// Update wheel-inner transition duration
wheelInner.style.transition = `transform ${spinDuration/1000}s cubic-bezier(0.1, 0.05, 0.1, 1.0)`;
// Calculate random rotation (5-10 full rotations + random offset)
const minRotation = 1800; // 5 full rotations (5 * 360)
const maxRotation = 3600; // 10 full rotations (10 * 360)
// Random rotation
const segmentAngle = 360 / classSize;
const randomOffset = Math.floor(Math.random() * classSize);
const finalSegmentIndex = randomOffset;
// Calculate total rotation
let totalRotation = minRotation + (maxRotation - minRotation) * Math.random();
// Add offset to land on the selected segment
totalRotation += (finalSegmentIndex * segmentAngle);
// Apply rotation to wheel
wheelInner.style.transform = `rotate(${-totalRotation}deg)`;
// Get the selected segment
selectedSegment = segments[finalSegmentIndex];
// For double mode, select a second segment
if (mode === 'double') {
let secondIndex = (finalSegmentIndex + Math.floor(classSize / 2)) % classSize;
secondSelectedSegment = segments[secondIndex];
}
// Process result after spinning animation
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'; // 使用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'; // Use flex layout
resultNumber.textContent = `${result1} & ${result2}`;
resultText.textContent = `恭喜 ${result1} 号和 ${result2} 号同学被选中!`;
// Adjust font size based on content length
if (result1.toString().length + result2.toString().length > 4) {
resultNumber.style.fontSize = `min(10vw, 5rem)`;
} else {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
}
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animations
setTimeout(() => {
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti effect
createConfetti();
}
function playFakeOutEffect(finalResult) {
// First show a fake result
const fakeResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber;
resultDisplay.style.display = 'flex';
resultNumber.textContent = fakeResult;
resultText.textContent = `恭喜 ${fakeResult} 号同学被选中!`;
// Adjust font size based on number length
if (fakeResult.toString().length > 2) {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
} else {
resultNumber.style.fontSize = `min(15vw, 8rem)`;
}
// After a short delay, apply shake effect and change to actual result
setTimeout(() => {
resultNumber.style.animation = 'shake 0.5s';
resultText.textContent = "等等,发生了什么...";
// After shake effect, reveal true result
setTimeout(() => {
resultNumber.style.animation = 'none';
resultNumber.offsetHeight; // Trigger reflow to restart animation
resultNumber.style.animation = 'jump 0.5s';
resultNumber.textContent = finalResult;
resultText.textContent = `真正被选中的是 ${finalResult} 号同学!`;
// Adjust font size for final result
if (finalResult.toString().length > 2) {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
} else {
resultNumber.style.fontSize = `min(15vw, 8rem)`;
}
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animations
setTimeout(() => {
resultNumber.style.animation = 'none';
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti for the true result
createConfetti();
}, 1000);
}, 1500);
}
function playOscillateEffect(finalResult) {
// Create alternative result
const altResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber;
resultDisplay.style.display = 'flex';
// Initial font size setting
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 = "究竟会是谁呢...";
// Adjust font size based on displayed number
if (currentResult.toString().length > 2) {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
} else {
resultNumber.style.fontSize = `min(15vw, 8rem)`;
}
if (count >= maxOscillations) {
clearInterval(oscillationInterval);
// Final result with visual effect
resultNumber.style.animation = 'jump 0.5s';
resultNumber.textContent = finalResult;
resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animations
setTimeout(() => {
resultNumber.style.animation = 'none';
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti
createConfetti();
}
}, 300);
}
function playMysteryEffect(finalResult) {
// Generate a series of random results ending with the actual one
const mysterySteps = 3;
const stepDelay = 600;
resultDisplay.style.display = 'flex';
let step = 0;
resultText.textContent = "神秘数字即将揭晓...";
// Add flicker effect to create suspense
resultNumber.style.animation = 'flicker 0.5s infinite';
resultNumber.style.fontSize = `min(15vw, 8rem)`;
const mysteryInterval = setInterval(() => {
step++;
if (step < mysterySteps) {
// Show random number
const randomNumber = Math.floor(Math.random() * classSize) + startNumber;
resultNumber.textContent = randomNumber;
// Adjust font size based on random number
if (randomNumber.toString().length > 2) {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
} else {
resultNumber.style.fontSize = `min(15vw, 8rem)`;
}
} else {
// Last step - show real result
clearInterval(mysteryInterval);
resultNumber.style.animation = 'none';
resultNumber.offsetHeight; // Trigger reflow to restart animation
resultNumber.style.animation = 'jump 0.5s';
resultNumber.textContent = finalResult;
resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
// Adjust font size for final result
if (finalResult.toString().length > 2) {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
} else {
resultNumber.style.fontSize = `min(15vw, 8rem)`;
}
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animations
setTimeout(() => {
resultNumber.style.animation = 'none';
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti
createConfetti();
}
}, stepDelay);
}
function playCountdownEffect(finalResult) {
resultDisplay.style.display = 'flex';
resultNumber.textContent = "3";
resultNumber.style.fontSize = `min(15vw, 8rem)`;
resultText.textContent = "倒计时开始...";
// Countdown animation
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} 号同学被选中!`;
// Adjust font size for final result
if (finalResult.toString().length > 2) {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
} else {
resultNumber.style.fontSize = `min(15vw, 8rem)`;
}
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animations
setTimeout(() => {
resultNumber.style.animation = 'none';
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti
createConfetti();
}, 3000);
}
function playHeartbeatEffect(finalResult) {
resultDisplay.style.display = 'flex';
resultNumber.textContent = "?";
resultNumber.style.fontSize = `min(15vw, 8rem)`;
resultText.textContent = "命运正在抉择...";
// Add heartbeat animation to result number
resultNumber.style.animation = 'heartbeat 1s infinite';
// After a few heartbeats, reveal the result
setTimeout(() => {
resultNumber.style.animation = 'none';
resultNumber.offsetHeight; // Trigger reflow to restart animation
resultNumber.style.animation = 'jump 0.5s';
resultNumber.textContent = finalResult;
resultText.textContent = `命运选择了 ${finalResult} 号同学!`;
// Adjust font size for final result
if (finalResult.toString().length > 2) {
resultNumber.style.fontSize = `min(12vw, 6rem)`;
} else {
resultNumber.style.fontSize = `min(15vw, 8rem)`;
}
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animations
setTimeout(() => {
resultNumber.style.animation = 'none';
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti
createConfetti();
}, 4000);
}
function createConfetti() {
// Clear any existing confetti
const existingEffects = document.querySelector('.special-effects');
if (existingEffects) {
existingEffects.remove();
}
// Create container for confetti
const specialEffects = document.createElement('div');
specialEffects.className = 'special-effects';
resultDisplay.appendChild(specialEffects);
// Create confetti pieces
for (let i = 0; i < 50; i++) {
const confetti = document.createElement('div');
confetti.className = 'confetti';
// Random position within container
const left = Math.random() * 100 + '%';
const top = -20 + 'px';
// Random size
const size = Math.random() * 8 + 5;
// Random color
const color = colors[Math.floor(Math.random() * colors.length)];
// Random shape
const shapes = ['circle', 'square', 'triangle'];
const shape = shapes[Math.floor(Math.random() * shapes.length)];
// Apply styles
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';
// Set shape
if (shape === 'circle') {
confetti.style.borderRadius = '50%';
} else if (shape === 'triangle') {
confetti.style.clipPath = 'polygon(50% 0%, 0% 100%, 100% 100%)';
}
// Add to container
specialEffects.appendChild(confetti);
// Animate falling
const duration = Math.random() * 3 + 2;
const delay = Math.random() * 1.5;
confetti.style.animation = `fall ${duration}s ease-in ${delay}s forwards`;
}
// Remove confetti after animation
setTimeout(() => {
if (specialEffects && specialEffects.parentNode) {
specialEffects.parentNode.removeChild(specialEffects);
}
}, 5000);
}
function reset() {
// Reset to setup panel
setupPanel.classList.remove('collapsed');
resultDisplay.style.display = 'none';
// Reset wheel rotation
wheelInner.style.transform = 'rotate(0deg)';
// Reset variables
isSpinning = false;
selectedSegment = null;
secondSelectedSegment = null;
// Recreate wheel with current settings
setupWheel();
}
// Initialize the app
function init() {
// Create the wheel for the first time
setupWheel();
// Setup event listeners
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';
});
// Initialize mode description
updateModeDescription();
}
// Initialize the app when the document is loaded
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>