dvc890 commited on
Commit
9525472
·
verified ·
1 Parent(s): 7ac1491

Update pages/GameZen.tsx

Browse files
Files changed (1) hide show
  1. pages/GameZen.tsx +74 -28
pages/GameZen.tsx CHANGED
@@ -176,6 +176,7 @@ export const GameZen: React.FC = () => {
176
  // Game Logic
177
  if (delta > 0) {
178
  setTotalSeconds(prev => prev + delta);
 
179
  if (vol < threshold) {
180
  setQuietSeconds(prev => prev + delta);
181
  }
@@ -194,7 +195,16 @@ export const GameZen: React.FC = () => {
194
 
195
  // Score Calculation
196
  const calculatedScore = totalSeconds > 0 ? Math.round((quietSeconds / totalSeconds) * 100) : 100;
197
- const isQuiet = currentVolume < threshold;
 
 
 
 
 
 
 
 
 
198
 
199
  const handleBatchGrant = async () => {
200
  const targets = students.filter(s => !excludedStudentIds.has(s._id || String(s.id)));
@@ -232,43 +242,67 @@ export const GameZen: React.FC = () => {
232
  width: '100vw',
233
  height: '100vh',
234
  zIndex: 999999,
235
- backgroundColor: '#0f766e',
236
  } : {};
237
 
238
- // Zen Visuals
239
- const monkY = isQuiet ? -20 : 0;
240
- const monkScale = isQuiet ? 1.1 : 1;
241
- const glowOpacity = isQuiet ? 0.6 : 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
  const GameContent = (
244
  <div
245
- className={`${isFullscreen ? '' : 'h-full w-full relative'} flex flex-col bg-teal-800 overflow-hidden select-none transition-all duration-300 font-sans`}
246
  style={containerStyle}
247
  >
248
- {/* Background */}
249
- <div className="absolute inset-0 bg-gradient-to-b from-teal-900 to-teal-700 opacity-90 pointer-events-none"></div>
250
- <div className="absolute inset-0 flex items-center justify-center pointer-events-none opacity-10">
251
- <div className={`w-[800px] h-[800px] border-[50px] rounded-full border-white transition-all duration-1000 ${isQuiet ? 'scale-100' : 'scale-90 border-red-500'}`}></div>
252
  </div>
253
 
254
  {/* HUD */}
255
  <div className="absolute top-4 left-4 z-50 flex gap-3">
256
- <div className="bg-black/30 border border-teal-300/30 rounded-xl p-3 min-w-[120px] text-center backdrop-blur-md">
257
- <span className="text-[10px] text-teal-200 font-bold uppercase block">倒计时</span>
258
  <span className="text-3xl font-mono font-black text-white">
259
  {Math.floor(timeLeft / 60)}:{String(Math.floor(timeLeft % 60)).padStart(2, '0')}
260
  </span>
261
  </div>
262
- <div className="bg-black/30 border border-teal-300/30 rounded-xl p-3 min-w-[120px] text-center backdrop-blur-md">
263
- <span className="text-[10px] text-teal-200 font-bold uppercase block">专注评分</span>
264
  <span className={`text-3xl font-mono font-black ${calculatedScore >= passRate ? 'text-green-400' : 'text-red-400'}`}>{calculatedScore}</span>
265
  </div>
266
  </div>
267
 
268
  {/* Status Indicator */}
269
  <div className="absolute top-4 right-4 z-50">
270
- <div className={`px-4 py-2 rounded-full font-bold shadow-lg backdrop-blur-md transition-colors ${isQuiet ? 'bg-green-500/20 text-green-300 border border-green-500/50' : 'bg-red-500/20 text-red-300 border border-red-500/50'}`}>
271
- {isQuiet ? '🌿 极静 · 专注' : '⚠️ 喧哗 · 浮躁'}
272
  </div>
273
  </div>
274
 
@@ -311,20 +345,20 @@ export const GameZen: React.FC = () => {
311
  <div className="flex-1 relative z-10 flex flex-col items-center justify-center pb-20">
312
  {/* Aura */}
313
  <div
314
- className="absolute w-64 h-64 bg-yellow-200 rounded-full blur-[80px] transition-all duration-1000"
315
- style={{ opacity: glowOpacity, transform: `scale(${isQuiet ? 1.5 : 0.5})` }}
316
  ></div>
317
 
318
  {/* Monk/Visual */}
319
  <div
320
- className="text-[150px] transition-all duration-1000 ease-in-out relative z-20 drop-shadow-2xl"
321
- style={{ transform: `translateY(${monkY}px) scale(${monkScale})` }}
322
  >
323
- {isQuiet ? '🧘' : '😖'}
324
  </div>
325
 
326
- {/* Levitation Base */}
327
- <div className={`w-40 h-10 bg-black/20 rounded-[100%] blur-md transition-all duration-1000 ${isQuiet ? 'scale-75 opacity-50' : 'scale-100 opacity-80'}`}></div>
328
  </div>
329
 
330
  {/* Config Modal */}
@@ -346,24 +380,36 @@ export const GameZen: React.FC = () => {
346
  <span>阈值设定: {threshold}</span>
347
  </div>
348
  <div className="w-full h-6 bg-gray-800 rounded-full overflow-hidden relative border border-gray-600">
 
349
  <div className="absolute top-0 bottom-0 w-0.5 bg-yellow-500 z-10 shadow-[0_0_5px_yellow]" style={{left: `${threshold}%`}}></div>
 
 
350
  <div
351
- className={`h-full transition-all duration-75 ${currentVolume < threshold ? 'bg-green-500' : 'bg-red-500'}`}
 
 
 
 
352
  style={{width: `${Math.min(currentVolume, 100)}%`}}
353
  ></div>
354
  </div>
355
- <div className="mt-2">
 
 
 
 
 
356
  <input type="range" min="1" max="100" className="w-full h-2 bg-slate-700 rounded-lg appearance-none cursor-pointer" value={threshold} onChange={e=>setThreshold(Number(e.target.value))}/>
357
  </div>
358
  </div>
359
  <button
360
- onClick={() => { startAudio(); setThreshold(currentVolume + 5); }}
361
  className="px-4 py-2 bg-slate-700 hover:bg-slate-600 rounded-lg text-xs font-bold whitespace-nowrap border border-slate-600"
362
  >
363
  <Volume2 size={14} className="inline mr-1"/> 自动设定
364
  </button>
365
  </div>
366
- <p className="text-[10px] text-gray-500 mt-2">* 点击“自动设定”前请让教室保持您期望的安静状态系统将自动设置合适的阈值。</p>
367
  </div>
368
 
369
  <div>
 
176
  // Game Logic
177
  if (delta > 0) {
178
  setTotalSeconds(prev => prev + delta);
179
+ // Count as quiet only if below threshold (Levels 1 & 2)
180
  if (vol < threshold) {
181
  setQuietSeconds(prev => prev + delta);
182
  }
 
195
 
196
  // Score Calculation
197
  const calculatedScore = totalSeconds > 0 ? Math.round((quietSeconds / totalSeconds) * 100) : 100;
198
+
199
+ // Determine Current State (4 Levels)
200
+ const getZenState = () => {
201
+ if (currentVolume < threshold * 0.5) return 'DEEP';
202
+ if (currentVolume < threshold) return 'FOCUSED';
203
+ if (currentVolume < threshold * 1.5) return 'RESTLESS';
204
+ return 'CHAOS';
205
+ };
206
+
207
+ const currentState = getZenState();
208
 
209
  const handleBatchGrant = async () => {
210
  const targets = students.filter(s => !excludedStudentIds.has(s._id || String(s.id)));
 
242
  width: '100vw',
243
  height: '100vh',
244
  zIndex: 999999,
 
245
  } : {};
246
 
247
+ // Visual Parameters Mapping
248
+ const visualParams = {
249
+ DEEP: {
250
+ bg: 'bg-gradient-to-b from-teal-900 to-teal-800',
251
+ monk: '🧘', monkY: -50, monkScale: 1.2,
252
+ text: '🌿 极静 · 入定', badge: 'bg-teal-500/20 text-teal-200 border-teal-500/50',
253
+ auraColor: 'bg-emerald-300', auraOpacity: 0.6, auraScale: 1.5
254
+ },
255
+ FOCUSED: {
256
+ bg: 'bg-gradient-to-b from-emerald-800 to-emerald-600',
257
+ monk: '🧘', monkY: 0, monkScale: 1.0,
258
+ text: '🍃 专注 · 宁静', badge: 'bg-emerald-500/20 text-emerald-100 border-emerald-500/50',
259
+ auraColor: 'bg-emerald-200', auraOpacity: 0.3, auraScale: 1.1
260
+ },
261
+ RESTLESS: {
262
+ bg: 'bg-gradient-to-b from-amber-800 to-orange-700',
263
+ monk: '😰', monkY: 10, monkScale: 0.95,
264
+ text: '🍂 浮躁 · 波动', badge: 'bg-amber-500/20 text-amber-100 border-amber-500/50',
265
+ auraColor: 'bg-orange-400', auraOpacity: 0.2, auraScale: 0.9
266
+ },
267
+ CHAOS: {
268
+ bg: 'bg-gradient-to-b from-red-900 to-red-700',
269
+ monk: '😖', monkY: 20, monkScale: 0.9,
270
+ text: '🔥 喧哗 · 破功', badge: 'bg-red-500/20 text-red-100 border-red-500/50',
271
+ auraColor: 'bg-red-500', auraOpacity: 0.1, auraScale: 0.8
272
+ }
273
+ };
274
+
275
+ const currentVisual = visualParams[currentState];
276
 
277
  const GameContent = (
278
  <div
279
+ className={`${isFullscreen ? '' : 'h-full w-full relative'} flex flex-col overflow-hidden select-none transition-all duration-1000 ease-in-out font-sans ${currentVisual.bg}`}
280
  style={containerStyle}
281
  >
282
+ {/* Background Overlay */}
283
+ <div className="absolute inset-0 bg-black/10 pointer-events-none"></div>
284
+ <div className="absolute inset-0 flex items-center justify-center pointer-events-none opacity-20">
285
+ <div className={`w-[800px] h-[800px] border-[50px] rounded-full border-white transition-all duration-1000 ${currentState === 'DEEP' ? 'scale-110 opacity-50' : 'scale-90 opacity-20'}`}></div>
286
  </div>
287
 
288
  {/* HUD */}
289
  <div className="absolute top-4 left-4 z-50 flex gap-3">
290
+ <div className="bg-black/30 border border-white/20 rounded-xl p-3 min-w-[120px] text-center backdrop-blur-md">
291
+ <span className="text-[10px] text-gray-300 font-bold uppercase block">倒计时</span>
292
  <span className="text-3xl font-mono font-black text-white">
293
  {Math.floor(timeLeft / 60)}:{String(Math.floor(timeLeft % 60)).padStart(2, '0')}
294
  </span>
295
  </div>
296
+ <div className="bg-black/30 border border-white/20 rounded-xl p-3 min-w-[120px] text-center backdrop-blur-md">
297
+ <span className="text-[10px] text-gray-300 font-bold uppercase block">专注评分</span>
298
  <span className={`text-3xl font-mono font-black ${calculatedScore >= passRate ? 'text-green-400' : 'text-red-400'}`}>{calculatedScore}</span>
299
  </div>
300
  </div>
301
 
302
  {/* Status Indicator */}
303
  <div className="absolute top-4 right-4 z-50">
304
+ <div className={`px-6 py-3 rounded-full font-bold text-lg shadow-lg backdrop-blur-md transition-all duration-500 border ${currentVisual.badge}`}>
305
+ {currentVisual.text}
306
  </div>
307
  </div>
308
 
 
345
  <div className="flex-1 relative z-10 flex flex-col items-center justify-center pb-20">
346
  {/* Aura */}
347
  <div
348
+ className={`absolute w-64 h-64 rounded-full blur-[80px] transition-all duration-1000 ${currentVisual.auraColor}`}
349
+ style={{ opacity: currentVisual.auraOpacity, transform: `scale(${currentVisual.auraScale})` }}
350
  ></div>
351
 
352
  {/* Monk/Visual */}
353
  <div
354
+ className={`text-[150px] transition-all duration-1000 ease-in-out relative z-20 drop-shadow-2xl ${currentState === 'CHAOS' ? 'animate-bounce' : currentState === 'RESTLESS' ? 'animate-pulse' : ''}`}
355
+ style={{ transform: `translateY(${currentVisual.monkY}px) scale(${currentVisual.monkScale})` }}
356
  >
357
+ {currentVisual.monk}
358
  </div>
359
 
360
+ {/* Levitation Base (Shadow) */}
361
+ <div className={`w-40 h-10 bg-black/30 rounded-[100%] blur-md transition-all duration-1000 ${currentState === 'DEEP' ? 'scale-50 opacity-40 translate-y-10' : 'scale-100 opacity-80'}`}></div>
362
  </div>
363
 
364
  {/* Config Modal */}
 
380
  <span>阈值设定: {threshold}</span>
381
  </div>
382
  <div className="w-full h-6 bg-gray-800 rounded-full overflow-hidden relative border border-gray-600">
383
+ {/* Threshold Marker */}
384
  <div className="absolute top-0 bottom-0 w-0.5 bg-yellow-500 z-10 shadow-[0_0_5px_yellow]" style={{left: `${threshold}%`}}></div>
385
+
386
+ {/* Current Vol Bar */}
387
  <div
388
+ className={`h-full transition-all duration-75 ${
389
+ currentVolume < threshold * 0.5 ? 'bg-teal-500' :
390
+ currentVolume < threshold ? 'bg-emerald-500' :
391
+ currentVolume < threshold * 1.5 ? 'bg-orange-500' : 'bg-red-600'
392
+ }`}
393
  style={{width: `${Math.min(currentVolume, 100)}%`}}
394
  ></div>
395
  </div>
396
+ <div className="mt-2 flex justify-between text-[10px] text-gray-500 px-1">
397
+ <span>静</span>
398
+ <span>↑ 阈值</span>
399
+ <span>闹</span>
400
+ </div>
401
+ <div className="mt-1">
402
  <input type="range" min="1" max="100" className="w-full h-2 bg-slate-700 rounded-lg appearance-none cursor-pointer" value={threshold} onChange={e=>setThreshold(Number(e.target.value))}/>
403
  </div>
404
  </div>
405
  <button
406
+ onClick={() => { startAudio(); setThreshold(currentVolume + 10); }}
407
  className="px-4 py-2 bg-slate-700 hover:bg-slate-600 rounded-lg text-xs font-bold whitespace-nowrap border border-slate-600"
408
  >
409
  <Volume2 size={14} className="inline mr-1"/> 自动设定
410
  </button>
411
  </div>
412
+ <p className="text-[10px] text-gray-500 mt-2">* 绿色区域为安静(计分)橙色/红色区域为喧哗(不计分)。请在安静环境下点击自动设。</p>
413
  </div>
414
 
415
  <div>