mistpe commited on
Commit
6ff84a7
·
verified ·
1 Parent(s): 8e1653d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1014 -19
index.html CHANGED
@@ -1,19 +1,1014 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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: #FF9E45;
10
+ --secondary-color: #FFB347;
11
+ --accent-color: #FF6B6B;
12
+ --light-color: #FFF8E6;
13
+ --text-color: #3D3D3D;
14
+ --shadow-color: rgba(255, 158, 69, 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%, #FFE8CC 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 #FFD8B1;
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
+ .flicker {
379
+ animation: flicker 0.2s infinite;
380
+ }
381
+
382
+ @keyframes flicker {
383
+ 0% { opacity: 1; }
384
+ 50% { opacity: 0.7; }
385
+ 100% { opacity: 1; }
386
+ }
387
+
388
+ .shake {
389
+ animation: shake 0.1s infinite;
390
+ }
391
+
392
+ @keyframes shake {
393
+ 0% { transform: translateX(0); }
394
+ 25% { transform: translateX(-5px); }
395
+ 50% { transform: translateX(0); }
396
+ 75% { transform: translateX(5px); }
397
+ 100% { transform: translateX(0); }
398
+ }
399
+
400
+ .jump {
401
+ animation: jump 0.5s;
402
+ }
403
+
404
+ @keyframes jump {
405
+ 0% { transform: scale(1); }
406
+ 50% { transform: scale(1.2); }
407
+ 100% { transform: scale(1); }
408
+ }
409
+
410
+ .help-bubble {
411
+ position: fixed;
412
+ bottom: 20px;
413
+ right: 20px;
414
+ background-color: var(--primary-color);
415
+ color: white;
416
+ width: 50px;
417
+ height: 50px;
418
+ border-radius: 50%;
419
+ display: flex;
420
+ align-items: center;
421
+ justify-content: center;
422
+ font-size: 1.5rem;
423
+ cursor: pointer;
424
+ box-shadow: 0 3px 10px var(--shadow-color);
425
+ transition: all 0.3s;
426
+ }
427
+
428
+ .help-bubble:hover {
429
+ background-color: var(--accent-color);
430
+ transform: scale(1.1);
431
+ }
432
+
433
+ .help-content {
434
+ position: fixed;
435
+ bottom: 80px;
436
+ right: 20px;
437
+ background-color: white;
438
+ padding: 1.5rem;
439
+ border-radius: 1rem;
440
+ width: 300px;
441
+ box-shadow: 0 10px 30px var(--shadow-color);
442
+ display: none;
443
+ z-index: 100;
444
+ }
445
+
446
+ .help-content h3 {
447
+ color: var(--primary-color);
448
+ margin-bottom: 1rem;
449
+ }
450
+
451
+ .help-content p {
452
+ margin-bottom: 0.8rem;
453
+ font-size: 0.9rem;
454
+ }
455
+
456
+ .close-help {
457
+ position: absolute;
458
+ top: 10px;
459
+ right: 10px;
460
+ font-size: 1.2rem;
461
+ cursor: pointer;
462
+ color: var(--accent-color);
463
+ }
464
+ </style>
465
+ </head>
466
+ <body>
467
+ <div class="container">
468
+ <header>
469
+ <h1>阳光点名转盘</h1>
470
+ <p class="subtitle">让点名充满乐趣和惊喜!</p>
471
+ </header>
472
+
473
+ <div class="setup-panel">
474
+ <h2 class="panel-title">设置</h2>
475
+ <div class="form-group">
476
+ <label for="class-size">班级人数:</label>
477
+ <input type="number" id="class-size" min="1" max="100" value="30">
478
+ </div>
479
+
480
+ <div class="form-group">
481
+ <label for="start-number">起始编号 (默认从1开始):</label>
482
+ <input type="number" id="start-number" min="0" value="1">
483
+ </div>
484
+
485
+ <div class="form-group">
486
+ <label for="picker-mode">选择模式:</label>
487
+ <select id="picker-mode">
488
+ <option value="normal">正常模式</option>
489
+ <option value="fake-out">假动作模式</option>
490
+ <option value="oscillate">摇摆不定模式</option>
491
+ <option value="mystery">神秘模式</option>
492
+ <option value="double">双重选择模式</option>
493
+ </select>
494
+ <div id="mode-normal" class="mode-description">普通的转盘选择,公平公正。</div>
495
+ <div id="mode-fake-out" class="mode-description">即将停止时,转盘会突然改变方向!</div>
496
+ <div id="mode-oscillate" class="mode-description">在两个结果之间反复摇摆,到底会是谁呢?</div>
497
+ <div id="mode-mystery" class="mode-description">结果会短暂显示然后突然变化,充满惊喜!</div>
498
+ <div id="mode-double" class="mode-description">同时选出两个幸运��!</div>
499
+ </div>
500
+
501
+ <div class="actions">
502
+ <button id="start-btn" class="btn">开始点名</button>
503
+ <button id="reset-btn" class="btn btn-secondary">重置</button>
504
+ </div>
505
+ </div>
506
+
507
+ <div class="wheel-container">
508
+ <div class="wheel-pointer"></div>
509
+ <div class="wheel">
510
+ <div class="wheel-inner" id="wheel-inner"></div>
511
+ </div>
512
+ <div class="wheel-center" id="spin-btn">转!</div>
513
+ </div>
514
+
515
+ <div class="result-display">
516
+ <div class="shine"></div>
517
+ <h2>点名结果</h2>
518
+ <div class="result-number" id="result-number">?</div>
519
+ <p class="result-text" id="result-text">请点击转盘开始</p>
520
+ <button id="spin-again-btn" class="btn">再来一次</button>
521
+ </div>
522
+ </div>
523
+
524
+ <div class="help-bubble">?</div>
525
+ <div class="help-content">
526
+ <span class="close-help">×</span>
527
+ <h3>使用帮助</h3>
528
+ <p><strong>正常模式:</strong> 普通的点名选择,公平公正。</p>
529
+ <p><strong>假动作模式:</strong> 转盘减速后会有一次假动作,然后才会显示最终结果。</p>
530
+ <p><strong>摇摆不定模式:</strong> 在两个选项之间来回摇摆,增加悬念感。</p>
531
+ <p><strong>神秘模式:</strong> 先显示一个结果,然后突然改变!谁也猜不到最终会是谁。</p>
532
+ <p><strong>双重选择模式:</strong> 同时选出两名同学,适合小组活动。</p>
533
+ </div>
534
+
535
+ <script>
536
+ // DOM Elements
537
+ const setupPanel = document.querySelector('.setup-panel');
538
+ const classSizeInput = document.getElementById('class-size');
539
+ const startNumberInput = document.getElementById('start-number');
540
+ const pickerModeSelect = document.getElementById('picker-mode');
541
+ const startBtn = document.getElementById('start-btn');
542
+ const resetBtn = document.getElementById('reset-btn');
543
+ const wheelContainer = document.querySelector('.wheel-container');
544
+ const wheelInner = document.getElementById('wheel-inner');
545
+ const spinBtn = document.getElementById('spin-btn');
546
+ const resultDisplay = document.querySelector('.result-display');
547
+ const resultNumber = document.getElementById('result-number');
548
+ const resultText = document.getElementById('result-text');
549
+ const spinAgainBtn = document.getElementById('spin-again-btn');
550
+ const helpBubble = document.querySelector('.help-bubble');
551
+ const helpContent = document.querySelector('.help-content');
552
+ const closeHelp = document.querySelector('.close-help');
553
+
554
+ // Mode descriptions
555
+ const modeDescriptions = {
556
+ normal: document.getElementById('mode-normal'),
557
+ 'fake-out': document.getElementById('mode-fake-out'),
558
+ oscillate: document.getElementById('mode-oscillate'),
559
+ mystery: document.getElementById('mode-mystery'),
560
+ double: document.getElementById('mode-double')
561
+ };
562
+
563
+ // Variables
564
+ let classSize = 30;
565
+ let startNumber = 1;
566
+ let mode = 'normal';
567
+ let isSpinning = false;
568
+ let segments = [];
569
+ let selectedSegment = null;
570
+ let secondSelectedSegment = null;
571
+
572
+ // Colors for the wheel segments (warm, sunlight palette)
573
+ const colors = [
574
+ '#FF9E45', '#FFB347', '#FF8C42', '#FFC154',
575
+ '#FF6B6B', '#FF9671', '#FFA25B', '#FFD56B',
576
+ '#FFBB69', '#FFD166', '#FFC857', '#E9C46A',
577
+ '#F4A261', '#FF7F50', '#FF8966', '#FFBA7C'
578
+ ];
579
+
580
+ // Initialize
581
+ function init() {
582
+ // Setup event listeners
583
+ startBtn.addEventListener('click', setupWheel);
584
+ resetBtn.addEventListener('click', reset);
585
+ spinBtn.addEventListener('click', spin);
586
+ spinAgainBtn.addEventListener('click', () => {
587
+ resultDisplay.style.display = 'none';
588
+ wheelContainer.style.display = 'block';
589
+ });
590
+
591
+ pickerModeSelect.addEventListener('change', updateModeDescription);
592
+
593
+ helpBubble.addEventListener('click', () => {
594
+ helpContent.style.display = 'block';
595
+ });
596
+
597
+ closeHelp.addEventListener('click', () => {
598
+ helpContent.style.display = 'none';
599
+ });
600
+
601
+ // Initialize mode description
602
+ updateModeDescription();
603
+ }
604
+
605
+ function updateModeDescription() {
606
+ const selectedMode = pickerModeSelect.value;
607
+
608
+ // Hide all descriptions
609
+ Object.values(modeDescriptions).forEach(desc => {
610
+ desc.style.display = 'none';
611
+ });
612
+
613
+ // Show selected description
614
+ const descElement = document.getElementById(`mode-${selectedMode}`);
615
+ if (descElement) {
616
+ descElement.style.display = 'block';
617
+ }
618
+ }
619
+
620
+ function setupWheel() {
621
+ classSize = parseInt(classSizeInput.value) || 30;
622
+ startNumber = parseInt(startNumberInput.value) || 1;
623
+ mode = pickerModeSelect.value;
624
+
625
+ if (classSize < 1) {
626
+ alert('班级人数必须大于0');
627
+ return;
628
+ }
629
+
630
+ // Create wheel segments
631
+ createWheel();
632
+
633
+ // Show wheel, hide setup
634
+ setupPanel.classList.add('minimized');
635
+ wheelContainer.style.display = 'block';
636
+ resultDisplay.style.display = 'none';
637
+
638
+ // Make setup panel expandable when minimized
639
+ setupPanel.addEventListener('click', function() {
640
+ if (this.classList.contains('minimized')) {
641
+ this.classList.remove('minimized');
642
+ }
643
+ });
644
+ }
645
+
646
+ function createWheel() {
647
+ // Clear previous wheel
648
+ wheelInner.innerHTML = '';
649
+ segments = [];
650
+
651
+ // Create segments
652
+ const segmentAngle = 360 / classSize;
653
+
654
+ for (let i = 0; i < classSize; i++) {
655
+ const segmentElement = document.createElement('div');
656
+ segmentElement.className = 'segment';
657
+
658
+ // Rotate and position segment
659
+ const rotation = i * segmentAngle;
660
+ segmentElement.style.transform = `rotate(${rotation}deg)`;
661
+ segmentElement.style.backgroundColor = colors[i % colors.length];
662
+
663
+ // Create content with student number
664
+ const contentElement = document.createElement('div');
665
+ contentElement.className = 'segment-content';
666
+ const studentNumber = startNumber + i;
667
+ contentElement.textContent = studentNumber;
668
+
669
+ segmentElement.appendChild(contentElement);
670
+ wheelInner.appendChild(segmentElement);
671
+
672
+ // Store segment data
673
+ segments.push({
674
+ element: segmentElement,
675
+ rotation,
676
+ value: studentNumber
677
+ });
678
+ }
679
+ }
680
+
681
+ function spin() {
682
+ if (isSpinning) return;
683
+
684
+ isSpinning = true;
685
+ wheelContainer.classList.add('spinning');
686
+
687
+ // Calculate random rotation (5-10 full rotations + random offset)
688
+ const minRotation = 1800; // 5 full rotations (5 * 360)
689
+ const maxRotation = 3600; // 10 full rotations (10 * 360)
690
+
691
+ // Random rotation
692
+ const segmentAngle = 360 / classSize;
693
+ const randomOffset = Math.floor(Math.random() * classSize);
694
+ const finalSegmentIndex = randomOffset;
695
+
696
+ // Calculate total rotation
697
+ let totalRotation = minRotation + (maxRotation - minRotation) * Math.random();
698
+
699
+ // Add offset to land on the selected segment
700
+ totalRotation += (finalSegmentIndex * segmentAngle);
701
+
702
+ // Apply rotation to wheel
703
+ wheelInner.style.transform = `rotate(${-totalRotation}deg)`;
704
+
705
+ // Get the selected segment
706
+ selectedSegment = segments[finalSegmentIndex];
707
+
708
+ // For double mode, select a second segment
709
+ if (mode === 'double') {
710
+ let secondIndex = (finalSegmentIndex + Math.floor(classSize / 2)) % classSize;
711
+ secondSelectedSegment = segments[secondIndex];
712
+ }
713
+
714
+ // Process result after spinning animation
715
+ setTimeout(() => {
716
+ processResult();
717
+ }, 5000); // Same as spin animation duration
718
+ }
719
+
720
+ function processResult() {
721
+ isSpinning = false;
722
+ wheelContainer.classList.remove('spinning');
723
+
724
+ let finalResult = selectedSegment.value;
725
+ let secondResult = secondSelectedSegment ? secondSelectedSegment.value : null;
726
+
727
+ switch (mode) {
728
+ case 'fake-out':
729
+ playFakeOutEffect(finalResult);
730
+ break;
731
+
732
+ case 'oscillate':
733
+ playOscillateEffect(finalResult);
734
+ break;
735
+
736
+ case 'mystery':
737
+ playMysteryEffect(finalResult);
738
+ break;
739
+
740
+ case 'double':
741
+ showDoubleResult(finalResult, secondResult);
742
+ break;
743
+
744
+ default:
745
+ showResult(finalResult);
746
+ break;
747
+ }
748
+ }
749
+
750
+ function showResult(result) {
751
+ wheelContainer.style.display = 'none';
752
+ resultDisplay.style.display = 'block';
753
+
754
+ resultNumber.textContent = result;
755
+ resultText.textContent = `恭喜 ${result} 号同学被选中!`;
756
+
757
+ // Add shine effect
758
+ const shine = document.querySelector('.shine');
759
+ shine.style.opacity = '1';
760
+ shine.style.animation = 'shine 2s 1';
761
+
762
+ // Reset animation after it completes
763
+ setTimeout(() => {
764
+ shine.style.opacity = '0';
765
+ shine.style.animation = 'none';
766
+ }, 2000);
767
+
768
+ // Add confetti effect
769
+ createConfetti();
770
+ }
771
+
772
+ function showDoubleResult(result1, result2) {
773
+ wheelContainer.style.display = 'none';
774
+ resultDisplay.style.display = 'block';
775
+
776
+ resultNumber.textContent = `${result1} & ${result2}`;
777
+ resultText.textContent = `恭喜 ${result1} 号和 ${result2} 号同学被选中!`;
778
+
779
+ // Add shine effect
780
+ const shine = document.querySelector('.shine');
781
+ shine.style.opacity = '1';
782
+ shine.style.animation = 'shine 2s 1';
783
+
784
+ // Reset animation after it completes
785
+ setTimeout(() => {
786
+ shine.style.opacity = '0';
787
+ shine.style.animation = 'none';
788
+ }, 2000);
789
+
790
+ // Add confetti effect
791
+ createConfetti();
792
+ }
793
+
794
+ function playFakeOutEffect(finalResult) {
795
+ // First show a fake result
796
+ const fakeResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber;
797
+
798
+ wheelContainer.style.display = 'none';
799
+ resultDisplay.style.display = 'block';
800
+
801
+ resultNumber.textContent = fakeResult;
802
+ resultText.textContent = `恭喜 ${fakeResult} 号同学被选中!`;
803
+
804
+ // After a short delay, apply shake effect and change to actual result
805
+ setTimeout(() => {
806
+ resultNumber.classList.add('shake');
807
+ resultText.textContent = "等等,发生了什么...";
808
+
809
+ // After shake effect, reveal true result
810
+ setTimeout(() => {
811
+ resultNumber.classList.remove('shake');
812
+ resultNumber.classList.add('jump');
813
+ resultNumber.textContent = finalResult;
814
+ resultText.textContent = `真正被选中的是 ${finalResult} 号同学!`;
815
+
816
+ // Add shine effect
817
+ const shine = document.querySelector('.shine');
818
+ shine.style.opacity = '1';
819
+ shine.style.animation = 'shine 2s 1';
820
+
821
+ // Reset animations
822
+ setTimeout(() => {
823
+ resultNumber.classList.remove('jump');
824
+ shine.style.opacity = '0';
825
+ shine.style.animation = 'none';
826
+ }, 2000);
827
+
828
+ // Add confetti for the true result
829
+ createConfetti();
830
+ }, 1000);
831
+ }, 1500);
832
+ }
833
+
834
+ function playOscillateEffect(finalResult) {
835
+ // Create alternative result
836
+ const altResult = ((finalResult - startNumber + Math.floor(classSize / 2)) % classSize) + startNumber;
837
+
838
+ wheelContainer.style.display = 'none';
839
+ resultDisplay.style.display = 'block';
840
+
841
+ let count = 0;
842
+ const maxOscillations = 6;
843
+ const oscillationInterval = setInterval(() => {
844
+ count++;
845
+ resultNumber.textContent = count % 2 === 0 ? finalResult : altResult;
846
+ resultText.textContent = "究竟会是谁呢...";
847
+
848
+ if (count >= maxOscillations) {
849
+ clearInterval(oscillationInterval);
850
+ // Final result with visual effect
851
+ resultNumber.classList.add('jump');
852
+ resultNumber.textContent = finalResult;
853
+ resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
854
+
855
+ // Add shine effect
856
+ const shine = document.querySelector('.shine');
857
+ shine.style.opacity = '1';
858
+ shine.style.animation = 'shine 2s 1';
859
+
860
+ // Reset animations
861
+ setTimeout(() => {
862
+ resultNumber.classList.remove('jump');
863
+ shine.style.opacity = '0';
864
+ shine.style.animation = 'none';
865
+ }, 2000);
866
+
867
+ // Add confetti
868
+ createConfetti();
869
+ }
870
+ }, 300);
871
+ }
872
+
873
+ function playMysteryEffect(finalResult) {
874
+ // Generate a series of random results ending with the actual one
875
+ const mysterySteps = 3;
876
+ const stepDelay = 600;
877
+
878
+ wheelContainer.style.display = 'none';
879
+ resultDisplay.style.display = 'block';
880
+
881
+ let step = 0;
882
+ resultText.textContent = "神秘数字即将揭晓...";
883
+
884
+ // Add flicker effect to create suspense
885
+ resultNumber.classList.add('flicker');
886
+
887
+ const mysteryInterval = setInterval(() => {
888
+ step++;
889
+
890
+ if (step < mysterySteps) {
891
+ // Show random number
892
+ const randomNumber = Math.floor(Math.random() * classSize) + startNumber;
893
+ resultNumber.textContent = randomNumber;
894
+ } else {
895
+ // Last step - show real result
896
+ clearInterval(mysteryInterval);
897
+ resultNumber.classList.remove('flicker');
898
+ resultNumber.classList.add('jump');
899
+ resultNumber.textContent = finalResult;
900
+ resultText.textContent = `恭喜 ${finalResult} 号同学被选中!`;
901
+
902
+ // Add shine effect
903
+ const shine = document.querySelector('.shine');
904
+ shine.style.opacity = '1';
905
+ shine.style.animation = 'shine 2s 1';
906
+
907
+ // Reset animations
908
+ setTimeout(() => {
909
+ resultNumber.classList.remove('jump');
910
+ shine.style.opacity = '0';
911
+ shine.style.animation = 'none';
912
+ }, 2000);
913
+
914
+ // Add confetti
915
+ createConfetti();
916
+ }
917
+ }, stepDelay);
918
+ }
919
+
920
+ function createConfetti() {
921
+ const specialEffects = document.createElement('div');
922
+ specialEffects.className = 'special-effects';
923
+ resultDisplay.appendChild(specialEffects);
924
+
925
+ // Create confetti pieces
926
+ for (let i = 0; i < 50; i++) {
927
+ const confetti = document.createElement('div');
928
+ confetti.className = 'confetti';
929
+
930
+ // Random position within container
931
+ const left = Math.random() * 100 + '%';
932
+ const top = -20 + 'px';
933
+
934
+ // Random size
935
+ const size = Math.random() * 8 + 5;
936
+
937
+ // Random color
938
+ const color = colors[Math.floor(Math.random() * colors.length)];
939
+
940
+ // Random shape
941
+ const shapes = ['circle', 'square', 'triangle'];
942
+ const shape = shapes[Math.floor(Math.random() * shapes.length)];
943
+
944
+ // Apply styles
945
+ confetti.style.left = left;
946
+ confetti.style.top = top;
947
+ confetti.style.width = size + 'px';
948
+ confetti.style.height = size + 'px';
949
+ confetti.style.backgroundColor = color;
950
+
951
+ if (shape === 'circle') {
952
+ confetti.style.borderRadius = '50%';
953
+ } else if (shape === 'triangle') {
954
+ confetti.style.clipPath = 'polygon(50% 0%, 0% 100%, 100% 100%)';
955
+ }
956
+
957
+ // Add to container
958
+ specialEffects.appendChild(confetti);
959
+
960
+ // Animate falling
961
+ const duration = Math.random() * 3 + 2;
962
+ const delay = Math.random() * 1.5;
963
+
964
+ confetti.style.opacity = '1';
965
+ confetti.style.animation = `fall ${duration}s ease-in ${delay}s forwards`;
966
+
967
+ // Add keyframe animation for falling confetti
968
+ if (!document.querySelector('#confetti-animation')) {
969
+ const styleSheet = document.createElement('style');
970
+ styleSheet.id = 'confetti-animation';
971
+ styleSheet.textContent = `
972
+ @keyframes fall {
973
+ 0% {
974
+ transform: translateY(0) rotate(0deg);
975
+ opacity: 1;
976
+ }
977
+ 100% {
978
+ transform: translateY(${resultDisplay.clientHeight}px) rotate(360deg);
979
+ opacity: 0;
980
+ }
981
+ }
982
+ `;
983
+ document.head.appendChild(styleSheet);
984
+ }
985
+ }
986
+
987
+ // Remove confetti after animation
988
+ setTimeout(() => {
989
+ if (specialEffects && specialEffects.parentNode) {
990
+ specialEffects.parentNode.removeChild(specialEffects);
991
+ }
992
+ }, 5000);
993
+ }
994
+
995
+ function reset() {
996
+ // Reset to setup panel
997
+ setupPanel.classList.remove('minimized');
998
+ wheelContainer.style.display = 'none';
999
+ resultDisplay.style.display = 'none';
1000
+
1001
+ // Reset wheel rotation
1002
+ wheelInner.style.transform = 'rotate(0deg)';
1003
+
1004
+ // Reset variables
1005
+ isSpinning = false;
1006
+ selectedSegment = null;
1007
+ secondSelectedSegment = null;
1008
+ }
1009
+
1010
+ // Initialize the app
1011
+ init();
1012
+ </script>
1013
+ </body>
1014
+ </html>