MVp
This commit is contained in:
110
lib/animateSplit.ts
Normal file
110
lib/animateSplit.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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
|
||||
})
|
||||
}
|
||||
15
lib/i18n.ts
Normal file
15
lib/i18n.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export const supportedLocales = ['en', 'de'] as const;
|
||||
export type Locale = typeof supportedLocales[number];
|
||||
|
||||
export function getInitialLocale(cookieStore: any, _hdrs: any): Locale {
|
||||
const cookie = cookieStore?.get?.('lang')?.value as Locale | undefined;
|
||||
if (cookie && (supportedLocales as readonly string[]).includes(cookie)) return cookie;
|
||||
return 'en';
|
||||
}
|
||||
|
||||
export async function getDictionary(locale: Locale) {
|
||||
// Use relative imports to ensure resolution in both server and edge runtimes
|
||||
const common = await import(`../locales/${locale}/common.json`).then((m) => m.default);
|
||||
const home = await import(`../locales/${locale}/home.json`).then((m) => m.default);
|
||||
return { common, home, ...home } as any;
|
||||
}
|
||||
10
lib/scroll.ts
Normal file
10
lib/scroll.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export function supportsScrollTimeline(): boolean {
|
||||
// @ts-ignore
|
||||
return typeof (CSS as any)?.scrollTimeline !== 'undefined'
|
||||
}
|
||||
export function initScrollFallback() {
|
||||
if (typeof window === 'undefined') return
|
||||
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
if (prefersReduced) return
|
||||
}
|
||||
export function disableAnimations() { }
|
||||
Reference in New Issue
Block a user