110 lines
2.1 KiB
TypeScript
110 lines
2.1 KiB
TypeScript
// Dynamic import to avoid SSR issues
|
|
let gsap: any
|
|
|
|
if (typeof window !== 'undefined') {
|
|
gsap = require('gsap').gsap
|
|
}
|
|
|
|
export function splitTextIntoChars(element: HTMLElement): HTMLElement[] {
|
|
const text = element.textContent || ''
|
|
const chars: HTMLElement[] = []
|
|
|
|
element.innerHTML = ''
|
|
|
|
for (let i = 0; i < text.length; i++) {
|
|
const char = text[i]
|
|
const span = document.createElement('span')
|
|
span.textContent = char === ' ' ? '\u00A0' : char
|
|
span.setAttribute('data-char', i.toString())
|
|
span.style.display = 'inline-block'
|
|
span.style.willChange = 'transform'
|
|
element.appendChild(span)
|
|
chars.push(span)
|
|
}
|
|
|
|
return chars
|
|
}
|
|
|
|
export function animateSplit(element: HTMLElement, options: {
|
|
delay?: number
|
|
stagger?: number
|
|
duration?: number
|
|
ease?: string
|
|
} = {}) {
|
|
if (!gsap) return
|
|
|
|
const {
|
|
delay = 0,
|
|
stagger = 0.015,
|
|
duration = 0.8,
|
|
ease = 'power2.out'
|
|
} = options
|
|
|
|
const chars = splitTextIntoChars(element)
|
|
|
|
// Set initial state
|
|
gsap.set(chars, {
|
|
yPercent: 110,
|
|
rotateZ: 5,
|
|
opacity: 0,
|
|
transformOrigin: 'center bottom'
|
|
})
|
|
|
|
// Animate in
|
|
return gsap.to(chars, {
|
|
yPercent: 0,
|
|
rotateZ: 0,
|
|
opacity: 1,
|
|
stagger,
|
|
duration,
|
|
ease,
|
|
delay
|
|
})
|
|
}
|
|
|
|
export function animateWords(element: HTMLElement, options: {
|
|
delay?: number
|
|
stagger?: number
|
|
duration?: number
|
|
ease?: string
|
|
} = {}) {
|
|
if (!gsap) return
|
|
|
|
const {
|
|
delay = 0,
|
|
stagger = 0.08,
|
|
duration = 0.6,
|
|
ease = 'power2.out'
|
|
} = options
|
|
|
|
const text = element.textContent || ''
|
|
const words = text.split(' ')
|
|
|
|
element.innerHTML = ''
|
|
|
|
const wordElements = words.map(word => {
|
|
const span = document.createElement('span')
|
|
span.textContent = word + ' '
|
|
span.style.display = 'inline-block'
|
|
span.style.willChange = 'transform'
|
|
element.appendChild(span)
|
|
return span
|
|
})
|
|
|
|
// Set initial state
|
|
gsap.set(wordElements, {
|
|
yPercent: 100,
|
|
opacity: 0,
|
|
transformOrigin: 'center bottom'
|
|
})
|
|
|
|
// Animate in
|
|
return gsap.to(wordElements, {
|
|
yPercent: 0,
|
|
opacity: 1,
|
|
stagger,
|
|
duration,
|
|
ease,
|
|
delay
|
|
})
|
|
} |