@livepeer-frameworks/player-svelte 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DevModePanel.svelte +650 -0
- package/dist/DevModePanel.svelte.d.ts +31 -0
- package/dist/DvdLogo.svelte +213 -0
- package/dist/DvdLogo.svelte.d.ts +7 -0
- package/dist/Icons.svelte +27 -0
- package/dist/Icons.svelte.d.ts +25 -0
- package/dist/IdleScreen.svelte +752 -0
- package/dist/IdleScreen.svelte.d.ts +11 -0
- package/dist/LoadingScreen.svelte +689 -0
- package/dist/LoadingScreen.svelte.d.ts +7 -0
- package/dist/Player.svelte +482 -0
- package/dist/Player.svelte.d.ts +26 -0
- package/dist/PlayerControls.svelte +739 -0
- package/dist/PlayerControls.svelte.d.ts +20 -0
- package/dist/SeekBar.svelte +274 -0
- package/dist/SeekBar.svelte.d.ts +25 -0
- package/dist/SkipIndicator.svelte +95 -0
- package/dist/SkipIndicator.svelte.d.ts +14 -0
- package/dist/SpeedIndicator.svelte +38 -0
- package/dist/SpeedIndicator.svelte.d.ts +8 -0
- package/dist/StatsPanel.svelte +155 -0
- package/dist/StatsPanel.svelte.d.ts +27 -0
- package/dist/StreamStateOverlay.svelte +266 -0
- package/dist/StreamStateOverlay.svelte.d.ts +18 -0
- package/dist/SubtitleRenderer.svelte +234 -0
- package/dist/SubtitleRenderer.svelte.d.ts +41 -0
- package/dist/ThumbnailOverlay.svelte +96 -0
- package/dist/ThumbnailOverlay.svelte.d.ts +11 -0
- package/dist/TitleOverlay.svelte +47 -0
- package/dist/TitleOverlay.svelte.d.ts +9 -0
- package/dist/assets/logomark.svg +56 -0
- package/dist/components/VolumeIcons.svelte +53 -0
- package/dist/components/VolumeIcons.svelte.d.ts +10 -0
- package/dist/global.d.ts +15 -0
- package/dist/icons/FullscreenExitIcon.svelte +33 -0
- package/dist/icons/FullscreenExitIcon.svelte.d.ts +8 -0
- package/dist/icons/FullscreenIcon.svelte +33 -0
- package/dist/icons/FullscreenIcon.svelte.d.ts +8 -0
- package/dist/icons/PauseIcon.svelte +28 -0
- package/dist/icons/PauseIcon.svelte.d.ts +8 -0
- package/dist/icons/PictureInPictureIcon.svelte +28 -0
- package/dist/icons/PictureInPictureIcon.svelte.d.ts +8 -0
- package/dist/icons/PlayIcon.svelte +27 -0
- package/dist/icons/PlayIcon.svelte.d.ts +8 -0
- package/dist/icons/SeekToLiveIcon.svelte +30 -0
- package/dist/icons/SeekToLiveIcon.svelte.d.ts +8 -0
- package/dist/icons/SettingsIcon.svelte +40 -0
- package/dist/icons/SettingsIcon.svelte.d.ts +8 -0
- package/dist/icons/SkipBackIcon.svelte +32 -0
- package/dist/icons/SkipBackIcon.svelte.d.ts +8 -0
- package/dist/icons/SkipForwardIcon.svelte +32 -0
- package/dist/icons/SkipForwardIcon.svelte.d.ts +8 -0
- package/dist/icons/StatsIcon.svelte +29 -0
- package/dist/icons/StatsIcon.svelte.d.ts +8 -0
- package/dist/icons/VolumeOffIcon.svelte +29 -0
- package/dist/icons/VolumeOffIcon.svelte.d.ts +8 -0
- package/dist/icons/VolumeUpIcon.svelte +34 -0
- package/dist/icons/VolumeUpIcon.svelte.d.ts +8 -0
- package/dist/icons/index.d.ts +17 -0
- package/dist/icons/index.js +17 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +54 -0
- package/dist/player.css +2 -0
- package/dist/stores/index.d.ts +15 -0
- package/dist/stores/index.js +21 -0
- package/dist/stores/playbackQuality.d.ts +43 -0
- package/dist/stores/playbackQuality.js +107 -0
- package/dist/stores/playerContext.d.ts +73 -0
- package/dist/stores/playerContext.js +166 -0
- package/dist/stores/playerController.d.ts +178 -0
- package/dist/stores/playerController.js +358 -0
- package/dist/stores/playerSelection.d.ts +84 -0
- package/dist/stores/playerSelection.js +159 -0
- package/dist/stores/streamState.d.ts +44 -0
- package/dist/stores/streamState.js +314 -0
- package/dist/stores/viewerEndpoints.d.ts +48 -0
- package/dist/stores/viewerEndpoints.js +178 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +4 -0
- package/dist/ui/Badge.svelte +21 -0
- package/dist/ui/Badge.svelte.d.ts +32 -0
- package/dist/ui/Button.svelte +42 -0
- package/dist/ui/Button.svelte.d.ts +35 -0
- package/dist/ui/Slider.svelte +100 -0
- package/dist/ui/Slider.svelte.d.ts +17 -0
- package/dist/ui/badge.d.ts +6 -0
- package/dist/ui/badge.js +10 -0
- package/dist/ui/button.d.ts +8 -0
- package/dist/ui/button.js +21 -0
- package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte +34 -0
- package/dist/ui/context-menu/ContextMenuCheckboxItem.svelte.d.ts +31 -0
- package/dist/ui/context-menu/ContextMenuContent.svelte +17 -0
- package/dist/ui/context-menu/ContextMenuContent.svelte.d.ts +7 -0
- package/dist/ui/context-menu/ContextMenuItem.svelte +22 -0
- package/dist/ui/context-menu/ContextMenuItem.svelte.d.ts +8 -0
- package/dist/ui/context-menu/ContextMenuLabel.svelte +22 -0
- package/dist/ui/context-menu/ContextMenuLabel.svelte.d.ts +8 -0
- package/dist/ui/context-menu/ContextMenuPortal.svelte +11 -0
- package/dist/ui/context-menu/ContextMenuPortal.svelte.d.ts +6 -0
- package/dist/ui/context-menu/ContextMenuRadioItem.svelte +21 -0
- package/dist/ui/context-menu/ContextMenuRadioItem.svelte.d.ts +31 -0
- package/dist/ui/context-menu/ContextMenuSeparator.svelte +14 -0
- package/dist/ui/context-menu/ContextMenuSeparator.svelte.d.ts +6 -0
- package/dist/ui/context-menu/ContextMenuShortcut.svelte +19 -0
- package/dist/ui/context-menu/ContextMenuShortcut.svelte.d.ts +7 -0
- package/dist/ui/context-menu/ContextMenuSubContent.svelte +20 -0
- package/dist/ui/context-menu/ContextMenuSubContent.svelte.d.ts +7 -0
- package/dist/ui/context-menu/ContextMenuSubTrigger.svelte +34 -0
- package/dist/ui/context-menu/ContextMenuSubTrigger.svelte.d.ts +8 -0
- package/dist/ui/context-menu/index.d.ts +17 -0
- package/dist/ui/context-menu/index.js +17 -0
- package/package.json +51 -0
- package/src/DevModePanel.svelte +650 -0
- package/src/DvdLogo.svelte +213 -0
- package/src/Icons.svelte +27 -0
- package/src/IdleScreen.svelte +739 -0
- package/src/LoadingScreen.svelte +674 -0
- package/src/Player.svelte +483 -0
- package/src/PlayerControls.svelte +752 -0
- package/src/SeekBar.svelte +274 -0
- package/src/SkipIndicator.svelte +95 -0
- package/src/SpeedIndicator.svelte +37 -0
- package/src/StatsPanel.svelte +155 -0
- package/src/StreamStateOverlay.svelte +266 -0
- package/src/SubtitleRenderer.svelte +234 -0
- package/src/ThumbnailOverlay.svelte +96 -0
- package/src/TitleOverlay.svelte +47 -0
- package/src/assets/logomark.svg +56 -0
- package/src/components/VolumeIcons.svelte +53 -0
- package/src/global.d.ts +15 -0
- package/src/icons/FullscreenExitIcon.svelte +33 -0
- package/src/icons/FullscreenIcon.svelte +33 -0
- package/src/icons/PauseIcon.svelte +28 -0
- package/src/icons/PictureInPictureIcon.svelte +28 -0
- package/src/icons/PlayIcon.svelte +27 -0
- package/src/icons/SeekToLiveIcon.svelte +30 -0
- package/src/icons/SettingsIcon.svelte +40 -0
- package/src/icons/SkipBackIcon.svelte +32 -0
- package/src/icons/SkipForwardIcon.svelte +32 -0
- package/src/icons/StatsIcon.svelte +29 -0
- package/src/icons/VolumeOffIcon.svelte +29 -0
- package/src/icons/VolumeUpIcon.svelte +34 -0
- package/src/icons/index.ts +18 -0
- package/src/index.ts +84 -0
- package/src/player.css +2 -0
- package/src/stores/index.ts +88 -0
- package/src/stores/playbackQuality.ts +137 -0
- package/src/stores/playerContext.ts +221 -0
- package/src/stores/playerController.ts +568 -0
- package/src/stores/playerSelection.ts +216 -0
- package/src/stores/streamState.ts +367 -0
- package/src/stores/viewerEndpoints.ts +224 -0
- package/src/types.ts +6 -0
- package/src/ui/Badge.svelte +21 -0
- package/src/ui/Button.svelte +42 -0
- package/src/ui/Slider.svelte +100 -0
- package/src/ui/badge.ts +20 -0
- package/src/ui/button.ts +35 -0
- package/src/ui/context-menu/ContextMenuCheckboxItem.svelte +34 -0
- package/src/ui/context-menu/ContextMenuContent.svelte +17 -0
- package/src/ui/context-menu/ContextMenuItem.svelte +22 -0
- package/src/ui/context-menu/ContextMenuLabel.svelte +22 -0
- package/src/ui/context-menu/ContextMenuPortal.svelte +11 -0
- package/src/ui/context-menu/ContextMenuRadioItem.svelte +21 -0
- package/src/ui/context-menu/ContextMenuSeparator.svelte +14 -0
- package/src/ui/context-menu/ContextMenuShortcut.svelte +19 -0
- package/src/ui/context-menu/ContextMenuSubContent.svelte +20 -0
- package/src/ui/context-menu/ContextMenuSubTrigger.svelte +34 -0
- package/src/ui/context-menu/index.ts +36 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
LoadingScreen.svelte - Animated loading screen with Tokyo Night theme
|
|
3
|
+
Port of src/components/LoadingScreen.tsx
|
|
4
|
+
|
|
5
|
+
Features:
|
|
6
|
+
- Animated colored bubbles with Tokyo Night palette
|
|
7
|
+
- Center logo with mouse-tracking "push away" effect
|
|
8
|
+
- Bouncing DVD logo component
|
|
9
|
+
- Hitmarker sound effects (Web Audio API synthesis)
|
|
10
|
+
- Floating particles with gradients
|
|
11
|
+
- Pulsing circle around logo
|
|
12
|
+
- Animated background gradient shifts
|
|
13
|
+
-->
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import { onMount, onDestroy } from 'svelte';
|
|
16
|
+
import DvdLogo from './DvdLogo.svelte';
|
|
17
|
+
import logomarkAsset from './assets/logomark.svg';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
message?: string;
|
|
21
|
+
logoSrc?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let { message = 'Waiting for source...', logoSrc }: Props = $props();
|
|
25
|
+
|
|
26
|
+
// Use imported asset as default if logoSrc not provided
|
|
27
|
+
let effectiveLogoSrc = $derived(logoSrc || logomarkAsset);
|
|
28
|
+
|
|
29
|
+
// Container ref for mouse tracking
|
|
30
|
+
let containerRef: HTMLDivElement | undefined = $state();
|
|
31
|
+
|
|
32
|
+
// Hitmarker state
|
|
33
|
+
interface Hitmarker {
|
|
34
|
+
id: number;
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
}
|
|
38
|
+
let hitmarkers = $state<Hitmarker[]>([]);
|
|
39
|
+
|
|
40
|
+
// Center logo state
|
|
41
|
+
let logoSize = $state(100);
|
|
42
|
+
let offset = $state({ x: 0, y: 0 });
|
|
43
|
+
let isHovered = $state(false);
|
|
44
|
+
|
|
45
|
+
// Tokyo Night inspired pastel colors for bubbles
|
|
46
|
+
const bubbleColors = [
|
|
47
|
+
'rgba(122, 162, 247, 0.2)', // Terminal Blue
|
|
48
|
+
'rgba(187, 154, 247, 0.2)', // Terminal Magenta
|
|
49
|
+
'rgba(158, 206, 106, 0.2)', // Strings/CSS classes
|
|
50
|
+
'rgba(115, 218, 202, 0.2)', // Terminal Green
|
|
51
|
+
'rgba(125, 207, 255, 0.2)', // Terminal Cyan
|
|
52
|
+
'rgba(247, 118, 142, 0.2)', // Keywords/Terminal Red
|
|
53
|
+
'rgba(224, 175, 104, 0.2)', // Terminal Yellow
|
|
54
|
+
'rgba(42, 195, 222, 0.2)', // Language functions
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// Particle colors
|
|
58
|
+
const particleColors = [
|
|
59
|
+
'#7aa2f7', // Terminal Blue
|
|
60
|
+
'#bb9af7', // Terminal Magenta
|
|
61
|
+
'#9ece6a', // Strings/CSS classes
|
|
62
|
+
'#73daca', // Terminal Green
|
|
63
|
+
'#7dcfff', // Terminal Cyan
|
|
64
|
+
'#f7768e', // Keywords/Terminal Red
|
|
65
|
+
'#e0af68', // Terminal Yellow
|
|
66
|
+
'#2ac3de', // Language functions
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Generate random particles
|
|
70
|
+
const particles = Array.from({ length: 12 }, (_, i) => ({
|
|
71
|
+
left: Math.random() * 100,
|
|
72
|
+
size: Math.random() * 4 + 2,
|
|
73
|
+
color: particleColors[i % 8],
|
|
74
|
+
duration: 8 + Math.random() * 4,
|
|
75
|
+
delay: Math.random() * 8,
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
// Animated bubble component state
|
|
79
|
+
interface BubbleState {
|
|
80
|
+
position: { top: number; left: number };
|
|
81
|
+
size: number;
|
|
82
|
+
opacity: number;
|
|
83
|
+
color: string;
|
|
84
|
+
timeoutId: ReturnType<typeof setTimeout> | null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let bubbles = $state<BubbleState[]>(
|
|
88
|
+
Array.from({ length: 8 }, (_, i) => ({
|
|
89
|
+
position: { top: Math.random() * 80 + 10, left: Math.random() * 80 + 10 },
|
|
90
|
+
size: Math.random() * 60 + 30,
|
|
91
|
+
opacity: 0,
|
|
92
|
+
color: bubbleColors[i % bubbleColors.length],
|
|
93
|
+
timeoutId: null,
|
|
94
|
+
}))
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Bubble animation cycle
|
|
98
|
+
function animateBubble(index: number) {
|
|
99
|
+
// Fade in
|
|
100
|
+
bubbles[index].opacity = 0.15;
|
|
101
|
+
|
|
102
|
+
const visibleDuration = 4000 + Math.random() * 3000; // 4-7 seconds visible
|
|
103
|
+
|
|
104
|
+
const timeout1 = setTimeout(() => {
|
|
105
|
+
// Fade out
|
|
106
|
+
bubbles[index].opacity = 0;
|
|
107
|
+
|
|
108
|
+
const timeout2 = setTimeout(() => {
|
|
109
|
+
// Move to new random position while invisible
|
|
110
|
+
bubbles[index].position = {
|
|
111
|
+
top: Math.random() * 80 + 10,
|
|
112
|
+
left: Math.random() * 80 + 10,
|
|
113
|
+
};
|
|
114
|
+
bubbles[index].size = Math.random() * 60 + 30;
|
|
115
|
+
|
|
116
|
+
const timeout3 = setTimeout(() => {
|
|
117
|
+
animateBubble(index);
|
|
118
|
+
}, 200);
|
|
119
|
+
bubbles[index].timeoutId = timeout3;
|
|
120
|
+
}, 1500); // Wait for fade out
|
|
121
|
+
bubbles[index].timeoutId = timeout2;
|
|
122
|
+
}, visibleDuration);
|
|
123
|
+
bubbles[index].timeoutId = timeout1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Hitmarker sound synthesis
|
|
127
|
+
function createSyntheticHitmarkerSound() {
|
|
128
|
+
try {
|
|
129
|
+
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
|
|
130
|
+
|
|
131
|
+
const oscillator1 = audioContext.createOscillator();
|
|
132
|
+
const oscillator2 = audioContext.createOscillator();
|
|
133
|
+
const noiseBuffer = audioContext.createBuffer(1, audioContext.sampleRate * 0.1, audioContext.sampleRate);
|
|
134
|
+
const noiseSource = audioContext.createBufferSource();
|
|
135
|
+
|
|
136
|
+
// Generate white noise for the initial "crack"
|
|
137
|
+
const noiseData = noiseBuffer.getChannelData(0);
|
|
138
|
+
for (let i = 0; i < noiseData.length; i++) {
|
|
139
|
+
noiseData[i] = Math.random() * 2 - 1;
|
|
140
|
+
}
|
|
141
|
+
noiseSource.buffer = noiseBuffer;
|
|
142
|
+
|
|
143
|
+
const gainNode1 = audioContext.createGain();
|
|
144
|
+
const gainNode2 = audioContext.createGain();
|
|
145
|
+
const noiseGain = audioContext.createGain();
|
|
146
|
+
const masterGain = audioContext.createGain();
|
|
147
|
+
|
|
148
|
+
oscillator1.connect(gainNode1);
|
|
149
|
+
oscillator2.connect(gainNode2);
|
|
150
|
+
noiseSource.connect(noiseGain);
|
|
151
|
+
|
|
152
|
+
gainNode1.connect(masterGain);
|
|
153
|
+
gainNode2.connect(masterGain);
|
|
154
|
+
noiseGain.connect(masterGain);
|
|
155
|
+
masterGain.connect(audioContext.destination);
|
|
156
|
+
|
|
157
|
+
// Sharp metallic frequencies
|
|
158
|
+
oscillator1.frequency.setValueAtTime(1800, audioContext.currentTime);
|
|
159
|
+
oscillator1.frequency.exponentialRampToValueAtTime(900, audioContext.currentTime + 0.08);
|
|
160
|
+
|
|
161
|
+
oscillator2.frequency.setValueAtTime(3600, audioContext.currentTime);
|
|
162
|
+
oscillator2.frequency.exponentialRampToValueAtTime(1800, audioContext.currentTime + 0.04);
|
|
163
|
+
|
|
164
|
+
oscillator1.type = 'triangle';
|
|
165
|
+
oscillator2.type = 'sine';
|
|
166
|
+
|
|
167
|
+
// Sharp attack, quick decay
|
|
168
|
+
gainNode1.gain.setValueAtTime(0, audioContext.currentTime);
|
|
169
|
+
gainNode1.gain.linearRampToValueAtTime(0.4, audioContext.currentTime + 0.002);
|
|
170
|
+
gainNode1.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.12);
|
|
171
|
+
|
|
172
|
+
gainNode2.gain.setValueAtTime(0, audioContext.currentTime);
|
|
173
|
+
gainNode2.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.001);
|
|
174
|
+
gainNode2.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.06);
|
|
175
|
+
|
|
176
|
+
// Noise burst
|
|
177
|
+
noiseGain.gain.setValueAtTime(0, audioContext.currentTime);
|
|
178
|
+
noiseGain.gain.linearRampToValueAtTime(0.2, audioContext.currentTime + 0.001);
|
|
179
|
+
noiseGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.01);
|
|
180
|
+
|
|
181
|
+
masterGain.gain.setValueAtTime(0.5, audioContext.currentTime);
|
|
182
|
+
|
|
183
|
+
const startTime = audioContext.currentTime;
|
|
184
|
+
const stopTime = startTime + 0.15;
|
|
185
|
+
|
|
186
|
+
oscillator1.start(startTime);
|
|
187
|
+
oscillator2.start(startTime);
|
|
188
|
+
noiseSource.start(startTime);
|
|
189
|
+
|
|
190
|
+
oscillator1.stop(stopTime);
|
|
191
|
+
oscillator2.stop(stopTime);
|
|
192
|
+
noiseSource.stop(startTime + 0.02);
|
|
193
|
+
} catch {
|
|
194
|
+
// Audio context not available
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function playHitmarkerSound() {
|
|
199
|
+
try {
|
|
200
|
+
// Embedded hitmarker sound as base64 data URL (matches React implementation)
|
|
201
|
+
const hitmarkerDataUrl = 'data:audio/mpeg;base64,SUQzBAAAAAAANFRDT04AAAAHAAADT3RoZXIAVFNTRQAAAA8AAANMYXZmNTcuODMuMTAwAAAAAAAAAAAA' +
|
|
202
|
+
'AAD/+1QAAAAAAAAAAAAAAAAAAAAA' +
|
|
203
|
+
'AAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAAYAAAnAADs7Ozs7Ozs7Ozs7Ozs7OztiYmJiYmJiYmJi' +
|
|
204
|
+
'YmJiYmJiYomJiYmJiYmJiYmJiYmJiYmxsbGxsbGxsbGxsbGxsbGxsdjY2NjY2NjY2NjY2NjY2NjY////' +
|
|
205
|
+
'/////////////////wAAAABMYXZjNTcuMTAAAAAAAAAAAAAAAAAkAkAAAAAAAAAJwOuMZun/+5RkAA8S' +
|
|
206
|
+
'/F23AGAaAi0AF0AAAAAInXsEAIRXyQ8D4OQgjEhE3cO7ujuHF0XCOu4G7xKbi3Funu7u7p9dw7unu7u7' +
|
|
207
|
+
'p7u7u6fXcW7om7u7uiU3dxdT67u7p7uHdxelN3cW6fXcW7oXXd3eJTd3d0+u4t3iXdw4up70W4uiPruL' +
|
|
208
|
+
'DzMw8Pz79Y99JfkyfPv5/h9uTJoy79Y99Y97q3vyZPJk0ZfrL6x73Vn+J35dKKS/STQyQ8CAiCPNuRAO' +
|
|
209
|
+
'OqquAx+fzJeBKDAsgAMBuWcBsHKhjJTcCwIALyAvABbI0ZIcCmP8jHJe8gZAdVRp2TpnU/kUXV4iQuBA' +
|
|
210
|
+
'AkAQgisLPvwQ2Jz7wIkIpQ8QOl/KFy75w+2HpTFnRqXLQo0fzlSYRe5Ce9yZMEzRM4xesu95Mo8QQsoM' +
|
|
211
|
+
'H4gLg+fJqkmY3GZJE2kwGfMECJiAdIttoEa2yotfC7jsS2mjKgbzAfEMeiwZpGSUFCQwPKQiWXh0TnkN' +
|
|
212
|
+
'or5SmrKvwHlX2zFxKxPCzRL/+5RkIwADvUxLawwb0GdF6Y1hJlgNNJk+DSRwyQwI6AD2JCiBmhaff0dz' +
|
|
213
|
+
'CEBjgFABAcDNFc3YAEV4hQn0L/QvQnevom+n13eIjoTvABLrHg/L9RzdWXYonHbbbE2K0pX+gkL2g56R' +
|
|
214
|
+
'iwrbuWwhoABzQoMKOAIGAfE4UKk6BhSIJpECBq0CEYmZKYIiAJt72H24dNou7y/Ee7a/3v+MgySemSTY' +
|
|
215
|
+
'mnBAFwIAAGfCJ8/D9YfkwQEBcP38uA1d/EB1T5dZKEsgnuhwZirY5fIMRMdRn7U4OcN2m5NWeYdcPBwX' +
|
|
216
|
+
'DBOsJF1DBYks62pAURqz1hGoGHH/QIoRC80tYAJ8g4f3MPD51sywAbhAn/X9P/75tvZww3gZ3pYPDx/+' +
|
|
217
|
+
'ACO/7//ffHj/D/AAfATC4DYGFA3MRABo0lqWjBOl2yAda1C1BdhduXgm8FGnAQB/lDiEi6j9qw9EHigI' +
|
|
218
|
+
'IOLB6F1eIPd+T6Agc4//lMo6+k3tdttJY2gArU7cN07m2FLSm4gCjyz/+5RECwACwSRZawkdLFGi2mVh' +
|
|
219
|
+
'5h4LfFdPVPGACViTavaeMAAV0UkkEsDhxxJwqF04on002mZah8w9+5ItfSAoyZa1dchnPpLmAEKrVMRA' +
|
|
220
|
+
'//sD8w0WsB4xiw4JqaZMB45TdpIuXXUPf8Bpa35p/jQIAOAuZkmUeJoM5W6L2gqqO6rTuHjUTDnhy4Qi' +
|
|
221
|
+
'K348vtFysOizShoHbBpsPRYcSINCbiN4XOLPPAgq3dW2Ga7SlyiKXBV7W1RQl5BiiVGkwayJfEnPxgXk' +
|
|
222
|
+
'QeZxxzyhTuLO2XFUDDstoc6CkM1J8QZAjUN3bM8580cRygNfmPAELGjIH0Z/0A+8csyH/4eHvgAf8APg' +
|
|
223
|
+
'ABmZ98AARAADP////Dw8PHEmIpgGttpJQJsmZjq5nPQ8j5VqWW1evqdjP182PA6tHJZgkC5iSbEQkyJS' +
|
|
224
|
+
'z/BvP3eucLKN0+Wiza4feKKFBqiAEBAMXyYni5NZc16CDl/QY9j6BAcWSmQYcIcoMHYoQNBiIBgIBUAz' +
|
|
225
|
+
'QUMSnjj/+5RkCwADsFLffjEAAjrJe63JHACO6WtlnPMACKaCK1uMMADU5dI6JhW2cam98UlRmY4ihyKF' +
|
|
226
|
+
'rNsgpZd5PYgBALnYofKEt82De0GbW1DLibvFDK+bSeOm8qKdqUFZ7uiK8XMPHyqm3pTxUvcunUfxXEo9' +
|
|
227
|
+
'RNe5b/8vfCD3kzDN7vTtHyaIcntVDAYBAUBAAAAQBI2vguYNsHWm5AR3mZtZib8WAHFvz2Kf9//iYvlR' +
|
|
228
|
+
'B/+n///////////+UH7XoIDMoJAEAMtj8JshJPRwklVqNSpYnalfE+VzNCAISCoxVHEpIo/WrTiMvP7V' +
|
|
229
|
+
'TujOPnOglLbMLN/pq/d2Y4lRJIkSnPlUSJEjSKJqM41d88zWtMzP+fCOORmc9NeM+f1nnO//efM52/fG' +
|
|
230
|
+
'/ef385+5u+u1bRJkwU8FAkEItZpkRYeQYcAgZTEYlaZa2yROLeC0qdX73rZJJ/d2f6v6Or0u/+5FBYcn' +
|
|
231
|
+
'g0MlCiQTR9GUU5LScmSuSlH00IWqXA6jlw4BEcD/+5REEAAi3RtU+eYbGF1E+lk9g0YJzLUgh7BlQVGT' +
|
|
232
|
+
'ZJD0jKhhTNVilqrMzFRK+x/szcMKBWKep4NP1A0DR6RESkTp5Z1Q9Y8REgqMg1DpUBPleeqlRQcerBpM' +
|
|
233
|
+
'jiURHVD4XwAALhAgbxxlxYD5OFkG8oQRPB2EpsxSCNVlgcYUqoAyiVJmaARlkwplICfPoUy/zWEzM2pc' +
|
|
234
|
+
'NYzAQNJDSniEYecSEqxFEzQqEvUFGnvzwUfcRlpZ9T2LCR5QdDQDDhKICAjpJCagpRo9UQRPClZZlg6E' +
|
|
235
|
+
'p9DMTkTl+okuhRIVIzAQEf9L+Mx/DUjqmqN6kX7M36lS4zgLyJV3iV6j3xF8kJduJawVw1nndAlBaLLg' +
|
|
236
|
+
'JupwsTcLkxmJgFLgSzoCmHjSNGSqkGPCpnNqTXIwolf6qlVWN+q/su37HzgrES1pWGg3KnWh0FXCVniJ' +
|
|
237
|
+
'9K5b4iCrpLEuIcFTqwkVLFiqgaDqCCSMVWqxBAVCFOLVrVahm2ahUThUKJnmFCw15hD0Qhb/+5REEAhC' +
|
|
238
|
+
'YSRCSQEb4FOGaBUMI6JIRYC0QIB2SQsgGpgwDghgIlS6FU8VBXDoiBp5Y9gtkVnhEhYBdJFQ7kQ3w1yp' +
|
|
239
|
+
'0NB2CoNPEttZ1/aeDUAAA26FEghWgEKNVAVWkFAQEmMK2Uwk/qI0hqUb/4epVIZH1ai6szf6kzH1f2ar' +
|
|
240
|
+
'xYGS9FcOsN5UlJLQt///+oo0FRDTUQ0FBQr9f5LxXP+mEUfk0AIrf/5GRmQ0//mX//ZbLP5b5GrWSz+W' +
|
|
241
|
+
'SkZMrWyyyy2GRqyggVRyMv////////st//sn/yyVDI1l8mVgoYGDCOqiqIQBxmvxWCggTpZZZD//aWfy' +
|
|
242
|
+
'yWf/y/7KGDA0ssBggTof9k/+WS/8slQyMp/5Nfln8WAqGcUbULCrKxT9ISF+kKsxQWpMQU1FMy4xMDCq' +
|
|
243
|
+
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' +
|
|
244
|
+
'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=';
|
|
245
|
+
|
|
246
|
+
const audio = new Audio(hitmarkerDataUrl);
|
|
247
|
+
audio.volume = 0.3;
|
|
248
|
+
audio.play().catch(() => {
|
|
249
|
+
// Fallback to synthetic sound if data URL fails
|
|
250
|
+
createSyntheticHitmarkerSound();
|
|
251
|
+
});
|
|
252
|
+
} catch {
|
|
253
|
+
// Fallback to synthetic sound
|
|
254
|
+
createSyntheticHitmarkerSound();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function createHitmarker(e: { clientX: number; clientY: number }) {
|
|
259
|
+
if (!containerRef) return;
|
|
260
|
+
|
|
261
|
+
const rect = containerRef.getBoundingClientRect();
|
|
262
|
+
const x = e.clientX - rect.left;
|
|
263
|
+
const y = e.clientY - rect.top;
|
|
264
|
+
|
|
265
|
+
const newHitmarker: Hitmarker = {
|
|
266
|
+
id: Date.now() + Math.random(),
|
|
267
|
+
x,
|
|
268
|
+
y,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
hitmarkers = [...hitmarkers, newHitmarker];
|
|
272
|
+
playHitmarkerSound();
|
|
273
|
+
|
|
274
|
+
// Remove hitmarker after animation
|
|
275
|
+
setTimeout(() => {
|
|
276
|
+
hitmarkers = hitmarkers.filter(h => h.id !== newHitmarker.id);
|
|
277
|
+
}, 600);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Mouse tracking for logo push-away effect
|
|
281
|
+
function handleMouseMove(e: MouseEvent) {
|
|
282
|
+
if (!containerRef) return;
|
|
283
|
+
|
|
284
|
+
const rect = containerRef.getBoundingClientRect();
|
|
285
|
+
const centerX = rect.left + rect.width / 2;
|
|
286
|
+
const centerY = rect.top + rect.height / 2;
|
|
287
|
+
|
|
288
|
+
const mouseX = e.clientX;
|
|
289
|
+
const mouseY = e.clientY;
|
|
290
|
+
|
|
291
|
+
const deltaX = mouseX - centerX;
|
|
292
|
+
const deltaY = mouseY - centerY;
|
|
293
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
294
|
+
|
|
295
|
+
const maxDistance = logoSize * 1.5;
|
|
296
|
+
if (distance < maxDistance && distance > 0) {
|
|
297
|
+
const pushStrength = (maxDistance - distance) / maxDistance;
|
|
298
|
+
const pushDistance = 50 * pushStrength;
|
|
299
|
+
|
|
300
|
+
offset = {
|
|
301
|
+
x: -(deltaX / distance) * pushDistance,
|
|
302
|
+
y: -(deltaY / distance) * pushDistance,
|
|
303
|
+
};
|
|
304
|
+
isHovered = true;
|
|
305
|
+
} else {
|
|
306
|
+
offset = { x: 0, y: 0 };
|
|
307
|
+
isHovered = false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function handleMouseLeave() {
|
|
312
|
+
offset = { x: 0, y: 0 };
|
|
313
|
+
isHovered = false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function handleLogoClick(e: MouseEvent) {
|
|
317
|
+
e.stopPropagation();
|
|
318
|
+
createHitmarker({ clientX: e.clientX, clientY: e.clientY });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Update logo size on container resize
|
|
322
|
+
$effect(() => {
|
|
323
|
+
if (!containerRef) return;
|
|
324
|
+
|
|
325
|
+
const updateLogoSize = () => {
|
|
326
|
+
if (containerRef) {
|
|
327
|
+
const minDimension = Math.min(containerRef.clientWidth, containerRef.clientHeight);
|
|
328
|
+
logoSize = minDimension * 0.2;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
updateLogoSize();
|
|
333
|
+
|
|
334
|
+
const resizeObserver = new ResizeObserver(updateLogoSize);
|
|
335
|
+
resizeObserver.observe(containerRef);
|
|
336
|
+
|
|
337
|
+
return () => resizeObserver.disconnect();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Start bubble animations on mount
|
|
341
|
+
onMount(() => {
|
|
342
|
+
bubbles.forEach((_, index) => {
|
|
343
|
+
setTimeout(() => animateBubble(index), index * 500);
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Cleanup on destroy
|
|
348
|
+
onDestroy(() => {
|
|
349
|
+
bubbles.forEach(bubble => {
|
|
350
|
+
if (bubble.timeoutId) clearTimeout(bubble.timeoutId);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
</script>
|
|
354
|
+
|
|
355
|
+
<style>
|
|
356
|
+
@keyframes fadeInOut {
|
|
357
|
+
0%, 100% { opacity: 0.6; }
|
|
358
|
+
50% { opacity: 0.9; }
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
@keyframes logoPulse {
|
|
362
|
+
0%, 100% {
|
|
363
|
+
opacity: 0.15;
|
|
364
|
+
transform: scale(1);
|
|
365
|
+
}
|
|
366
|
+
50% {
|
|
367
|
+
opacity: 0.25;
|
|
368
|
+
transform: scale(1.05);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
@keyframes floatUp {
|
|
373
|
+
0% {
|
|
374
|
+
transform: translateY(100vh) rotate(0deg);
|
|
375
|
+
opacity: 0;
|
|
376
|
+
}
|
|
377
|
+
10% { opacity: 0.6; }
|
|
378
|
+
90% { opacity: 0.6; }
|
|
379
|
+
100% {
|
|
380
|
+
transform: translateY(-100px) rotate(360deg);
|
|
381
|
+
opacity: 0;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
@keyframes gradientShift {
|
|
386
|
+
0%, 100% { background-position: 0% 50%; }
|
|
387
|
+
50% { background-position: 100% 50%; }
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
@keyframes hitmarkerFade {
|
|
391
|
+
0% {
|
|
392
|
+
opacity: 1;
|
|
393
|
+
transform: scale(0.5);
|
|
394
|
+
}
|
|
395
|
+
20% {
|
|
396
|
+
opacity: 1;
|
|
397
|
+
transform: scale(1.2);
|
|
398
|
+
}
|
|
399
|
+
100% {
|
|
400
|
+
opacity: 0;
|
|
401
|
+
transform: scale(1);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@keyframes hitmarkerFade45 {
|
|
406
|
+
0% {
|
|
407
|
+
opacity: 1;
|
|
408
|
+
transform: translate(-50%, -50%) rotate(45deg) scale(0.5);
|
|
409
|
+
}
|
|
410
|
+
20% {
|
|
411
|
+
opacity: 1;
|
|
412
|
+
transform: translate(-50%, -50%) rotate(45deg) scale(1.2);
|
|
413
|
+
}
|
|
414
|
+
100% {
|
|
415
|
+
opacity: 0;
|
|
416
|
+
transform: translate(-50%, -50%) rotate(45deg) scale(1);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
@keyframes hitmarkerFadeNeg45 {
|
|
421
|
+
0% {
|
|
422
|
+
opacity: 1;
|
|
423
|
+
transform: translate(-50%, -50%) rotate(-45deg) scale(0.5);
|
|
424
|
+
}
|
|
425
|
+
20% {
|
|
426
|
+
opacity: 1;
|
|
427
|
+
transform: translate(-50%, -50%) rotate(-45deg) scale(1.2);
|
|
428
|
+
}
|
|
429
|
+
100% {
|
|
430
|
+
opacity: 0;
|
|
431
|
+
transform: translate(-50%, -50%) rotate(-45deg) scale(1);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.loading-container {
|
|
436
|
+
position: relative;
|
|
437
|
+
width: 100%;
|
|
438
|
+
height: 100%;
|
|
439
|
+
min-height: 300px;
|
|
440
|
+
background: linear-gradient(135deg,
|
|
441
|
+
hsl(var(--tn-bg-dark, 235 21% 11%)) 0%,
|
|
442
|
+
hsl(var(--tn-bg, 233 23% 17%)) 25%,
|
|
443
|
+
hsl(var(--tn-bg-dark, 235 21% 11%)) 50%,
|
|
444
|
+
hsl(var(--tn-bg, 233 23% 17%)) 75%,
|
|
445
|
+
hsl(var(--tn-bg-dark, 235 21% 11%)) 100%
|
|
446
|
+
);
|
|
447
|
+
background-size: 400% 400%;
|
|
448
|
+
animation: gradientShift 16s ease-in-out infinite;
|
|
449
|
+
display: flex;
|
|
450
|
+
flex-direction: column;
|
|
451
|
+
align-items: center;
|
|
452
|
+
justify-content: center;
|
|
453
|
+
overflow: hidden;
|
|
454
|
+
border-radius: 0;
|
|
455
|
+
user-select: none;
|
|
456
|
+
-webkit-user-select: none;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.bubble {
|
|
460
|
+
position: absolute;
|
|
461
|
+
border-radius: 50%;
|
|
462
|
+
transition: opacity 1s ease-in-out;
|
|
463
|
+
pointer-events: none;
|
|
464
|
+
user-select: none;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.particle {
|
|
468
|
+
position: absolute;
|
|
469
|
+
border-radius: 50%;
|
|
470
|
+
opacity: 0;
|
|
471
|
+
animation: floatUp linear infinite;
|
|
472
|
+
pointer-events: none;
|
|
473
|
+
user-select: none;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.center-logo {
|
|
477
|
+
position: absolute;
|
|
478
|
+
top: 50%;
|
|
479
|
+
left: 50%;
|
|
480
|
+
display: flex;
|
|
481
|
+
align-items: center;
|
|
482
|
+
justify-content: center;
|
|
483
|
+
z-index: 10;
|
|
484
|
+
transition: transform 0.3s ease-out;
|
|
485
|
+
user-select: none;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.logo-pulse {
|
|
489
|
+
position: absolute;
|
|
490
|
+
border-radius: 50%;
|
|
491
|
+
background: rgba(122, 162, 247, 0.15);
|
|
492
|
+
animation: logoPulse 3s ease-in-out infinite;
|
|
493
|
+
user-select: none;
|
|
494
|
+
pointer-events: none;
|
|
495
|
+
transition: transform 0.3s ease-out;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.logo-pulse.hovered {
|
|
499
|
+
animation: logoPulse 1s ease-in-out infinite;
|
|
500
|
+
transform: scale(1.2);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.logo-image {
|
|
504
|
+
position: relative;
|
|
505
|
+
z-index: 1;
|
|
506
|
+
filter: drop-shadow(0 4px 8px rgba(36, 40, 59, 0.3));
|
|
507
|
+
transition: all 0.3s ease-out;
|
|
508
|
+
user-select: none;
|
|
509
|
+
-webkit-user-drag: none;
|
|
510
|
+
-webkit-touch-callout: none;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.logo-image.hovered {
|
|
514
|
+
filter: drop-shadow(0 6px 12px rgba(36, 40, 59, 0.4)) brightness(1.1);
|
|
515
|
+
transform: scale(1.1);
|
|
516
|
+
cursor: pointer;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.message {
|
|
520
|
+
position: absolute;
|
|
521
|
+
bottom: 20%;
|
|
522
|
+
left: 50%;
|
|
523
|
+
transform: translateX(-50%);
|
|
524
|
+
color: #a9b1d6;
|
|
525
|
+
font-size: 16px;
|
|
526
|
+
font-weight: 500;
|
|
527
|
+
text-align: center;
|
|
528
|
+
animation: fadeInOut 2s ease-in-out infinite;
|
|
529
|
+
text-shadow: 0 2px 4px rgba(36, 40, 59, 0.5);
|
|
530
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
531
|
+
user-select: none;
|
|
532
|
+
pointer-events: none;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.overlay-texture {
|
|
536
|
+
position: absolute;
|
|
537
|
+
top: 0;
|
|
538
|
+
left: 0;
|
|
539
|
+
right: 0;
|
|
540
|
+
bottom: 0;
|
|
541
|
+
background:
|
|
542
|
+
radial-gradient(circle at 20% 80%, rgba(122, 162, 247, 0.03) 0%, transparent 50%),
|
|
543
|
+
radial-gradient(circle at 80% 20%, rgba(187, 154, 247, 0.03) 0%, transparent 50%),
|
|
544
|
+
radial-gradient(circle at 40% 40%, rgba(158, 206, 106, 0.02) 0%, transparent 50%);
|
|
545
|
+
pointer-events: none;
|
|
546
|
+
user-select: none;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.hitmarker {
|
|
550
|
+
position: absolute;
|
|
551
|
+
transform: translate(-50%, -50%);
|
|
552
|
+
pointer-events: none;
|
|
553
|
+
z-index: 100;
|
|
554
|
+
width: 40px;
|
|
555
|
+
height: 40px;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.hitmarker-line {
|
|
559
|
+
position: absolute;
|
|
560
|
+
width: 12px;
|
|
561
|
+
height: 3px;
|
|
562
|
+
background-color: #ffffff;
|
|
563
|
+
box-shadow: 0 0 8px rgba(255, 255, 255, 0.8);
|
|
564
|
+
border-radius: 1px;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.hitmarker-line.tl {
|
|
568
|
+
top: 25%;
|
|
569
|
+
left: 25%;
|
|
570
|
+
animation: hitmarkerFade45 0.6s ease-out forwards;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.hitmarker-line.tr {
|
|
574
|
+
top: 25%;
|
|
575
|
+
left: 75%;
|
|
576
|
+
animation: hitmarkerFadeNeg45 0.6s ease-out forwards;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.hitmarker-line.bl {
|
|
580
|
+
top: 75%;
|
|
581
|
+
left: 25%;
|
|
582
|
+
animation: hitmarkerFadeNeg45 0.6s ease-out forwards;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.hitmarker-line.br {
|
|
586
|
+
top: 75%;
|
|
587
|
+
left: 75%;
|
|
588
|
+
animation: hitmarkerFade45 0.6s ease-out forwards;
|
|
589
|
+
}
|
|
590
|
+
</style>
|
|
591
|
+
|
|
592
|
+
<div
|
|
593
|
+
bind:this={containerRef}
|
|
594
|
+
class="loading-container fw-player-root"
|
|
595
|
+
role="status"
|
|
596
|
+
aria-label="Loading"
|
|
597
|
+
onmousemove={handleMouseMove}
|
|
598
|
+
onmouseleave={handleMouseLeave}
|
|
599
|
+
>
|
|
600
|
+
<!-- Hitmarkers -->
|
|
601
|
+
{#each hitmarkers as hitmarker (hitmarker.id)}
|
|
602
|
+
<div class="hitmarker" style="left: {hitmarker.x}px; top: {hitmarker.y}px;">
|
|
603
|
+
<div class="hitmarker-line tl"></div>
|
|
604
|
+
<div class="hitmarker-line tr"></div>
|
|
605
|
+
<div class="hitmarker-line bl"></div>
|
|
606
|
+
<div class="hitmarker-line br"></div>
|
|
607
|
+
</div>
|
|
608
|
+
{/each}
|
|
609
|
+
|
|
610
|
+
<!-- Floating particles -->
|
|
611
|
+
{#each particles as particle, i}
|
|
612
|
+
<div
|
|
613
|
+
class="particle"
|
|
614
|
+
style="
|
|
615
|
+
left: {particle.left}%;
|
|
616
|
+
width: {particle.size}px;
|
|
617
|
+
height: {particle.size}px;
|
|
618
|
+
background: {particle.color};
|
|
619
|
+
animation-duration: {particle.duration}s;
|
|
620
|
+
animation-delay: {particle.delay}s;
|
|
621
|
+
"
|
|
622
|
+
/>
|
|
623
|
+
{/each}
|
|
624
|
+
|
|
625
|
+
<!-- Animated bubbles -->
|
|
626
|
+
{#each bubbles as bubble, i}
|
|
627
|
+
<div
|
|
628
|
+
class="bubble"
|
|
629
|
+
style="
|
|
630
|
+
top: {bubble.position.top}%;
|
|
631
|
+
left: {bubble.position.left}%;
|
|
632
|
+
width: {bubble.size}px;
|
|
633
|
+
height: {bubble.size}px;
|
|
634
|
+
background: {bubble.color};
|
|
635
|
+
opacity: {bubble.opacity};
|
|
636
|
+
"
|
|
637
|
+
/>
|
|
638
|
+
{/each}
|
|
639
|
+
|
|
640
|
+
<!-- Center logo with push-away effect -->
|
|
641
|
+
<div
|
|
642
|
+
class="center-logo"
|
|
643
|
+
style="transform: translate(-50%, -50%) translate({offset.x}px, {offset.y}px);"
|
|
644
|
+
>
|
|
645
|
+
<!-- Pulsing circle background -->
|
|
646
|
+
<div
|
|
647
|
+
class="logo-pulse"
|
|
648
|
+
class:hovered={isHovered}
|
|
649
|
+
style="width: {logoSize * 1.4}px; height: {logoSize * 1.4}px;"
|
|
650
|
+
/>
|
|
651
|
+
|
|
652
|
+
<!-- Logo image -->
|
|
653
|
+
<img
|
|
654
|
+
src={effectiveLogoSrc}
|
|
655
|
+
alt="Logo"
|
|
656
|
+
class="logo-image"
|
|
657
|
+
class:hovered={isHovered}
|
|
658
|
+
style="width: {logoSize}px; height: {logoSize}px;"
|
|
659
|
+
onclick={handleLogoClick}
|
|
660
|
+
draggable="false"
|
|
661
|
+
/>
|
|
662
|
+
</div>
|
|
663
|
+
|
|
664
|
+
<!-- Bouncing DVD Logo -->
|
|
665
|
+
<DvdLogo parentRef={containerRef} scale={0.08} />
|
|
666
|
+
|
|
667
|
+
<!-- Message -->
|
|
668
|
+
<div class="message">
|
|
669
|
+
{message}
|
|
670
|
+
</div>
|
|
671
|
+
|
|
672
|
+
<!-- Subtle overlay texture -->
|
|
673
|
+
<div class="overlay-texture" />
|
|
674
|
+
</div>
|