feat: add typewriter animation effect to hero text

- Added typewriter-effect package for animated text rendering
- Implemented sequential typing animation for welcome message and signature
- Added client-side rendering check to prevent hydration issues
- Adjusted text opacity and timing delays for better readability
- Created invisible placeholder text to maintain layout during animation loading
This commit is contained in:
2025-10-24 05:06:16 +02:00
parent c3bb18d495
commit a6a0bbef0b
4 changed files with 96 additions and 12 deletions

View File

@@ -1,9 +1,19 @@
'use client'
import Image from 'next/image'
import { useState, useEffect } from 'react'
import Typewriter from 'typewriter-effect'
import { Button } from '@/components/Button'
import { Container } from '@/components/Container'
import BgNoise from '@/components/BgNoise'
export function Hero() {
const [isClient, setIsClient] = useState(false)
useEffect(() => {
setIsClient(true)
}, [])
return (
<>
<div className="relative -mt-20 min-h-screen">
@@ -37,18 +47,68 @@ export function Hero() {
<div className="relative">
<div
className="bg-cover bg-center -mt-14"
style={{ backgroundImage: 'url(/images/paper.jpg)' }}
style={{ backgroundImage: 'url(/images/paper.jpg)', opacity: '0.9' }}
>
<div className="px-6 py-24 lg:px-8">
<div className="mx-auto max-w-2xl text-left">
<p className="font-script text-2xl text-love-red mb-2 font-typewriter">Mon cher,</p>
<p className="font-typewriter text-xl max-w-2xl mx-auto leading-relaxed">
If you find yourself here, youve survived the noise.<br />
Sit, let the glass sweat a little.<br />
The band will start soon, and perhaps someone will meet your eyes before the chorus ends.<br />
If not, the night still owes you a dance.
<p className="font-script text-2xl text-love-red mb-2 font-typewriter relative">
<span className="invisible">Mon cher,</span>
{isClient && (
<span className="absolute top-0 left-0">
<Typewriter
options={{
delay: 75,
}}
onInit={(typewriter) => {
typewriter.typeString('Mon cher,').start()
}}
/>
</span>
)}
</p>
<p className="font-typewriter text-xl max-w-2xl mx-auto leading-relaxed relative">
<span className="invisible" dangerouslySetInnerHTML={{ __html: 'If you find yourself here, youve survived the noise.<br />Sit, let the glass sweat a little.<br />The band will start soon, and perhaps someone will meet your eyes before the chorus ends.<br />If not, the night still owes you a dance.' }} />
{isClient && (
<span className="absolute top-0 left-0">
<Typewriter
options={{
delay: 50,
}}
onInit={(typewriter) => {
typewriter
.pauseFor(2000)
.typeString(
'If you find yourself here, youve survived the noise.<br />',
)
.pauseFor(500)
.typeString('Sit, let the glass sweat a little.<br />')
.pauseFor(500)
.typeString(
'The band will start soon, and perhaps someone will meet your eyes before the chorus ends.<br />',
)
.pauseFor(500)
.typeString('If not, the night still owes you a dance.')
.start()
}}
/>
</span>
)}
</p>
<p className="font-script text-xl text-love-red mt-6 font-typewriter relative">
<span className="invisible"> M.N.</span>
{isClient && (
<span className="absolute top-0 left-0">
<Typewriter
onInit={(typewriter) => {
typewriter
.pauseFor(17000)
.typeString('— M.N.')
.start()
}}
/>
</span>
)}
</p>
<p className="font-script text-xl text-love-red mt-6 font-typewriter"> M.N.</p>
</div>
</div>
</div>