summaryrefslogtreecommitdiff
path: root/frontend/src/components/LoadingScreen.tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2025-11-15 18:00:09 +0000
committersoryu <soryu@soryu.co>2025-11-15 18:00:09 +0000
commit3e7b2beca1136a42700a7e1aebfe4c0fb2861a00 (patch)
tree6c896c31820681e360e50a73839fc2284c043dea /frontend/src/components/LoadingScreen.tsx
downloadsoryu-3e7b2beca1136a42700a7e1aebfe4c0fb2861a00.tar.gz
soryu-3e7b2beca1136a42700a7e1aebfe4c0fb2861a00.zip
Initial commit
Diffstat (limited to 'frontend/src/components/LoadingScreen.tsx')
-rw-r--r--frontend/src/components/LoadingScreen.tsx129
1 files changed, 129 insertions, 0 deletions
diff --git a/frontend/src/components/LoadingScreen.tsx b/frontend/src/components/LoadingScreen.tsx
new file mode 100644
index 0000000..5f33d00
--- /dev/null
+++ b/frontend/src/components/LoadingScreen.tsx
@@ -0,0 +1,129 @@
+import React, { useEffect, useState, useRef } from 'react'
+
+type Props = {
+ onComplete: () => void
+}
+
+export const LoadingScreen: React.FC<Props> = ({ onComplete }) => {
+ const [fadeOut, setFadeOut] = useState(false)
+ const [mounted, setMounted] = useState(true)
+ const [subtitle, setSubtitle] = useState('')
+ const [animatedText, setAnimatedText] = useState('')
+ const subtitleLoaded = useRef(false)
+
+ useEffect(() => {
+ console.log('Loading screen mounted - starting timer...')
+
+ // Set fixed subtitle and animate it
+ if (!subtitleLoaded.current) {
+ subtitleLoaded.current = true
+
+ const fixedSubtitle = 'Whisper of the Heart'
+ setSubtitle(fixedSubtitle)
+
+ // Animate text character by character
+ let currentIndex = 0
+ const animateText = () => {
+ if (currentIndex <= fixedSubtitle.length) {
+ setAnimatedText(fixedSubtitle.slice(0, currentIndex))
+ currentIndex++
+ setTimeout(animateText, 50) // 50ms delay between characters
+ }
+ }
+
+ // Start animation after heart appears (1000ms = 0.2s heart delay + 0.8s heart animation)
+ setTimeout(animateText, 1000)
+ }
+
+ // Start fade after 4 seconds (longer to show ray spinning)
+ const fadeTimer = setTimeout(() => {
+ console.log('4 seconds passed - Starting fade out...')
+ setFadeOut(true)
+ }, 4000)
+
+ // Complete after fade finishes (4s show + 1s fade = 5s total)
+ const completeTimer = setTimeout(() => {
+ console.log('5 seconds passed - Loading screen completing...')
+ setMounted(false)
+ onComplete()
+ }, 5000)
+
+ return () => {
+ console.log('Cleaning up timers...')
+ clearTimeout(fadeTimer)
+ clearTimeout(completeTimer)
+ }
+ }, []) // Empty dependency array - run once on mount
+
+ const handleClick = () => {
+ console.log('Loading screen clicked, completing early...')
+ setFadeOut(true)
+ setTimeout(() => {
+ setMounted(false)
+ onComplete()
+ }, 1000)
+ }
+
+ console.log('LoadingScreen render - fadeOut:', fadeOut, 'mounted:', mounted)
+
+ if (!mounted) return null
+
+ return (
+ <div className={`loading-screen ${fadeOut ? 'fade-out' : ''}`} onClick={handleClick}>
+ <div className="loading-logo">
+ <div className="heart-container">
+ <div className="sun-rays loading-animate-rays">
+ <svg viewBox="0 0 100 100" className="rays-svg">
+ {/* Rising Sun flag style rays - triangular rays extending to edges */}
+ <g transform="translate(50, 50)">
+ {[...Array(16)].map((_, i) => {
+ const angle = (i * 22.5);
+ const nextAngle = ((i + 1) * 22.5);
+ const rayWidth = 11.25; // Half the angle between rays for triangular shape
+
+ // Create triangular ray path
+ const startAngle = angle - rayWidth;
+ const endAngle = angle + rayWidth;
+
+ const innerRadius = 0; // Start from center (heart center)
+ const outerRadius = 70; // Extend to screen edge
+
+ const x1 = Math.cos(startAngle * Math.PI / 180) * innerRadius;
+ const y1 = Math.sin(startAngle * Math.PI / 180) * innerRadius;
+ const x2 = Math.cos(endAngle * Math.PI / 180) * innerRadius;
+ const y2 = Math.sin(endAngle * Math.PI / 180) * innerRadius;
+ const x3 = Math.cos(startAngle * Math.PI / 180) * outerRadius;
+ const y3 = Math.sin(startAngle * Math.PI / 180) * outerRadius;
+ const x4 = Math.cos(endAngle * Math.PI / 180) * outerRadius;
+ const y4 = Math.sin(endAngle * Math.PI / 180) * outerRadius;
+
+ return (
+ <polygon
+ key={i}
+ points={`${x1},${y1} ${x2},${y2} ${x4},${y4} ${x3},${y3}`}
+ fill={i % 2 === 0 ? "#8B0000" : "#000000"}
+ opacity="0.9"
+ />
+ );
+ })}
+ </g>
+ </svg>
+ </div>
+ <div className="heart-outline loading-animate-heart">
+ <svg viewBox="0 0 100 100" className="heart-svg">
+ <path d="M50,85 C50,85 10,60 10,35 C10,18 20,8 35,8 C42,8 50,13 50,25 C50,13 58,8 65,8 C80,8 90,18 90,35 C90,60 50,85 50,85 Z"
+ fill="#8B0000"
+ stroke="#8B0000"
+ strokeWidth="2"/>
+ </svg>
+ </div>
+ <div className="logo-text loading-animate-text">soryu</div>
+ <div className="loading-subtitle loading-animate-subtitle">{animatedText}</div>
+ <div className="loading-dots loading-animate-text">
+ <span>.</span><span>.</span><span>.</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ )
+} \ No newline at end of file