Files
Aether/frontend/src/views/public/useSectionAnimations.ts
2025-12-10 20:52:44 +08:00

200 lines
6.8 KiB
TypeScript

import { computed, type Ref, type CSSProperties } from 'vue'
import { SECTIONS } from './home-config'
// Animation configuration constants
const ANIMATION_CONFIG = {
delays: {
badge: 0,
title: 0.1,
desc: 0.2,
buttons: 0.3,
scrollIndicator: 0.4,
cardBase: 0.25,
cardIncrement: 0.1,
featureCardBase: 0.2,
featureCardIncrement: 0.15
},
translateY: {
home: 30,
cli: 10,
badge: 8,
featureCard: 30
},
translateX: {
badge: 24,
title: 32,
desc: 28,
buttons: 24,
card: 20
}
} as const
// Get horizontal direction based on section layout
// Claude(1) and Gemini(3) content on right, slide from right
// Codex(2) content on left, slides from left
function getDirectionMultiplier(index: number): number {
if (index === SECTIONS.CLAUDE || index === SECTIONS.GEMINI) return 1
if (index === SECTIONS.CODEX) return -1
return 0
}
function getHorizontalOffset(index: number, distance: number, progress: number): number {
const direction = getDirectionMultiplier(index)
if (direction === 0) return 0
return (1 - progress) * distance * direction
}
export function useSectionAnimations(sectionVisibility: Ref<number[]>) {
const { delays, translateY, translateX } = ANIMATION_CONFIG
// Style generators for different elements
const getBadgeStyle = (index: number): CSSProperties => {
const visibility = sectionVisibility.value[index]
const opacity = Math.min(1, visibility * 3)
const progress = Math.min(1, visibility * 2)
const direction = getDirectionMultiplier(index)
const offsetX = getHorizontalOffset(index, translateX.badge, progress)
const offsetY = direction === 0 ? (1 - progress) * translateY.badge : 0
return {
opacity,
transform: `translate(${offsetX}px, ${offsetY}px)`
}
}
const getTitleStyle = (index: number): CSSProperties => {
const visibility = sectionVisibility.value[index]
const adjustedVisibility = Math.max(0, visibility - delays.title) / (1 - delays.title)
const progress = Math.min(1, adjustedVisibility * 2)
const yBase = getDirectionMultiplier(index) === 0 ? translateY.home : translateY.cli
const offsetY = (1 - progress) * yBase
const offsetX = getHorizontalOffset(index, translateX.title, progress)
return {
opacity: progress,
transform: `translate(${offsetX}px, ${offsetY}px)`
}
}
const getDescStyle = (index: number): CSSProperties => {
const visibility = sectionVisibility.value[index]
const adjustedVisibility = Math.max(0, visibility - delays.desc) / (1 - delays.desc)
const progress = Math.min(1, adjustedVisibility * 2)
const yBase = getDirectionMultiplier(index) === 0 ? translateY.home : translateY.badge
const offsetY = (1 - progress) * yBase
const offsetX = getHorizontalOffset(index, translateX.desc, progress)
return {
opacity: progress,
transform: `translate(${offsetX}px, ${offsetY}px)`
}
}
const getButtonsStyle = (index: number): CSSProperties => {
const visibility = sectionVisibility.value[index]
const adjustedVisibility = Math.max(0, visibility - delays.buttons) / (1 - delays.buttons)
const progress = Math.min(1, adjustedVisibility * 2)
const yBase = getDirectionMultiplier(index) === 0 ? 20 : translateY.badge
const offsetY = (1 - progress) * yBase
const offsetX = getHorizontalOffset(index, translateX.buttons, progress)
return {
opacity: progress,
transform: `translate(${offsetX}px, ${offsetY}px)`
}
}
const getScrollIndicatorStyle = (index: number): CSSProperties => {
const visibility = sectionVisibility.value[index]
const adjustedVisibility = Math.max(0, visibility - delays.scrollIndicator) / (1 - delays.scrollIndicator)
const opacity = Math.min(1, adjustedVisibility * 2)
return { opacity }
}
const getCardStyle = (sectionIndex: number, cardIndex: number): CSSProperties => {
const visibility = sectionVisibility.value[sectionIndex]
const totalDelay = delays.cardBase + cardIndex * delays.cardIncrement
const adjustedVisibility = Math.max(0, visibility - totalDelay) / (1 - totalDelay)
const progress = Math.min(1, adjustedVisibility * 2)
const yBase = getDirectionMultiplier(sectionIndex) === 0 ? 20 : translateY.cli
const offsetY = (1 - progress) * yBase
const offsetX = getHorizontalOffset(sectionIndex, translateX.card, progress)
return {
opacity: progress,
transform: `translate(${offsetX}px, ${offsetY}px)`
}
}
const getFeatureCardStyle = (sectionIndex: number, cardIndex: number): CSSProperties => {
const visibility = sectionVisibility.value[sectionIndex]
const totalDelay = delays.featureCardBase + cardIndex * delays.featureCardIncrement
const adjustedVisibility = Math.max(0, visibility - totalDelay) / (1 - totalDelay)
const opacity = Math.min(1, adjustedVisibility * 2)
const offsetY = (1 - Math.min(1, adjustedVisibility * 2)) * translateY.featureCard
const scale = 0.9 + Math.min(1, adjustedVisibility * 2) * 0.1
return {
opacity,
transform: `translateY(${offsetY}px) scale(${scale})`
}
}
return {
getBadgeStyle,
getTitleStyle,
getDescStyle,
getButtonsStyle,
getScrollIndicatorStyle,
getCardStyle,
getFeatureCardStyle
}
}
// Fixed logo position style based on current section
export function useLogoPosition(
currentSection: Ref<number>,
windowWidth: Ref<number>
) {
const fixedLogoStyle = computed(() => {
const section = currentSection.value
const isDesktop = windowWidth.value >= 768
let transform = ''
let opacity = 1
if (section === SECTIONS.HOME) {
transform = 'scale(1.1) translateY(-18vh)'
opacity = 0.25
} else if (section === SECTIONS.CLAUDE) {
transform = isDesktop ? 'translateX(-25vw) scale(1)' : 'translateY(-20vh) scale(0.8)'
} else if (section === SECTIONS.CODEX) {
transform = isDesktop ? 'translateX(25vw) scale(1)' : 'translateY(-20vh) scale(0.8)'
} else if (section === SECTIONS.GEMINI) {
transform = isDesktop ? 'translateX(-25vw) scale(1)' : 'translateY(-20vh) scale(0.8)'
} else {
transform = isDesktop ? 'translateX(0) scale(1)' : 'translateY(-20vh) scale(0.8)'
opacity = 0.15
}
return {
transform,
opacity,
transition: 'transform 0.8s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.6s ease-out'
}
})
return { fixedLogoStyle }
}
// Logo transition name based on scroll direction
export function useLogoTransition(
currentSection: Ref<number>,
previousSection: Ref<number>
) {
const logoTransitionName = computed(() => {
if (currentSection.value === SECTIONS.HOME || previousSection.value === SECTIONS.HOME) {
return 'logo-scale'
}
if (currentSection.value > previousSection.value) {
return 'logo-slide-left'
}
return 'logo-slide-right'
})
return { logoTransitionName }
}