From 73f7e8fcd586cdf1717074f59ee8c79b149574da Mon Sep 17 00:00:00 2001 From: balex Date: Fri, 27 Mar 2026 18:31:56 +0100 Subject: [PATCH] portfolio anim --- .../src/components/hero/HeroSection.tsx | 153 ++++++++++-------- 1 file changed, 86 insertions(+), 67 deletions(-) diff --git a/portfolio-view/src/components/hero/HeroSection.tsx b/portfolio-view/src/components/hero/HeroSection.tsx index 6a75c1e..1bae505 100644 --- a/portfolio-view/src/components/hero/HeroSection.tsx +++ b/portfolio-view/src/components/hero/HeroSection.tsx @@ -1,23 +1,65 @@ import { useEffect, useState } from 'react' +/** + * nameStage: + * 0 – all hidden, b/vic.com reset to off-screen (transition: none) + * 1 – "Alex" fades in, b/vic.com still off-screen + * 2 – b slides in from left, vic.com from right; "A" → "a" + * 3 – b/vic.com fade out in-place; "a" → "A" + * + * Cycle: 0→1 (300ms) →2 (1600ms) →3 (4600ms) →0 (7400ms) → repeat every 7600ms + * Content (subtitle, buttons, etc.) fades in once at 3000ms and stays. + */ + export function HeroSection() { - const [stage, setStage] = useState(0) + const [nameStage, setNameStage] = useState(0) + const [contentVisible, setContentVisible] = useState(false) useEffect(() => { - const t1 = setTimeout(() => setStage(1), 300) - const t2 = setTimeout(() => setStage(2), 1600) - const t3 = setTimeout(() => setStage(3), 3000) - return () => { - clearTimeout(t1) - clearTimeout(t2) - clearTimeout(t3) + const timers: ReturnType[] = [] + let alive = true + const add = (fn: () => void, ms: number) => + timers.push(setTimeout(() => { if (alive) fn() }, ms)) + + const CYCLE = 7600 + + const scheduleCycle = (offset: number) => { + add(() => setNameStage(1), offset + 300) + add(() => setNameStage(2), offset + 1600) + add(() => setNameStage(3), offset + 4600) + add(() => setNameStage(0), offset + 7400) } + + scheduleCycle(0) + add(() => setContentVisible(true), 3000) + + for (let i = 1; i <= 40; i++) scheduleCycle(CYCLE * i) + + return () => { alive = false; timers.forEach(clearTimeout) } }, []) - const fadeUp = (active: boolean) => ({ + /** Styles for the sliding "b" and "vic.com" spans */ + const sideStyle = (side: 'left' | 'right'): React.CSSProperties => { + const offscreen = side === 'left' ? 'translateX(-110vw)' : 'translateX(110vw)' + const reset = nameStage === 0 + const visible = nameStage === 2 + const fadingOut = nameStage === 3 + return { + display: 'inline-block', + color: '#22d3ee', + opacity: visible ? 1 : 0, + transform: visible || fadingOut ? 'translateX(0)' : offscreen, + transition: reset + ? 'none' + : 'opacity 0.7s ease-out, transform 1s ease-out', + } + } + + /** Generic fade-up for content blocks */ + const fadeUp = (active: boolean, delay = '0s'): React.CSSProperties => ({ opacity: active ? 1 : 0, - transform: active ? 'translateY(0)' : 'translateY(16px)', - transition: 'opacity 0.8s ease, transform 0.8s ease', + transform: active ? 'translateY(0)' : 'translateY(18px)', + transition: `opacity 0.8s ease ${delay}, transform 0.8s ease ${delay}`, }) return ( @@ -39,64 +81,54 @@ export function HeroSection() {
- {/* Location badge — Stage 3 */} -
= 3)}> + + {/* Location badge */} +
Switzerland, Bern
- {/* Name — animated across 3 stages */} -

- {/* "b" slides in from the left — Stage 2 */} - = 2 ? 1 : 0, - transform: stage >= 2 ? 'translateX(0)' : 'translateX(-160px)', - transition: 'opacity 1s ease, transform 1s ease', - }} - > - b - + {/* ── Animated heading ── */} +

+ {/* "b" — slides in from the left */} + b - {/* "Alex" fades in centered — Stage 1 */} + {/* "Alex" / "alex" — fades in first */} = 1 ? 1 : 0, - transition: 'opacity 0.8s ease', + opacity: nameStage >= 1 ? 1 : 0, + transition: nameStage === 0 ? 'none' : 'opacity 0.8s ease-out', }} > - Alex + {nameStage === 2 ? 'alex' : 'Alex'} - {/* "vic.com" slides in from the right — Stage 2 */} - = 2 ? 1 : 0, - transform: stage >= 2 ? 'translateX(0)' : 'translateX(160px)', - transition: 'opacity 1s ease, transform 1s ease', - }} - > - vic.com - + {/* "vic.com" — slides in from the right */} + vic.com

- {/* Role — Stage 3 */} -
= 3)}> + {/* Role */} +

Java / Fullstack Developer

- {/* Description — Stage 3 */} -
= 3), transitionDelay: '0.1s' }}> + {/* Description */} +

Building backend systems and intelligent applications with{' '} Spring Boot,{' '} @@ -105,8 +137,8 @@ export function HeroSection() {

- {/* CTA buttons — Stage 3 */} -