name / index1.html
mistpe's picture
Rename index.html to index1.html
ffc3791 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: #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>
// DOM Elements
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');
// 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')
};
// Variables
let classSize = 30;
let startNumber = 1;
let mode = 'normal';
let isSpinning = false;
let segments = [];
let selectedSegment = null;
let secondSelectedSegment = null;
// Colors for the wheel segments (warm, sunlight palette)
const colors = [
'#FF9E45', '#FFB347', '#FF8C42', '#FFC154',
'#FF6B6B', '#FF9671', '#FFA25B', '#FFD56B',
'#FFBB69', '#FFD166', '#FFC857', '#E9C46A',
'#F4A261', '#FF7F50', '#FF8966', '#FFBA7C'
];
// Initialize
function init() {
// Setup event listeners
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';
});
// Initialize mode description
updateModeDescription();
}
function updateModeDescription() {
const selectedMode = pickerModeSelect.value;
// Hide all descriptions
Object.values(modeDescriptions).forEach(desc => {
desc.style.display = 'none';
});
// Show selected description
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;
}
// Create wheel segments
createWheel();
// Show wheel, hide setup
setupPanel.classList.add('minimized');
wheelContainer.style.display = 'block';
resultDisplay.style.display = 'none';
// Make setup panel expandable when minimized
setupPanel.addEventListener('click', function() {
if (this.classList.contains('minimized')) {
this.classList.remove('minimized');
}
});
}
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 spin() {
if (isSpinning) return;
isSpinning = true;
wheelContainer.classList.add('spinning');
// 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();
}, 5000); // Same as spin animation duration
}
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} 号同学被选中!`;
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animation after it completes
setTimeout(() => {
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti effect
createConfetti();
}
function showDoubleResult(result1, result2) {
wheelContainer.style.display = 'none';
resultDisplay.style.display = 'block';
resultNumber.textContent = `${result1} & ${result2}`;
resultText.textContent = `恭喜 ${result1} 号和 ${result2} 号同学被选中!`;
// Add shine effect
const shine = document.querySelector('.shine');
shine.style.opacity = '1';
shine.style.animation = 'shine 2s 1';
// Reset animation after it completes
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;
wheelContainer.style.display = 'none';
resultDisplay.style.display = 'block';
resultNumber.textContent = fakeResult;
resultText.textContent = `恭喜 ${fakeResult} 号同学被选中!`;
// After a short delay, apply shake effect and change to actual result
setTimeout(() => {
resultNumber.classList.add('shake');
resultText.textContent = "等等,发生了什么...";
// After shake effect, reveal true result
setTimeout(() => {
resultNumber.classList.remove('shake');
resultNumber.classList.add('jump');
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.classList.remove('jump');
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;
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);
// Final result with visual effect
resultNumber.classList.add('jump');
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.classList.remove('jump');
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;
wheelContainer.style.display = 'none';
resultDisplay.style.display = 'block';
let step = 0;
resultText.textContent = "神秘数字即将揭晓...";
// Add flicker effect to create suspense
resultNumber.classList.add('flicker');
const mysteryInterval = setInterval(() => {
step++;
if (step < mysterySteps) {
// Show random number
const randomNumber = Math.floor(Math.random() * classSize) + startNumber;
resultNumber.textContent = randomNumber;
} else {
// Last step - show real result
clearInterval(mysteryInterval);
resultNumber.classList.remove('flicker');
resultNumber.classList.add('jump');
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.classList.remove('jump');
shine.style.opacity = '0';
shine.style.animation = 'none';
}, 2000);
// Add confetti
createConfetti();
}
}, stepDelay);
}
function createConfetti() {
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;
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.opacity = '1';
confetti.style.animation = `fall ${duration}s ease-in ${delay}s forwards`;
// Add keyframe animation for falling confetti
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);
}
}
// Remove confetti after animation
setTimeout(() => {
if (specialEffects && specialEffects.parentNode) {
specialEffects.parentNode.removeChild(specialEffects);
}
}, 5000);
}
function reset() {
// Reset to setup panel
setupPanel.classList.remove('minimized');
wheelContainer.style.display = 'none';
resultDisplay.style.display = 'none';
// Reset wheel rotation
wheelInner.style.transform = 'rotate(0deg)';
// Reset variables
isSpinning = false;
selectedSegment = null;
secondSelectedSegment = null;
}
// Initialize the app
init();
</script>
</body>
</html>