202 lines
5.3 KiB
TypeScript
202 lines
5.3 KiB
TypeScript
'use client'
|
|
import { useEffect, useRef } from 'react'
|
|
import { animateSplit, animateWords } from '@/lib/animateSplit'
|
|
|
|
// Dynamic imports to avoid SSR issues
|
|
let gsap: any
|
|
let ScrollTrigger: any
|
|
|
|
if (typeof window !== 'undefined') {
|
|
gsap = require('gsap').gsap
|
|
ScrollTrigger = require('gsap/ScrollTrigger').ScrollTrigger
|
|
gsap.registerPlugin(ScrollTrigger)
|
|
}
|
|
|
|
export default function DogstudioAnimations() {
|
|
const initialized = useRef(false)
|
|
|
|
useEffect(() => {
|
|
if (initialized.current) return
|
|
initialized.current = true
|
|
|
|
// Check if GSAP is available
|
|
if (!gsap || !ScrollTrigger) return
|
|
|
|
// Check for reduced motion
|
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
if (prefersReducedMotion) return
|
|
|
|
const ctx = gsap.context(() => {
|
|
// Animate main headline with character split
|
|
const mainHeadline = document.querySelector('.h1')
|
|
if (mainHeadline) {
|
|
gsap.set(mainHeadline, { opacity: 1 })
|
|
animateSplit(mainHeadline as HTMLElement, {
|
|
delay: 0.5,
|
|
stagger: 0.02,
|
|
duration: 0.8
|
|
})
|
|
}
|
|
|
|
// Animate section headings as they come into view
|
|
const sectionHeadings = gsap.utils.toArray<HTMLElement>('.h2')
|
|
sectionHeadings.forEach((heading) => {
|
|
gsap.set(heading, { opacity: 0 })
|
|
|
|
ScrollTrigger.create({
|
|
trigger: heading,
|
|
start: 'top 80%',
|
|
onEnter: () => {
|
|
gsap.set(heading, { opacity: 1 })
|
|
animateWords(heading, { stagger: 0.05 })
|
|
}
|
|
})
|
|
})
|
|
|
|
// Animate cards with stagger
|
|
const cards = gsap.utils.toArray<HTMLElement>('.card, .gallery-item, .testimonial')
|
|
cards.forEach((card, i) => {
|
|
gsap.from(card, {
|
|
y: 60,
|
|
opacity: 0,
|
|
duration: 0.8,
|
|
ease: 'power2.out',
|
|
scrollTrigger: {
|
|
trigger: card,
|
|
start: 'top 85%',
|
|
toggleActions: 'play none none reverse'
|
|
}
|
|
})
|
|
})
|
|
|
|
// Animate chips with stagger
|
|
const chips = gsap.utils.toArray<HTMLElement>('.chip')
|
|
if (chips.length > 0) {
|
|
gsap.from(chips, {
|
|
scale: 0,
|
|
opacity: 0,
|
|
duration: 0.4,
|
|
stagger: 0.1,
|
|
ease: 'back.out(1.7)',
|
|
scrollTrigger: {
|
|
trigger: chips[0],
|
|
start: 'top 80%',
|
|
toggleActions: 'play none none reverse'
|
|
}
|
|
})
|
|
}
|
|
|
|
// Animate gallery items with hover effects
|
|
const galleryItems = gsap.utils.toArray<HTMLElement>('.gallery-item')
|
|
galleryItems.forEach((item) => {
|
|
const img = item.querySelector('img')
|
|
if (!img) return
|
|
|
|
const handleMouseEnter = () => {
|
|
gsap.to(img, {
|
|
scale: 1.05,
|
|
duration: 0.3,
|
|
ease: 'power2.out'
|
|
})
|
|
}
|
|
|
|
const handleMouseLeave = () => {
|
|
gsap.to(img, {
|
|
scale: 1,
|
|
duration: 0.3,
|
|
ease: 'power2.out'
|
|
})
|
|
}
|
|
|
|
item.addEventListener('mouseenter', handleMouseEnter)
|
|
item.addEventListener('mouseleave', handleMouseLeave)
|
|
})
|
|
|
|
// Animate buttons with magnetic effect
|
|
const buttons = gsap.utils.toArray<HTMLElement>('.btn')
|
|
buttons.forEach((button) => {
|
|
const handleMouseMove = (e: MouseEvent) => {
|
|
const rect = button.getBoundingClientRect()
|
|
const x = e.clientX - rect.left - rect.width / 2
|
|
const y = e.clientY - rect.top - rect.height / 2
|
|
|
|
gsap.to(button, {
|
|
x: x * 0.1,
|
|
y: y * 0.1,
|
|
duration: 0.3,
|
|
ease: 'power2.out'
|
|
})
|
|
}
|
|
|
|
const handleMouseLeave = () => {
|
|
gsap.to(button, {
|
|
x: 0,
|
|
y: 0,
|
|
duration: 0.5,
|
|
ease: 'elastic.out(1, 0.3)'
|
|
})
|
|
}
|
|
|
|
button.addEventListener('mousemove', handleMouseMove)
|
|
button.addEventListener('mouseleave', handleMouseLeave)
|
|
})
|
|
|
|
// Parallax effect on hero image
|
|
const heroImage = document.querySelector('.hero__media')
|
|
if (heroImage) {
|
|
gsap.to(heroImage, {
|
|
yPercent: -20,
|
|
ease: 'none',
|
|
scrollTrigger: {
|
|
trigger: heroImage,
|
|
start: 'top bottom',
|
|
end: 'bottom top',
|
|
scrub: true
|
|
}
|
|
})
|
|
}
|
|
|
|
// Smooth reveal for lead text
|
|
const leadText = document.querySelector('.lead')
|
|
if (leadText) {
|
|
gsap.from(leadText, {
|
|
y: 30,
|
|
opacity: 0,
|
|
duration: 0.8,
|
|
delay: 1.2,
|
|
ease: 'power2.out'
|
|
})
|
|
}
|
|
|
|
// Animate eyebrow
|
|
const eyebrow = document.querySelector('.eyebrow')
|
|
if (eyebrow) {
|
|
gsap.from(eyebrow, {
|
|
y: 20,
|
|
opacity: 0,
|
|
duration: 0.6,
|
|
delay: 0.3,
|
|
ease: 'power2.out'
|
|
})
|
|
}
|
|
|
|
// Animate hero buttons
|
|
const heroButtons = document.querySelectorAll('.hero .btn')
|
|
if (heroButtons.length > 0) {
|
|
gsap.from(heroButtons, {
|
|
y: 20,
|
|
opacity: 0,
|
|
duration: 0.6,
|
|
stagger: 0.1,
|
|
delay: 1.5,
|
|
ease: 'power2.out'
|
|
})
|
|
}
|
|
|
|
})
|
|
|
|
return () => ctx.revert()
|
|
}, [])
|
|
|
|
return null
|
|
} |