feat: implement QR code scan login system with admin control
- Add scan login service with Redis-based token management - Add scan login API routes for token generation and validation - Add QRCodeLogin component for PC-side QR code display - Add EntryQRCode component for mass login scenarios - Add ScanLoginView for mobile-side login form - Add localStorage persistence for user identity - Add logout functionality to mobile client - Add auto-redirect for logged-in users - Add admin console control for QR code display on big screen - Add socket event forwarding from admin to screen display Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -443,16 +443,10 @@ export class LotteryMachine {
|
||||
p.text.y = centerY + y1 * scale;
|
||||
p.text.scale.set(scale * 0.8);
|
||||
|
||||
// Depth-based alpha and color
|
||||
// Depth-based alpha - all participants show gold color
|
||||
const depthAlpha = (z2 + this.sphereRadius) / (this.sphereRadius * 2);
|
||||
p.text.alpha = p.isEligible ? 0.3 + depthAlpha * 0.7 : 0.15;
|
||||
|
||||
// Dim ineligible names
|
||||
if (!p.isEligible) {
|
||||
p.text.style.fill = 0x666666;
|
||||
} else {
|
||||
p.text.style.fill = COLORS.gold;
|
||||
}
|
||||
p.text.alpha = 0.3 + depthAlpha * 0.7;
|
||||
p.text.style.fill = COLORS.gold;
|
||||
|
||||
// Z-sorting
|
||||
p.text.zIndex = Math.floor(z2);
|
||||
@@ -462,46 +456,65 @@ export class LotteryMachine {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Storm Phase (Tornado)
|
||||
// Storm Phase (Fast Spinning Sphere)
|
||||
// ============================================================================
|
||||
|
||||
private updateStorm(deltaMs: number): void {
|
||||
const centerX = this.app.screen.width / 2;
|
||||
const centerY = this.app.screen.height / 2;
|
||||
|
||||
// Ramp up storm intensity
|
||||
this.stormIntensity = Math.min(1, this.stormIntensity + deltaMs * 0.001);
|
||||
this.stormAngle += deltaMs * 0.01 * this.stormIntensity;
|
||||
// Ramp up rotation speed
|
||||
this.stormIntensity = Math.min(1, this.stormIntensity + deltaMs * 0.002);
|
||||
|
||||
// Apply motion blur based on intensity
|
||||
this.blurFilter.strength = this.stormIntensity * 8;
|
||||
// Fast sphere rotation - much faster than galaxy phase
|
||||
this.sphereRotationY += deltaMs * 0.008 * (1 + this.stormIntensity * 3);
|
||||
this.sphereRotationX += deltaMs * 0.003 * this.stormIntensity;
|
||||
|
||||
// Light motion blur for speed effect
|
||||
this.blurFilter.strength = this.stormIntensity * 2;
|
||||
|
||||
this.nameParticles.forEach((p, index) => {
|
||||
if (!p.text) return;
|
||||
|
||||
// Tornado vortex motion
|
||||
const baseAngle = this.stormAngle + (index / this.nameParticles.length) * Math.PI * 2;
|
||||
const verticalPos = ((this.time * 0.001 + index * 0.1) % 2) - 1; // -1 to 1
|
||||
const radius = 100 + Math.abs(verticalPos) * 200 * this.stormIntensity;
|
||||
// Get original sphere position from fibonacci distribution
|
||||
const phi = Math.acos(1 - 2 * (index + 0.5) / this.nameParticles.length);
|
||||
const theta = Math.PI * (1 + Math.sqrt(5)) * index;
|
||||
|
||||
const targetX = centerX + Math.cos(baseAngle) * radius;
|
||||
const targetY = centerY + verticalPos * 300;
|
||||
const sphereX = this.sphereRadius * Math.sin(phi) * Math.cos(theta);
|
||||
const sphereY = this.sphereRadius * Math.sin(phi) * Math.sin(theta);
|
||||
const sphereZ = this.sphereRadius * Math.cos(phi);
|
||||
|
||||
// Smooth interpolation
|
||||
p.x += (targetX - p.x) * 0.1;
|
||||
p.y += (targetY - p.y) * 0.1;
|
||||
// Apply 3D rotation (Y axis then X axis)
|
||||
const cosY = Math.cos(this.sphereRotationY);
|
||||
const sinY = Math.sin(this.sphereRotationY);
|
||||
const cosX = Math.cos(this.sphereRotationX);
|
||||
const sinX = Math.sin(this.sphereRotationX);
|
||||
|
||||
p.text.x = p.x;
|
||||
p.text.y = p.y;
|
||||
p.text.rotation += p.rotationSpeed * this.stormIntensity * 3;
|
||||
p.text.alpha = p.isEligible ? 0.8 : 0.2;
|
||||
// Rotate around Y axis
|
||||
const x1 = sphereX * cosY - sphereZ * sinY;
|
||||
const z1 = sphereX * sinY + sphereZ * cosY;
|
||||
|
||||
// Scale based on position
|
||||
const distFromCenter = Math.sqrt(
|
||||
Math.pow(p.x - centerX, 2) + Math.pow(p.y - centerY, 2)
|
||||
);
|
||||
p.text.scale.set(0.5 + (1 - distFromCenter / 400) * 0.5);
|
||||
// Rotate around X axis
|
||||
const y1 = sphereY * cosX - z1 * sinX;
|
||||
const z2 = sphereY * sinX + z1 * cosX;
|
||||
|
||||
// Project to 2D with perspective
|
||||
const perspective = 800;
|
||||
const scale = perspective / (perspective + z2);
|
||||
|
||||
p.text.x = centerX + x1 * scale;
|
||||
p.text.y = centerY + y1 * scale;
|
||||
p.text.scale.set(scale * 0.9);
|
||||
|
||||
// Depth-based alpha - all participants show same
|
||||
const depthAlpha = (z2 + this.sphereRadius) / (this.sphereRadius * 2);
|
||||
p.text.alpha = 0.4 + depthAlpha * 0.6;
|
||||
|
||||
// Z-sorting
|
||||
p.text.zIndex = Math.floor(z2);
|
||||
});
|
||||
|
||||
this.galaxyContainer.sortChildren();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user