mistpe commited on
Commit
fe21207
·
verified ·
1 Parent(s): ffc3791

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +1436 -0
index.html ADDED
@@ -0,0 +1,1436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>命运抉择之轮</title>
7
+ <style>
8
+ :root {
9
+ --primary-color: #4CAF50;
10
+ --secondary-color: #66BB6A;
11
+ --accent-color: #81C784;
12
+ --light-color: #E8F5E9;
13
+ --text-color: #2E7D32;
14
+ --shadow-color: rgba(76, 175, 80, 0.3);
15
+ }
16
+
17
+ * {
18
+ margin: 0;
19
+ padding: 0;
20
+ box-sizing: border-box;
21
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
22
+ }
23
+
24
+ body {
25
+ background: linear-gradient(135deg, var(--light-color) 0%, #C8E6C9 100%);
26
+ color: var(--text-color);
27
+ min-height: 100vh;
28
+ display: flex;
29
+ flex-direction: column;
30
+ align-items: center;
31
+ padding: 2rem;
32
+ }
33
+
34
+ .container {
35
+ max-width: 1200px;
36
+ width: 100%;
37
+ display: flex;
38
+ flex-direction: column;
39
+ align-items: center;
40
+ gap: 2rem;
41
+ }
42
+
43
+ header {
44
+ text-align: center;
45
+ margin-bottom: 1rem;
46
+ }
47
+
48
+ h1 {
49
+ font-size: 2.5rem;
50
+ color: var(--primary-color);
51
+ text-shadow: 2px 2px 4px var(--shadow-color);
52
+ margin-bottom: 0.5rem;
53
+ }
54
+
55
+ .subtitle {
56
+ font-size: 1.2rem;
57
+ color: var(--accent-color);
58
+ font-weight: 300;
59
+ }
60
+
61
+ .setup-panel {
62
+ background-color: white;
63
+ padding: 2rem;
64
+ border-radius: 1rem;
65
+ box-shadow: 0 10px 30px var(--shadow-color);
66
+ width: 100%;
67
+ max-width: 500px;
68
+ transition: all 0.3s ease;
69
+ }
70
+
71
+ .setup-panel.minimized {
72
+ padding: 1rem;
73
+ max-width: 300px;
74
+ cursor: pointer;
75
+ }
76
+
77
+ .setup-panel.minimized .form-group {
78
+ display: none;
79
+ }
80
+
81
+ .setup-panel.minimized .panel-title:after {
82
+ content: " (点击展开)";
83
+ font-size: 0.8rem;
84
+ color: var(--accent-color);
85
+ }
86
+
87
+ .panel-title {
88
+ text-align: center;
89
+ margin-bottom: 1.5rem;
90
+ color: var(--primary-color);
91
+ }
92
+
93
+ .form-group {
94
+ margin-bottom: 1.5rem;
95
+ }
96
+
97
+ label {
98
+ display: block;
99
+ margin-bottom: 0.5rem;
100
+ font-weight: 500;
101
+ }
102
+
103
+ input, select {
104
+ width: 100%;
105
+ padding: 0.8rem;
106
+ border: 2px solid #A5D6A7;
107
+ border-radius: 0.5rem;
108
+ font-size: 1rem;
109
+ background-color: var(--light-color);
110
+ transition: all 0.3s;
111
+ }
112
+
113
+ input:focus, select:focus {
114
+ outline: none;
115
+ border-color: var(--primary-color);
116
+ box-shadow: 0 0 0 3px var(--shadow-color);
117
+ }
118
+
119
+ .btn {
120
+ background-color: var(--primary-color);
121
+ color: white;
122
+ border: none;
123
+ padding: 0.8rem 1.5rem;
124
+ border-radius: 0.5rem;
125
+ font-size: 1rem;
126
+ font-weight: 600;
127
+ cursor: pointer;
128
+ transition: all 0.3s;
129
+ box-shadow: 0 4px 10px var(--shadow-color);
130
+ }
131
+
132
+ .btn:hover {
133
+ background-color: var(--accent-color);
134
+ transform: translateY(-2px);
135
+ box-shadow: 0 6px 15px var(--shadow-color);
136
+ }
137
+
138
+ .btn-secondary {
139
+ background-color: var(--light-color);
140
+ color: var(--primary-color);
141
+ border: 2px solid var(--primary-color);
142
+ }
143
+
144
+ .btn-secondary:hover {
145
+ background-color: var(--primary-color);
146
+ color: white;
147
+ }
148
+
149
+ .actions {
150
+ display: flex;
151
+ justify-content: center;
152
+ gap: 1rem;
153
+ }
154
+
155
+ .wheel-container {
156
+ position: relative;
157
+ width: 100%;
158
+ max-width: 600px;
159
+ aspect-ratio: 1;
160
+ margin: 2rem 0;
161
+ display: none;
162
+ }
163
+
164
+ .wheel {
165
+ width: 100%;
166
+ height: 100%;
167
+ position: relative;
168
+ border-radius: 50%;
169
+ overflow: hidden;
170
+ box-shadow: 0 0 30px var(--shadow-color), 0 0 60px rgba(255, 214, 102, 0.6);
171
+ transition: box-shadow 0.3s;
172
+ }
173
+
174
+ .wheel-inner {
175
+ width: 100%;
176
+ height: 100%;
177
+ border-radius: 50%;
178
+ position: relative;
179
+ transition: transform 5s cubic-bezier(0.1, 0.05, 0.1, 1.0);
180
+ }
181
+
182
+ .segment {
183
+ position: absolute;
184
+ width: 50%;
185
+ height: 50%;
186
+ transform-origin: bottom right;
187
+ display: flex;
188
+ align-items: center;
189
+ justify-content: center;
190
+ overflow: hidden;
191
+ }
192
+
193
+ .segment-content {
194
+ position: absolute;
195
+ left: 30px;
196
+ width: 80px;
197
+ text-align: center;
198
+ transform: rotate(90deg);
199
+ transform-origin: left;
200
+ font-weight: bold;
201
+ font-size: 0.9rem;
202
+ color: rgba(255, 255, 255, 0.9);
203
+ text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5);
204
+ overflow: hidden;
205
+ text-overflow: ellipsis;
206
+ white-space: nowrap;
207
+ }
208
+
209
+ .wheel-pointer {
210
+ position: absolute;
211
+ top: -20px;
212
+ left: 50%;
213
+ transform: translateX(-50%);
214
+ width: 40px;
215
+ height: 60px;
216
+ background-color: var(--accent-color);
217
+ clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
218
+ z-index: 10;
219
+ filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.2));
220
+ }
221
+
222
+ .wheel-center {
223
+ position: absolute;
224
+ top: 50%;
225
+ left: 50%;
226
+ transform: translate(-50%, -50%);
227
+ width: 15%;
228
+ height: 15%;
229
+ background: radial-gradient(circle, var(--light-color) 0%, var(--primary-color) 100%);
230
+ border-radius: 50%;
231
+ z-index: 5;
232
+ box-shadow: 0 0 15px var(--shadow-color);
233
+ display: flex;
234
+ align-items: center;
235
+ justify-content: center;
236
+ color: white;
237
+ font-weight: bold;
238
+ font-size: 1.2rem;
239
+ cursor: pointer;
240
+ }
241
+
242
+ .wheel-center:hover {
243
+ box-shadow: 0 0 20px var(--accent-color);
244
+ }
245
+
246
+ .wheel-center:after {
247
+ content: "";
248
+ position: absolute;
249
+ top: 50%;
250
+ left: 50%;
251
+ transform: translate(-50%, -50%);
252
+ width: 70%;
253
+ height: 70%;
254
+ background-color: var(--accent-color);
255
+ border-radius: 50%;
256
+ z-index: -1;
257
+ }
258
+
259
+ .result-display {
260
+ background-color: white;
261
+ padding: 2rem;
262
+ border-radius: 1rem;
263
+ box-shadow: 0 10px 30px var(--shadow-color);
264
+ text-align: center;
265
+ max-width: 500px;
266
+ width: 100%;
267
+ display: none;
268
+ position: relative;
269
+ overflow: hidden;
270
+ }
271
+
272
+ .result-display h2 {
273
+ color: var(--primary-color);
274
+ margin-bottom: 1rem;
275
+ }
276
+
277
+ .result-number {
278
+ font-size: 5rem;
279
+ font-weight: bold;
280
+ color: var(--accent-color);
281
+ margin: 1rem 0;
282
+ position: relative;
283
+ transition: all 0.3s;
284
+ }
285
+
286
+ .result-text {
287
+ font-size: 1.2rem;
288
+ margin-bottom: 1.5rem;
289
+ }
290
+
291
+ .shine {
292
+ position: absolute;
293
+ top: 0;
294
+ left: -100%;
295
+ width: 50%;
296
+ height: 100%;
297
+ background: linear-gradient(
298
+ 90deg,
299
+ rgba(255, 255, 255, 0) 0%,
300
+ rgba(255, 255, 255, 0.8) 50%,
301
+ rgba(255, 255, 255, 0) 100%
302
+ );
303
+ z-index: 10;
304
+ animation: shine 2s infinite;
305
+ opacity: 0;
306
+ pointer-events: none;
307
+ }
308
+
309
+ .confetti {
310
+ position: absolute;
311
+ width: 10px;
312
+ height: 10px;
313
+ background-color: var(--primary-color);
314
+ opacity: 0;
315
+ pointer-events: none;
316
+ }
317
+
318
+ @keyframes shine {
319
+ 0% {
320
+ left: -100%;
321
+ opacity: 0;
322
+ }
323
+ 10% {
324
+ opacity: 1;
325
+ }
326
+ 50% {
327
+ left: 100%;
328
+ opacity: 1;
329
+ }
330
+ 51% {
331
+ opacity: 0;
332
+ }
333
+ 100% {
334
+ opacity: 0;
335
+ }
336
+ }
337
+
338
+ .spinning .wheel {
339
+ box-shadow: 0 0 50px var(--accent-color), 0 0 100px rgba(255, 214, 102, 0.8);
340
+ }
341
+
342
+ @media (max-width: 768px) {
343
+ h1 {
344
+ font-size: 2rem;
345
+ }
346
+
347
+ .container {
348
+ gap: 1rem;
349
+ }
350
+
351
+ .setup-panel, .result-display {
352
+ padding: 1.5rem;
353
+ }
354
+
355
+ .result-number {
356
+ font-size: 3.5rem;
357
+ }
358
+ }
359
+
360
+ .mode-description {
361
+ display: none;
362
+ margin-top: 0.5rem;
363
+ font-size: 0.85rem;
364
+ color: var(--accent-color);
365
+ font-style: italic;
366
+ }
367
+
368
+ .special-effects {
369
+ position: absolute;
370
+ top: 0;
371
+ left: 0;
372
+ width: 100%;
373
+ height: 100%;
374
+ pointer-events: none;
375
+ z-index: 20;
376
+ }
377
+
378
+ @keyframes flicker {
379
+ 0% { opacity: 1; }
380
+ 50% { opacity: 0.7; }
381
+ 100% { opacity: 1; }
382
+ }
383
+
384
+ @keyframes shake {
385
+ 0% { transform: translateX(0); }
386
+ 25% { transform: translateX(-5px); }
387
+ 50% { transform: translateX(0); }
388
+ 75% { transform: translateX(5px); }
389
+ 100% { transform: translateX(0); }
390
+ }
391
+
392
+ @keyframes jump {
393
+ 0% { transform: scale(1); }
394
+ 50% { transform: scale(1.2); }
395
+ 100% { transform: scale(1); }
396
+ }
397
+
398
+ @keyframes spotlight {
399
+ 0% { box-shadow: 0 0 10px var(--shadow-color); }
400
+ 50% { box-shadow: 0 0 60px var(--accent-color), 0 0 120px rgba(129, 199, 132, 0.8); }
401
+ 100% { box-shadow: 0 0 10px var(--shadow-color); }
402
+ }
403
+
404
+ @keyframes heartbeat {
405
+ 0% { transform: scale(1); }
406
+ 15% { transform: scale(1.15); }
407
+ 30% { transform: scale(1); }
408
+ 45% { transform: scale(1.15); }
409
+ 60% { transform: scale(1); }
410
+ 100% { transform: scale(1); }
411
+ }
412
+
413
+ /* Spotlight effect */
414
+ .spotlight-container {
415
+ position: absolute;
416
+ top: 0;
417
+ left: 0;
418
+ width: 100%;
419
+ height: 100%;
420
+ overflow: hidden;
421
+ pointer-events: none;
422
+ z-index: 5;
423
+ background: radial-gradient(
424
+ circle at center,
425
+ rgba(255, 255, 255, 0) 30%,
426
+ rgba(0, 0, 0, 0.7) 100%
427
+ );
428
+ opacity: 0;
429
+ transition: opacity 0.5s;
430
+ }
431
+
432
+ .help-bubble {
433
+ position: fixed;
434
+ bottom: 20px;
435
+ right: 20px;
436
+ background-color: var(--primary-color);
437
+ color: white;
438
+ width: 50px;
439
+ height: 50px;
440
+ border-radius: 50%;
441
+ display: flex;
442
+ align-items: center;
443
+ justify-content: center;
444
+ font-size: 1.5rem;
445
+ cursor: pointer;
446
+ box-shadow: 0 3px 10px var(--shadow-color);
447
+ transition: all 0.3s;
448
+ }
449
+
450
+ .help-bubble:hover {
451
+ background-color: var(--accent-color);
452
+ transform: scale(1.1);
453
+ }
454
+
455
+ .help-content {
456
+ position: fixed;
457
+ bottom: 80px;
458
+ right: 20px;
459
+ background-color: white;
460
+ padding: 1.5rem;
461
+ border-radius: 1rem;
462
+ width: 300px;
463
+ box-shadow: 0 10px 30px var(--shadow-color);
464
+ display: none;
465
+ z-index: 100;
466
+ }
467
+
468
+ .help-content h3 {
469
+ color: var(--primary-color);
470
+ margin-bottom: 1rem;
471
+ }
472
+
473
+ .help-content p {
474
+ margin-bottom: 0.8rem;
475
+ font-size: 0.9rem;
476
+ }
477
+
478
+ .close-help {
479
+ position: absolute;
480
+ top: 10px;
481
+ right: 10px;
482
+ font-size: 1.2rem;
483
+ cursor: pointer;
484
+ color: var(--accent-color);
485
+ }
486
+ </style>
487
+ </head>
488
+ <body>
489
+ <div class="container">
490
+ <header>
491
+ <h1>命运抉择之轮</h1>
492
+ <p class="subtitle">掌控命运,抉择未来!</p>
493
+ </header>
494
+
495
+ <div class="setup-panel">
496
+ <h2 class="panel-title">设置</h2>
497
+ <div class="form-group">
498
+ <label for="class-size">班级人数:</label>
499
+ <input type="number" id="class-size" min="1" max="100" value="30">
500
+ </div>
501
+
502
+ <div class="form-group">
503
+ <label for="start-number">起始编号 (默认从1开始):</label>
504
+ <input type="number" id="start-number" min="0" value="1">
505
+ </div>
506
+
507
+ <div class="form-group">
508
+ <label for="animation-speed">动画速度:</label>
509
+ <select id="animation-speed">
510
+ <option value="slow">慢速 (更多悬念)</option>
511
+ <option value="normal" selected>正常</option>
512
+ <option value="fast">快速</option>
513
+ <option value="insane">超快 (刺激模式)</option>
514
+ </select>
515
+ </div>
516
+
517
+ <div class="form-group">
518
+ <label for="sound-effects">音效:</label>
519
+ <select id="sound-effects">
520
+ <option value="off">关闭</option>
521
+ <option value="basic" selected>基础音效</option>
522
+ <option value="dramatic">戏剧性音效</option>
523
+ <option value="suspense">悬疑音效</option>
524
+ </select>
525
+ </div>
526
+
527
+ <div class="form-group">
528
+ <label for="picker-mode">选择模式:</label>
529
+ <select id="picker-mode">
530
+ <option value="normal">正常模式</option>
531
+ <option value="fake-out">假动作模式</option>
532
+ <option value="oscillate">摇摆不定模式</option>
533
+ <option value="mystery">神秘模式</option>
534
+ <option value="double">双重选择模式</option>
535
+ <option value="countdown">倒计时模式</option>
536
+ <option value="elimination">淘汰赛模式</option>
537
+ <option value="spotlight">聚光灯模式</option>
538
+ <option value="heartbeat">心跳紧张模式</option>
539
+ </select>
540
+ <div id="mode-normal" class="mode-description">普通的转盘选择,公平公正。</div>
541
+ <div id="mode-fake-out" class="mode-description">即将停止时,转盘会突然改变方向!</div>
542
+ <div id="mode-oscillate" class="mode-description">在两个结果之间反复摇摆,到底会是谁呢?</div>
543
+ <div id="mode-mystery" class="mode-description">结果会短暂显示然后突然变化,充满惊喜!</div>
544
+ <div id="mode-double" class="mode-description">同时选出两个幸运儿!</div>
545
+ <div id="mode-countdown" class="mode-description">紧张的倒计时音效,增加悬念感!</div>
546
+ <div id="mode-elimination" class="mode-description">一轮轮淘汰,最后只留下一个幸运儿!</div>
547
+ <div id="mode-spotlight" class="mode-description">灯光聚焦效果,犹如舞台中央的明星!</div>
548
+ <div id="mode-heartbeat" class="mode-description">伴随心跳声,感受命运的紧张抉择!</div>
549
+ </div>
550
+
551
+ <div class="actions">
552
+ <button id="start-btn" class="btn">开始点名</button>
553
+ <button id="reset-btn" class="btn btn-secondary">重置</button>
554
+ </div>
555
+ </div>
556
+
557
+ <div class="wheel-container">
558
+ <div class="wheel-pointer"></div>
559
+ <div class="wheel">
560
+ <div class="wheel-inner" id="wheel-inner"></div>
561
+ </div>
562
+ <div class="wheel-center" id="spin-btn">转!</div>
563
+ </div>
564
+
565
+ <div class="result-display">
566
+ <div class="shine"></div>
567
+ <h2>点名结果</h2>
568
+ <div class="result-number" id="result-number">?</div>
569
+ <p class="result-text" id="result-text">请点击转盘开始</p>
570
+ <button id="spin-again-btn" class="btn">再来一次</button>
571
+ </div>
572
+ </div>
573
+
574
+ <div class="help-bubble">?</div>
575
+ <div class="help-content">
576
+ <span class="close-help">×</span>
577
+ <h3>使用帮助</h3>
578
+ <p><strong>正常模式:</strong> 普通的点名选择,公平公正。</p>
579
+ <p><strong>假动作模式:</strong> 转盘减速后会有一次假动作,然后才会显示最终结果。</p>
580
+ <p><strong>摇摆不定模式:</strong> 在两个选项之间来回摇摆,增加悬念感。</p>
581
+ <p><strong>神秘模式:</strong> 先显示一个结果,然后突然改变!谁也猜不到最终会是谁。</p>
582
+ <p><strong>双重选择模式:</strong> 同时选出两名同学,适合小组活动。</p>
583
+ <p><strong>倒计时模式:</strong> 伴随紧张的倒计时音效和动画,增加刺激感。</p>
584
+ <p><strong>淘汰赛模式:</strong> 连续多轮抽取,逐步淘汰,最终决出命运之选。</p>
585
+ <p><strong>聚光灯模式:</strong> 模拟真实大型抽奖活动,灯光聚焦效果。</p>
586
+ <p><strong>心跳紧张模式:</strong> 伴随心跳声音效,让结果更加扣人心弦。</p>
587
+ <p><strong>提示:</strong> 尝试不同的音效和动画速度组合,获得最佳体验!</p>
588
+ </div>
589
+
590
+ <script>
591
+ // DOM Elements
592
+ const setupPanel = document.querySelector('.setup-panel');
593
+ const classSizeInput = document.getElementById('class-size');
594
+ const startNumberInput = document.getElementById('start-number');
595
+ const pickerModeSelect = document.getElementById('picker-mode');
596
+ const animationSpeedSelect = document.getElementById('animation-speed');
597
+ const soundEffectsSelect = document.getElementById('sound-effects');
598
+ const startBtn = document.getElementById('start-btn');
599
+ const resetBtn = document.getElementById('reset-btn');
600
+ const wheelContainer = document.querySelector('.wheel-container');
601
+ const wheelInner = document.getElementById('wheel-inner');
602
+ const spinBtn = document.getElementById('spin-btn');
603
+ const resultDisplay = document.querySelector('.result-display');
604
+ const resultNumber = document.getElementById('result-number');
605
+ const resultText = document.getElementById('result-text');
606
+ const spinAgainBtn = document.getElementById('spin-again-btn');
607
+ const helpBubble = document.querySelector('.help-bubble');
608
+ const helpContent = document.querySelector('.help-content');
609
+ const closeHelp = document.querySelector('.close-help');
610
+
611
+ // Mode descriptions
612
+ const modeDescriptions = {
613
+ normal: document.getElementById('mode-normal'),
614
+ 'fake-out': document.getElementById('mode-fake-out'),
615
+ oscillate: document.getElementById('mode-oscillate'),
616
+ mystery: document.getElementById('mode-mystery'),
617
+ double: document.getElementById('mode-double'),
618
+ countdown: document.getElementById('mode-countdown'),
619
+ elimination: document.getElementById('mode-elimination'),
620
+ spotlight: document.getElementById('mode-spotlight'),
621
+ heartbeat: document.getElementById('mode-heartbeat')
622
+ };
623
+
624
+ // Variables
625
+ let classSize = 30;
626
+ let startNumber = 1;
627
+ let mode = 'normal';
628
+ let animationSpeed = 'normal';
629
+ let soundEffects = 'basic';
630
+ let isSpinning = false;
631
+ let segments = [];
632
+ let selectedSegment = null;
633
+ let secondSelectedSegment = null;
634
+ let eliminationCandidates = [];
635
+ let eliminationRound = 0;
636
+
637
+ // Sound effects
638
+ const sounds = {
639
+ spin: new Audio('data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAkJCQkJCQkJCQkJCQkJCQkJCQwMDAwMDAwMDAwMDAwMDAwMDA4ODg4ODg4ODg4ODg4ODg4ODg4P////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjEzAAAAAAAAAAAAAAAAJAXAAAAAAAAAAbC3hL2QAAAAAAD/+9DEAAAJtPl39BEAJgpN7j84wAGMKUpWlP19W1v1f3a1apSktaUkxpJgYGAYGDEYO5eHyXh9wfP+H4fwfD6AZ8Pw+gGfnygY+MDDDDDDDDDDDDDDDD4fg+gGfh8/8Pn/+H0Az5+UMfP/+oBooyZVLj6iYqhplXOKiomz1kVdVUksYnF3KszMrUxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'),
640
+ result: new Audio('data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAXnAyMjIyMjIyMjIyMjIyMjIyMjI5OTk5OTk5OTk5OTk5OTk5OTk5P39/f39/f39/f39/f39/f39/f///////////////////////////////////////////////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjEzAAAAAAAAAAAAAAAAJAaUAAAAAAAAeblBaHYAAAAAAP/7sMQAAAfA53P5hgAKGUruPzjAASEQQQNhjHHdPnPz797ve0vbdM7vPPPGBBBQoUhISEhIAAAIQhAVOP4WOeeOJwIBANDuK+Xn8fH06Ic7etFbiMVhYAgIBgYGBwfB8Hw+H0A0NDQ0NDQ0Af/////////+qqqqoAAAAAVVVVQAAAxiMQ5nJTnOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5znOc5zmIxEZ//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////MTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV'),
641
+ countdown: new Audio('data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAABAAAAeAAMDAwMDAwMDAwMDAwVVVVVVVVVVVVVVVVVaqqqqqqqqqqqqqqqqqq1dXV1dXV1dXV1dXV1dXw8PDw8PDw8PDw8PDw8P////////////////////////////////8AAAAATGF2YzU4LjEzAAAAAAAAAAAAAAAAJAZgAAAAAAAAei6yB8AAAAAAA//7kMQAAAs0x2/4YYABRCOqOzDACpdGMYxtBpqqtSv1KUyRRZVlzKdBkCgIBAIJiQUGijgQDBSMFwAGAAYAGPR++D4PnAEHwfJAUeBAQEBEREQIQhCNDHw+fB8HwfJAR8Pn5IIEBAQEBAQEBAT5ICAj4fTHw+f///+SCAgI+DgICPjggICbICAgICAn////5IICBMkBAT/+SCAgICAgJ////kggICAgQEyQEBAQECYICAgI+Hz////5IICAn////+SCAgIDRMNDQ0NDQ0TDQ0NDQ0NDa0mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpMQU1FMy4xMDBV'),
642
+ heartbeat: new Audio('data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAkJCQkJCQkJCQkJCQkJCQkJCQwMDAwMDAwMDAwMDAwMDAwMDA4ODg4ODg4ODg4ODg4ODg4ODg4P////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjEzAAAAAAAAAAAAAAAAJAXAAAAAAAAAAbC3hL2QAAAAAAD/+xDEAAAHRId9+GGAARIrKn8wwAGqqqpN9aqqkqqpN9VNIiNu7Zmg2JMICJVAwEAgELhcGAYBgMGQwZfL+XyQEBAQEeD5/y/kgICPB8/5ICAgI8Hc/kgICAj4fP8kBAQEwQEB4PggICPB3/P+SAgICPh8/8kBAQEBASD4PggICPB3P5ICAgI+D4ICAj4fD5IIPggICPB8/8kBAQEBAQEfB9/kgICAkH/JBB8Hz/kgg+CAkH/P/JBB8EHwQf/JAQEggICPg+/yQQEBMEBAR8Hz/kggICYIPgg+CD4IP+SCAgI+D5/yQEBAT/B8Hc/6oAAAAAAAAA'),
643
+ dramatic: new Audio('data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAzMzMzMzMzMzMzMzMzMzMzMzM/Pz8/Pz8/Pz8/Pz8/Pz8/Pz8/P///////////////////////////////////////////////////////////////////////////////////8AAAAATGF2YzU4LjEzAAAAAAAAAAAAAAAAJAZgAAAAAAAAAbD6L30gAAAAAAD/+7DEAAAKYOFy+cmAASikabj8qAA2lVVmtOmdzHHtK7mONOmcyczMqgoWFJwcLCwpIhYWFk4OFixQsLD/wULCwJFFCwsOaSIWFA4WFhQFFFFjGMWFhQWFhQWFhQUDGMb/BQWHNkUUUUdJowULDssUUWMWKKOkgcLDuyKKOklFqEVwsO72MWKKOkldJI/iqPaRX+yP/oRVH/ysKDu9lYf/ZVHaFUe0iv9JIO72VR7SKo8dJI6Qij2hlQdhFUeOkkdIRVHtDKg7SK//2MWKPYGKPaGVR/9lYUFh/aFC/8Ozs7O/8rOzv/DKg7uw7OzsOzo7Ozs6Ozs7Ozr///zs7Ozs6Os6Ozs7Olh2dDKo7Ozs6Ozs6OsOzs6Ozs7Ozs6Ozs6OsOzo7Ojr///w7Ojs7Ow6Ojr/w7Ozs7OsOw7Ozs6Ow7Ozs6Ow7Ozs6Ozs7DsOzs7OjsOzs7Ojr//8Ozs7Ols7OztbOzs6Ozs7O1s7OztbOzs6Ozs7O1s7OztbOzs6O1s7OztbOzs7WztbOztbOzs7O1s7OztbOzs7O1s7OzpbOzs7WztbOzs7Wzs7O1s7OztbOzs7O1s7OztbOzs7O1s7OzpbOzs7WztbOztbOzs7O1s7OzpbOzs7WztbOzs7Wzs7O1s7OztbOzs7O1s7O1s7OztbOzs7O1s7OztbOzs7WzqzWztbOztbOzs6WzpbOzs6szs6szs6szs6szs6szs6szs6s1s6szs6szs6szs6szs6szs6s1s6szs6szs6szs6szs6szs6s1s6szs6szs6szs6szs6szs6s1s6szs6szs6szs6szs6szs6s1s6szs6szs6szs6szs6szs6s1s7OzpbOzs7WztbOzs6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsqs1s6Wzsq')
644
+ };
645
+
646
+ // Play a sound from the sound library
647
+ function playSound(soundName) {
648
+ // Only play if sound effects are enabled
649
+ if (soundEffects === 'off') return;
650
+
651
+ // Stop any currently playing sounds
652
+ sounds.spin.pause();
653
+ sounds.spin.currentTime = 0;
654
+ sounds.result.pause();
655
+ sounds.result.currentTime = 0;
656
+ sounds.countdown.pause();
657
+ sounds.countdown.currentTime = 0;
658
+ sounds.heartbeat.pause();
659
+ sounds.heartbeat.currentTime = 0;
660
+ sounds.dramatic.pause();
661
+ sounds.dramatic.currentTime = 0;
662
+
663
+ // Play the requested sound
664
+ if (sounds[soundName]) {
665
+ sounds[soundName].play().catch(e => {
666
+ console.log("Sound could not be played: " + e);
667
+ });
668
+ }
669
+ }
670
+
671
+ function updateModeDescription() {
672
+ const selectedMode = pickerModeSelect.value;
673
+
674
+ // Hide all descriptions
675
+ Object.values(modeDescriptions).forEach(desc => {
676
+ desc.style.display = 'none';
677
+ });
678
+
679
+ // Show selected description
680
+ const descElement = document.getElementById(`mode-${selectedMode}`);
681
+ if (descElement) {
682
+ descElement.style.display = 'block';
683
+ }
684
+ }
685
+
686
+ function setupWheel() {
687
+ classSize = parseInt(classSizeInput.value) || 30;
688
+ startNumber = parseInt(startNumberInput.value) || 1;
689
+ mode = pickerModeSelect.value;
690
+ animationSpeed = animationSpeedSelect.value;
691
+ soundEffects = soundEffectsSelect.value;
692
+
693
+ if (classSize < 1) {
694
+ alert('班级人数必须大于0');
695
+ return;
696
+ }
697
+
698
+ // For elimination mode, prepare the candidates
699
+ if (mode === 'elimination') {
700
+ eliminationCandidates = [];
701
+ for (let i = 0; i < classSize; i++) {
702
+ eliminationCandidates.push(startNumber + i);
703
+ }
704
+ eliminationRound = 0;
705
+ }
706
+
707
+ // Create wheel segments
708
+ createWheel();
709
+
710
+ // Show wheel, hide setup
711
+ setupPanel.classList.add('minimized');
712
+ wheelContainer.style.display = 'block';
713
+ resultDisplay.style.display = 'none';
714
+
715
+ // Make setup panel expandable when minimized
716
+ setupPanel.addEventListener('click', function() {
717
+ if (this.classList.contains('minimized')) {
718
+ this.classList.remove('minimized');
719
+ }
720
+ });
721
+ }
722
+
723
+ function createWheel() {
724
+ // Clear previous wheel
725
+ wheelInner.innerHTML = '';
726
+ segments = [];
727
+
728
+ // Colors for the wheel segments (soft green palette)
729
+ const colors = [
730
+ '#4CAF50', '#66BB6A', '#81C784', '#A5D6A7',
731
+ '#C8E6C9', '#7CB342', '#8BC34A', '#9CCC65',
732
+ '#AED581', '#C5E1A5', '#DCEDC8', '#43A047',
733
+ '#388E3C', '#2E7D32', '#689F38', '#558B2F'
734
+ ];
735
+
736
+ // Create segments
737
+ const segmentAngle = 360 / classSize;
738
+
739
+ for (let i = 0; i < classSize; i++) {
740
+ const segmentElement = document.createElement('div');
741
+ segmentElement.className = 'segment';
742
+
743
+ // Rotate and position segment
744
+ const rotation = i * segmentAngle;
745
+ segmentElement.style.transform = `rotate(${rotation}deg)`;
746
+ segmentElement.style.backgroundColor = colors[i % colors.length];
747
+
748
+ // Create content with student number
749
+ const contentElement = document.createElement('div');
750
+ contentElement.className = 'segment-content';
751
+ const studentNumber = startNumber + i;
752
+ contentElement.textContent = studentNumber;
753
+
754
+ segmentElement.appendChild(contentElement);
755
+ wheelInner.appendChild(segmentElement);
756
+
757
+ // Store segment data
758
+ segments.push({
759
+ element: segmentElement,
760
+ rotation,
761
+ value: studentNumber
762
+ });
763
+ }
764
+ }
765
+
766
+ function spin() {
767
+ if (isSpinning) return;
768
+
769
+ isSpinning = true;
770
+ wheelContainer.classList.add('spinning');
771
+
772
+ // Get rotation speed based on animation speed setting
773
+ let spinDuration = 5000; // Default duration (normal speed)
774
+
775
+ switch (animationSpeed) {
776
+ case 'slow':
777
+ spinDuration = 8000;
778
+ break;
779
+ case 'fast':
780
+ spinDuration = 3000;
781
+ break;
782
+ case 'insane':
783
+ spinDuration = 1500;
784
+ break;
785
+ }
786
+
787
+ // Update wheel-inner transition duration
788
+ wheelInner.style.transition = `transform ${spinDuration/1000}s cubic-bezier(0.1, 0.05, 0.1, 1.0)`;
789
+
790
+ // Calculate random rotation (5-10 full rotations + random offset)
791
+ const minRotation = 1800; // 5 full rotations (5 * 360)
792
+ const maxRotation = 3600; // 10 full rotations (10 * 360)
793
+
794
+ // Random rotation
795
+ const segmentAngle = 360 / classSize;
796
+ const randomOffset = Math.floor(Math.random() * classSize);
797
+ const finalSegmentIndex = randomOffset;
798
+
799
+ // Calculate total rotation
800
+ let totalRotation = minRotation + (maxRotation - minRotation) * Math.random();
801
+
802
+ // Add offset to land on the selected segment
803
+ totalRotation += (finalSegmentIndex * segmentAngle);
804
+
805
+ // Apply rotation to wheel
806
+ wheelInner.style.transform = `rotate(${-totalRotation}deg)`;
807
+
808
+ // Get the selected segment
809
+ selectedSegment = segments[finalSegmentIndex];
810
+
811
+ // For double mode, select a second segment
812
+ if (mode === 'double') {
813
+ let secondIndex = (finalSegmentIndex + Math.floor(classSize / 2)) % classSize;
814
+ secondSelectedSegment = segments[secondIndex];
815
+ }
816
+
817
+ // Play sound effects if enabled
818
+ if (soundEffects !== 'off') {
819
+ playSound('spin');
820
+
821
+ if (mode === 'countdown') {
822
+ // Start countdown sound after a delay
823
+ setTimeout(() => {
824
+ playSound('countdown');
825
+ }, spinDuration - 3000); // Start 3 seconds before end
826
+ }
827
+
828
+ if (mode === 'heartbeat') {
829
+ // Start heartbeat sound after a delay
830
+ setTimeout(() => {
831
+ playSound('heartbeat');
832
+ }, spinDuration - 3000); // Start 3 seconds before end
833
+ }
834
+ }
835
+
836
+ // Process result after spinning animation
837
+ setTimeout(() => {
838
+ processResult();
839
+ }, spinDuration);
840
+ }
841
+
842
+ function processResult() {
843
+ isSpinning = false;
844
+ wheelContainer.classList.remove('spinning');
845
+
846
+ let finalResult = selectedSegment.value;
847
+ let secondResult = secondSelectedSegment ? secondSelectedSegment.value : null;
848
+
849
+ // Play result sound if enabled
850
+ if (soundEffects !== 'off') {
851
+ // Different sounds for different modes
852
+ if (mode === 'dramatic' && soundEffects === 'dramatic') {
853
+ playSound('dramatic');
854
+ } else {
855
+ playSound('result');
856
+ }
857
+ }
858
+
859
+ switch (mode) {
860
+ case 'fake-out':
861
+ playFakeOutEffect(finalResult);
862
+ break;
863
+
864
+ case 'oscillate':
865
+ playOscillateEffect(finalResult);
866
+ break;
867
+
868
+ case 'mystery':
869
+ playMysteryEffect(finalResult);
870
+ break;
871
+
872
+ case 'double':
873
+ showDoubleResult(finalResult, secondResult);
874
+ break;
875
+
876
+ case 'countdown':
877
+ playCountdownEffect(finalResult);
878
+ break;
879
+
880
+ case 'elimination':
881
+ playEliminationEffect(finalResult);
882
+ break;
883
+
884
+ case 'spotlight':
885
+ playSpotlightEffect(finalResult);
886
+ break;
887
+
888
+ case 'heartbeat':
889
+ playHeartbeatEffect(finalResult);
890
+ break;
891
+
892
+ default:
893
+ showResult(finalResult);
894
+ break;
895
+ }
896
+ }
897
+
898
+ function showResult(result) {
899
+ wheelContainer.style.display = 'none';
900
+ resultDisplay.style.display = 'block';
901
+
902
+ resultNumber.textContent = result;
903
+ resultText.textContent = `恭喜 ${result} 号同学被选中!`;
904
+
905
+ // Add shine effect
906
+ const shine = document.querySelector('.shine');
907
+ shine.style.opacity = '1';
908
+ shine.style.animation = 'shine 2s 1';
909
+
910
+ // Reset animation after it completes
911
+ setTimeout(() => {
912
+ shine.style.opacity = '0';
913
+ shine.style.animation = 'none';
914
+ }, 2000);
915
+
916
+ // Add confetti effect
917
+ createConfetti();
918
+ }
919
+
920
+ function showDoubleResult(result1, result2) {
921
+ wheelContainer.style.display = 'none';
922
+ resultDisplay.style.display = 'block';
923
+
924
+ resultNumber.textContent = `${result1} & ${result2}`;
925
+ resultText.textContent = `恭喜 ${result1} 号和 ${result2} 号同学被选中!`;
926
+
927
+ // Add shine effect
928
+ const shine = document.querySelector('.shine');
929
+ shine.style.opacity = '1';
930
+ shine.style.animation = 'shine 2s 1';
931
+
932
+ // Reset animation after it completes
933
+ setTimeout(() => {
934
+ shine.style.opacity = '0';
935
+ shine.style.animation = 'none';
936
+ }, 2000);
937
+
938
+ // Add confetti effect
939
+ createConfetti();
940
+ }
941
+
942
+ function playFakeOutEffect(finalResult) {
943
+ // First show a fake result
944
+ const fakeResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber;
945
+
946
+ wheelContainer.style.display = 'none';
947
+ resultDisplay.style.display = 'block';
948
+
949
+ resultNumber.textContent = fakeResult;
950
+ resultText.textContent = `恭喜 ${fakeResult} 号同学被选中!`;
951
+
952
+ // After a short delay, apply shake effect and change to actual result
953
+ setTimeout(() => {
954
+ resultNumber.classList.add('shake');
955
+ resultText.textContent = "等等,发生了什么...";
956
+
957
+ // After shake effect, reveal true result
958
+ setTimeout(() => {
959
+ resultNumber.classList.remove('shake');
960
+ resultNumber.classList.add('jump');
961
+ resultNumber.textContent = finalResult;
962
+ resultText.textContent = `真正被选中的是 ${finalResult} 号同学!`;
963
+
964
+ // Add shine effect
965
+ const shine = document.querySelector('.shine');
966
+ shine.style.opacity = '1';
967
+ shine.style.animation = 'shine 2s 1';
968
+
969
+ // Reset animations
970
+ setTimeout(() => {
971
+ resultNumber.classList.remove('jump');
972
+ shine.style.opacity = '0';
973
+ shine.style.animation = 'none';
974
+ }, 2000);
975
+
976
+ // Add confetti for the true result
977
+ createConfetti();
978
+ }, 1000);
979
+ }, 1500);
980
+ }
981
+
982
+ function playOscillateEffect(finalResult) {
983
+ // Create alternative result
984
+ const altResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber;
985
+
986
+ wheelContainer.style.display = 'none';
987
+ resultDisplay.style.display = 'block';
988
+
989
+ let count = 0;
990
+ const maxOscillations = 6;
991
+ const oscillationInterval = setInterval(() => {
992
+ count++;
993
+ resultNumber.textContent = count % 2 === 0 ? finalResult : altResult;
994
+ resultText.textContent = "究竟会是谁呢...";
995
+
996
+ if (count >= maxOscillations) {
997
+ clearInterval(oscillationInterval);
998
+ // Final result with visual effect
999
+ resultNumber.classList.add('jump');
1000
+ resultNumber.textContent = finalResult;
1001
+ resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
1002
+
1003
+ // Add shine effect
1004
+ const shine = document.querySelector('.shine');
1005
+ shine.style.opacity = '1';
1006
+ shine.style.animation = 'shine 2s 1';
1007
+
1008
+ // Reset animations
1009
+ setTimeout(() => {
1010
+ resultNumber.classList.remove('jump');
1011
+ shine.style.opacity = '0';
1012
+ shine.style.animation = 'none';
1013
+ }, 2000);
1014
+
1015
+ // Add confetti
1016
+ createConfetti();
1017
+ }
1018
+ }, 300);
1019
+ }
1020
+
1021
+ function playMysteryEffect(finalResult) {
1022
+ // Generate a series of random results ending with the actual one
1023
+ const mysterySteps = 3;
1024
+ const stepDelay = 600;
1025
+
1026
+ wheelContainer.style.display = 'none';
1027
+ resultDisplay.style.display = 'block';
1028
+
1029
+ let step = 0;
1030
+ resultText.textContent = "神秘数字即将揭晓...";
1031
+
1032
+ // Add flicker effect to create suspense
1033
+ resultNumber.classList.add('flicker');
1034
+
1035
+ const mysteryInterval = setInterval(() => {
1036
+ step++;
1037
+
1038
+ if (step < mysterySteps) {
1039
+ // Show random number
1040
+ const randomNumber = Math.floor(Math.random() * classSize) + startNumber;
1041
+ resultNumber.textContent = randomNumber;
1042
+ } else {
1043
+ // Last step - show real result
1044
+ clearInterval(mysteryInterval);
1045
+ resultNumber.classList.remove('flicker');
1046
+ resultNumber.classList.add('jump');
1047
+ resultNumber.textContent = finalResult;
1048
+ resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
1049
+
1050
+ // Add shine effect
1051
+ const shine = document.querySelector('.shine');
1052
+ shine.style.opacity = '1';
1053
+ shine.style.animation = 'shine 2s 1';
1054
+
1055
+ // Reset animations
1056
+ setTimeout(() => {
1057
+ resultNumber.classList.remove('jump');
1058
+ shine.style.opacity = '0';
1059
+ shine.style.animation = 'none';
1060
+ }, 2000);
1061
+
1062
+ // Add confetti
1063
+ createConfetti();
1064
+ }
1065
+ }, stepDelay);
1066
+ }
1067
+
1068
+ function playCountdownEffect(finalResult) {
1069
+ wheelContainer.style.display = 'none';
1070
+ resultDisplay.style.display = 'block';
1071
+
1072
+ resultNumber.textContent = "3";
1073
+ resultText.textContent = "倒计时开始...";
1074
+
1075
+ // Countdown animation
1076
+ setTimeout(() => {
1077
+ resultNumber.textContent = "2";
1078
+ resultNumber.classList.add('jump');
1079
+ setTimeout(() => resultNumber.classList.remove('jump'), 300);
1080
+ }, 1000);
1081
+
1082
+ setTimeout(() => {
1083
+ resultNumber.textContent = "1";
1084
+ resultNumber.classList.add('jump');
1085
+ setTimeout(() => resultNumber.classList.remove('jump'), 300);
1086
+ }, 2000);
1087
+
1088
+ setTimeout(() => {
1089
+ resultNumber.classList.add('jump');
1090
+ resultNumber.textContent = finalResult;
1091
+ resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
1092
+
1093
+ // Add shine effect
1094
+ const shine = document.querySelector('.shine');
1095
+ shine.style.opacity = '1';
1096
+ shine.style.animation = 'shine 2s 1';
1097
+
1098
+ // Reset animations
1099
+ setTimeout(() => {
1100
+ resultNumber.classList.remove('jump');
1101
+ shine.style.opacity = '0';
1102
+ shine.style.animation = 'none';
1103
+ }, 2000);
1104
+
1105
+ // Add confetti
1106
+ createConfetti();
1107
+ }, 3000);
1108
+ }
1109
+
1110
+ function playEliminationEffect(finalResult) {
1111
+ eliminationRound++;
1112
+
1113
+ // If this is the first round, start with all candidates
1114
+ if (eliminationRound === 1) {
1115
+ wheelContainer.style.display = 'none';
1116
+ resultDisplay.style.display = 'block';
1117
+
1118
+ // Show how many candidates remain
1119
+ if (eliminationCandidates.length > 1) {
1120
+ resultNumber.textContent = eliminationCandidates.length;
1121
+ resultText.textContent = `第${eliminationRound}轮: 还剩${eliminationCandidates.length}名候选者`;
1122
+
1123
+ // Remove the selected candidate
1124
+ const index = eliminationCandidates.indexOf(finalResult);
1125
+ if (index > -1) {
1126
+ eliminationCandidates.splice(index, 1);
1127
+ }
1128
+
1129
+ // Provide option to continue elimination or select winner
1130
+ spinAgainBtn.textContent = "继续淘汰";
1131
+ const selectWinnerBtn = document.createElement('button');
1132
+ selectWinnerBtn.textContent = "选出获胜者";
1133
+ selectWinnerBtn.className = "btn btn-secondary";
1134
+ selectWinnerBtn.style.marginLeft = "10px";
1135
+
1136
+ // Add button to DOM if not already there
1137
+ if (!document.getElementById('select-winner-btn')) {
1138
+ selectWinnerBtn.id = 'select-winner-btn';
1139
+ spinAgainBtn.parentNode.appendChild(selectWinnerBtn);
1140
+
1141
+ // Add event listener
1142
+ selectWinnerBtn.addEventListener('click', function() {
1143
+ // Choose random winner from remaining candidates
1144
+ const winnerIndex = Math.floor(Math.random() * eliminationCandidates.length);
1145
+ const winner = eliminationCandidates[winnerIndex];
1146
+
1147
+ // Display winner
1148
+ resultNumber.textContent = winner;
1149
+ resultText.textContent = `最终获胜者是 ${winner} 号同学!`;
1150
+
1151
+ // Remove this button
1152
+ this.remove();
1153
+ spinAgainBtn.textContent = "再来一次";
1154
+
1155
+ // Add confetti
1156
+ createConfetti();
1157
+
1158
+ // Reset elimination mode
1159
+ eliminationRound = 0;
1160
+ eliminationCandidates = [];
1161
+ });
1162
+ }
1163
+ } else if (eliminationCandidates.length === 1) {
1164
+ // Only one candidate left - they're the winner
1165
+ resultNumber.textContent = eliminationCandidates[0];
1166
+ resultText.textContent = `最终获胜者是 ${eliminationCandidates[0]} 号同学!`;
1167
+ spinAgainBtn.textContent = "再来一次";
1168
+
1169
+ // Remove select winner button if it exists
1170
+ const selectWinnerBtn = document.getElementById('select-winner-btn');
1171
+ if (selectWinnerBtn) {
1172
+ selectWinnerBtn.remove();
1173
+ }
1174
+
1175
+ // Add confetti
1176
+ createConfetti();
1177
+
1178
+ // Reset elimination mode
1179
+ eliminationRound = 0;
1180
+ eliminationCandidates = [];
1181
+ }
1182
+ } else {
1183
+ // Subsequent rounds
1184
+ // Remove the newly selected candidate
1185
+ const index = eliminationCandidates.indexOf(finalResult);
1186
+ if (index > -1) {
1187
+ eliminationCandidates.splice(index, 1);
1188
+ }
1189
+
1190
+ // Show how many candidates remain
1191
+ resultNumber.textContent = eliminationCandidates.length;
1192
+
1193
+ if (eliminationCandidates.length > 1) {
1194
+ resultText.textContent = `第${eliminationRound}轮: 还剩${eliminationCandidates.length}名候选者`;
1195
+ } else if (eliminationCandidates.length === 1) {
1196
+ // Only one candidate left - they're the winner
1197
+ resultNumber.textContent = eliminationCandidates[0];
1198
+ resultText.textContent = `最终获胜者是 ${eliminationCandidates[0]} 号同学!`;
1199
+ spinAgainBtn.textContent = "再来一次";
1200
+
1201
+ // Remove select winner button if it exists
1202
+ const selectWinnerBtn = document.getElementById('select-winner-btn');
1203
+ if (selectWinnerBtn) {
1204
+ selectWinnerBtn.remove();
1205
+ }
1206
+
1207
+ // Add confetti
1208
+ createConfetti();
1209
+
1210
+ // Reset elimination mode
1211
+ eliminationRound = 0;
1212
+ eliminationCandidates = [];
1213
+ } else {
1214
+ // No candidates left (shouldn't happen normally)
1215
+ resultText.textContent = `所有候选者都已淘汰!`;
1216
+ spinAgainBtn.textContent = "再来一次";
1217
+
1218
+ // Remove select winner button if it exists
1219
+ const selectWinnerBtn = document.getElementById('select-winner-btn');
1220
+ if (selectWinnerBtn) {
1221
+ selectWinnerBtn.remove();
1222
+ }
1223
+
1224
+ // Reset elimination mode
1225
+ eliminationRound = 0;
1226
+ eliminationCandidates = [];
1227
+ }
1228
+ }
1229
+ }
1230
+
1231
+ function playSpotlightEffect(finalResult) {
1232
+ wheelContainer.style.display = 'none';
1233
+ resultDisplay.style.display = 'block';
1234
+
1235
+ // Create spotlight container if it doesn't exist
1236
+ let spotlightContainer = document.querySelector('.spotlight-container');
1237
+ if (!spotlightContainer) {
1238
+ spotlightContainer = document.createElement('div');
1239
+ spotlightContainer.className = 'spotlight-container';
1240
+ resultDisplay.appendChild(spotlightContainer);
1241
+ }
1242
+
1243
+ // Show the spotlight
1244
+ spotlightContainer.style.opacity = '1';
1245
+
1246
+ // First show blank result
1247
+ resultNumber.textContent = "?";
1248
+ resultText.textContent = "聚光灯将选择...";
1249
+
1250
+ // Add drumroll effect with increasing intensity
1251
+ let drumrollInterval;
1252
+ let intensity = 0;
1253
+ const maxIntensity = 10;
1254
+
1255
+ drumrollInterval = setInterval(() => {
1256
+ intensity++;
1257
+ resultNumber.style.opacity = 0.5 + (intensity / maxIntensity * 0.5);
1258
+
1259
+ if (intensity >= maxIntensity) {
1260
+ clearInterval(drumrollInterval);
1261
+
1262
+ // Show the result with dramatic effect
1263
+ resultNumber.classList.add('jump');
1264
+ resultNumber.textContent = finalResult;
1265
+ resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
1266
+
1267
+ // Apply spotlight pulse animation
1268
+ resultDisplay.style.animation = 'spotlight 2s';
1269
+
1270
+ // Create confetti
1271
+ createConfetti();
1272
+
1273
+ // Gradually fade out spotlight
1274
+ setTimeout(() => {
1275
+ spotlightContainer.style.opacity = '0';
1276
+ resultNumber.classList.remove('jump');
1277
+ resultDisplay.style.animation = 'none';
1278
+ }, 3000);
1279
+ }
1280
+ }, 300);
1281
+ }
1282
+
1283
+ function playHeartbeatEffect(finalResult) {
1284
+ wheelContainer.style.display = 'none';
1285
+ resultDisplay.style.display = 'block';
1286
+
1287
+ resultNumber.textContent = "?";
1288
+ resultText.textContent = "命运正在抉择...";
1289
+
1290
+ // Add heartbeat animation to result number
1291
+ resultNumber.style.animation = 'heartbeat 1s infinite';
1292
+
1293
+ // After a few heartbeats, reveal the result
1294
+ setTimeout(() => {
1295
+ resultNumber.style.animation = 'none';
1296
+ resultNumber.classList.add('jump');
1297
+ resultNumber.textContent = finalResult;
1298
+ resultText.textContent = `命运选择了 ${finalResult} 号同学!`;
1299
+
1300
+ // Add shine effect
1301
+ const shine = document.querySelector('.shine');
1302
+ shine.style.opacity = '1';
1303
+ shine.style.animation = 'shine 2s 1';
1304
+
1305
+ // Reset animations
1306
+ setTimeout(() => {
1307
+ resultNumber.classList.remove('jump');
1308
+ shine.style.opacity = '0';
1309
+ shine.style.animation = 'none';
1310
+ }, 2000);
1311
+
1312
+ // Add confetti
1313
+ createConfetti();
1314
+ }, 4000);
1315
+ }
1316
+
1317
+ function createConfetti() {
1318
+ const specialEffects = document.createElement('div');
1319
+ specialEffects.className = 'special-effects';
1320
+ resultDisplay.appendChild(specialEffects);
1321
+
1322
+ // Create confetti pieces
1323
+ for (let i = 0; i < 50; i++) {
1324
+ const confetti = document.createElement('div');
1325
+ confetti.className = 'confetti';
1326
+
1327
+ // Random position within container
1328
+ const left = Math.random() * 100 + '%';
1329
+ const top = -20 + 'px';
1330
+
1331
+ // Random size
1332
+ const size = Math.random() * 8 + 5;
1333
+
1334
+ // Random color
1335
+ const color = colors[Math.floor(Math.random() * colors.length)];
1336
+
1337
+ // Random shape
1338
+ const shapes = ['circle', 'square', 'triangle'];
1339
+ const shape = shapes[Math.floor(Math.random() * shapes.length)];
1340
+
1341
+ // Apply styles
1342
+ confetti.style.left = left;
1343
+ confetti.style.top = top;
1344
+ confetti.style.width = size + 'px';
1345
+ confetti.style.height = size + 'px';
1346
+ confetti.style.backgroundColor = color;
1347
+
1348
+ if (shape === 'circle') {
1349
+ confetti.style.borderRadius = '50%';
1350
+ } else if (shape === 'triangle') {
1351
+ confetti.style.clipPath = 'polygon(50% 0%, 0% 100%, 100% 100%)';
1352
+ }
1353
+
1354
+ // Add to container
1355
+ specialEffects.appendChild(confetti);
1356
+
1357
+ // Animate falling
1358
+ const duration = Math.random() * 3 + 2;
1359
+ const delay = Math.random() * 1.5;
1360
+
1361
+ confetti.style.opacity = '1';
1362
+ confetti.style.animation = `fall ${duration}s ease-in ${delay}s forwards`;
1363
+
1364
+ // Add keyframe animation for falling confetti
1365
+ if (!document.querySelector('#confetti-animation')) {
1366
+ const styleSheet = document.createElement('style');
1367
+ styleSheet.id = 'confetti-animation';
1368
+ styleSheet.textContent = `
1369
+ @keyframes fall {
1370
+ 0% {
1371
+ transform: translateY(0) rotate(0deg);
1372
+ opacity: 1;
1373
+ }
1374
+ 100% {
1375
+ transform: translateY(${resultDisplay.clientHeight}px) rotate(360deg);
1376
+ opacity: 0;
1377
+ }
1378
+ }
1379
+ `;
1380
+ document.head.appendChild(styleSheet);
1381
+ }
1382
+ }
1383
+
1384
+ // Remove confetti after animation
1385
+ setTimeout(() => {
1386
+ if (specialEffects && specialEffects.parentNode) {
1387
+ specialEffects.parentNode.removeChild(specialEffects);
1388
+ }
1389
+ }, 5000);
1390
+ }
1391
+
1392
+ function reset() {
1393
+ // Reset to setup panel
1394
+ setupPanel.classList.remove('minimized');
1395
+ wheelContainer.style.display = 'none';
1396
+ resultDisplay.style.display = 'none';
1397
+
1398
+ // Reset wheel rotation
1399
+ wheelInner.style.transform = 'rotate(0deg)';
1400
+
1401
+ // Reset variables
1402
+ isSpinning = false;
1403
+ selectedSegment = null;
1404
+ secondSelectedSegment = null;
1405
+ }
1406
+
1407
+ // Initialize the app
1408
+ function init() {
1409
+ // Setup event listeners
1410
+ startBtn.addEventListener('click', setupWheel);
1411
+ resetBtn.addEventListener('click', reset);
1412
+ spinBtn.addEventListener('click', spin);
1413
+ spinAgainBtn.addEventListener('click', () => {
1414
+ resultDisplay.style.display = 'none';
1415
+ wheelContainer.style.display = 'block';
1416
+ });
1417
+
1418
+ pickerModeSelect.addEventListener('change', updateModeDescription);
1419
+
1420
+ helpBubble.addEventListener('click', () => {
1421
+ helpContent.style.display = 'block';
1422
+ });
1423
+
1424
+ closeHelp.addEventListener('click', () => {
1425
+ helpContent.style.display = 'none';
1426
+ });
1427
+
1428
+ // Initialize mode description
1429
+ updateModeDescription();
1430
+ }
1431
+
1432
+ // Initialize the app
1433
+ init();
1434
+ </script>
1435
+ </body>
1436
+ </html>