import type { AnimationIntensity, InterfaceType, AnimationSpec } from '../utils/types.js';
import { ENTRY_ANIMATIONS, SCROLL_ANIMATIONS, HOVER_ANIMATIONS, LOADING_ANIMATIONS, EASING_FUNCTIONS, TRANSITION_CHOREOGRAPHY } from '../resources/animation-patterns.js';
interface AnimationCompositionInput {
interfaceType: InterfaceType;
intensity: AnimationIntensity;
sections?: string[];
customRequirements?: string;
}
function getIntensityMultiplier(intensity: AnimationIntensity): { duration: number; delay: number; complexity: number } {
const multipliers = {
subtle: { duration: 0.7, delay: 0.5, complexity: 1 },
moderate: { duration: 1.0, delay: 1.0, complexity: 2 },
dramatic: { duration: 1.3, delay: 1.2, complexity: 3 },
cinematic: { duration: 1.6, delay: 1.5, complexity: 4 }
};
return multipliers[intensity];
}
function selectEntryAnimations(intensity: AnimationIntensity): string[] {
const animationsByIntensity = {
subtle: ['fadeIn', 'fadeUp'],
moderate: ['fadeUp', 'slideFromLeft', 'slideFromRight', 'scaleUp'],
dramatic: ['scaleUp', 'blurIn', 'flipIn', 'slideFromLeft'],
cinematic: ['blurIn', 'flipIn', 'bounceIn', 'scaleUp']
};
return animationsByIntensity[intensity];
}
function selectHoverAnimations(intensity: AnimationIntensity): string[] {
const hoverByIntensity = {
subtle: ['lift'],
moderate: ['lift', 'glow', 'backgroundFill'],
dramatic: ['lift', 'glow', 'magneticPull', 'borderDraw'],
cinematic: ['magneticPull', 'tilt3D', 'borderDraw', 'glow']
};
return hoverByIntensity[intensity];
}
function selectScrollAnimations(intensity: AnimationIntensity): string[] {
const scrollByIntensity = {
subtle: ['revealOnScroll'],
moderate: ['revealOnScroll', 'parallax'],
dramatic: ['parallax', 'horizontalScroll', 'scaleOnScroll'],
cinematic: ['parallax', 'horizontalScroll', 'stickyReveal', 'scaleOnScroll', 'progressiveBlur']
};
return scrollByIntensity[intensity];
}
function generateSectionAnimations(interfaceType: InterfaceType, intensity: AnimationIntensity): AnimationSpec[] {
const multiplier = getIntensityMultiplier(intensity);
const specs: AnimationSpec[] = [];
const sectionAnimations: Record<string, Partial<AnimationSpec>> = {
hero: {
element: 'Hero Section',
trigger: 'load',
type: 'Orchestrated entrance - background fades, then image scales, then text slides up, then CTA bounces',
description: 'Creates dramatic first impression with choreographed reveal'
},
navigation: {
element: 'Navigation',
trigger: 'load',
type: 'Fade in with subtle slide down',
description: 'Quick, unobtrusive appearance'
},
features: {
element: 'Feature Cards',
trigger: 'scroll',
type: 'Staggered fade up as they enter viewport',
description: 'Sequential reveal creates rhythm and guides reading'
},
testimonials: {
element: 'Testimonials',
trigger: 'scroll',
type: 'Scale up from center with blur in',
description: 'Draws attention to social proof'
},
pricing: {
element: 'Pricing Cards',
trigger: 'scroll',
type: 'Staggered slide in from sides, featured plan with extra bounce',
description: 'Highlights recommended option'
},
cta: {
element: 'Call-to-Action',
trigger: 'scroll',
type: 'Pulse animation when in view, magnetic hover effect',
description: 'Draws attention and encourages interaction'
},
footer: {
element: 'Footer',
trigger: 'scroll',
type: 'Simple fade in',
description: 'Subtle, doesn\'t distract from content above'
}
};
const baseDuration = 0.5;
const baseDelay = 0.1;
for (const [section, config] of Object.entries(sectionAnimations)) {
specs.push({
element: config.element || section,
trigger: config.trigger || 'scroll',
type: config.type || 'fade up',
duration: `${(baseDuration * multiplier.duration).toFixed(2)}s`,
easing: 'cubic-bezier(0.22, 1, 0.36, 1)',
delay: `${(baseDelay * multiplier.delay).toFixed(2)}s`,
description: config.description || ''
});
}
return specs;
}
function generateCSSVariables(intensity: AnimationIntensity): string {
const multiplier = getIntensityMultiplier(intensity);
return `
### CSS Custom Properties for Animations
\`\`\`css
:root {
/* Timing */
--duration-instant: ${(0.1 * multiplier.duration).toFixed(2)}s;
--duration-fast: ${(0.2 * multiplier.duration).toFixed(2)}s;
--duration-normal: ${(0.3 * multiplier.duration).toFixed(2)}s;
--duration-slow: ${(0.5 * multiplier.duration).toFixed(2)}s;
--duration-slower: ${(0.8 * multiplier.duration).toFixed(2)}s;
/* Easing */
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
--ease-in: cubic-bezier(0.64, 0, 0.78, 0);
--ease-in-out: cubic-bezier(0.45, 0, 0.55, 1);
--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
/* Stagger delays */
--stagger-1: ${(0.05 * multiplier.delay).toFixed(2)}s;
--stagger-2: ${(0.1 * multiplier.delay).toFixed(2)}s;
--stagger-3: ${(0.15 * multiplier.delay).toFixed(2)}s;
--stagger-4: ${(0.2 * multiplier.delay).toFixed(2)}s;
--stagger-5: ${(0.25 * multiplier.delay).toFixed(2)}s;
/* Transform values */
--translate-sm: 10px;
--translate-md: 20px;
--translate-lg: 40px;
--scale-sm: 0.98;
--scale-md: 0.95;
--scale-lg: 0.9;
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
:root {
--duration-instant: 0s;
--duration-fast: 0s;
--duration-normal: 0.01s;
--duration-slow: 0.01s;
--duration-slower: 0.01s;
}
*, *::before, *::after {
animation-duration: 0.01s !important;
transition-duration: 0.01s !important;
}
}
\`\`\`
`;
}
function generateKeyframeExamples(intensity: AnimationIntensity): string {
return `
### Keyframe Definitions
\`\`\`css
/* Fade Up */
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(var(--translate-md));
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Scale In */
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(var(--scale-lg));
}
to {
opacity: 1;
transform: scale(1);
}
}
/* Blur In */
@keyframes blurIn {
from {
opacity: 0;
filter: blur(10px);
}
to {
opacity: 1;
filter: blur(0);
}
}
/* Slide From Left */
@keyframes slideFromLeft {
from {
opacity: 0;
transform: translateX(calc(var(--translate-lg) * -1));
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* Bounce In */
@keyframes bounceIn {
0% {
opacity: 0;
transform: scale(0.3);
}
50% {
transform: scale(1.05);
}
70% {
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* Pulse (for loading/attention) */
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
/* Shimmer (for skeleton loaders) */
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
/* Float (for ambient motion) */
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
\`\`\`
`;
}
export function composeAnimations(input: AnimationCompositionInput): string {
const { interfaceType, intensity, sections, customRequirements } = input;
const multiplier = getIntensityMultiplier(intensity);
const entryAnims = selectEntryAnimations(intensity);
const hoverAnims = selectHoverAnimations(intensity);
const scrollAnims = selectScrollAnimations(intensity);
const sectionSpecs = generateSectionAnimations(interfaceType, intensity);
return `# Animation Specification
## Overview
**Interface Type**: ${interfaceType.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase())}
**Animation Intensity**: ${intensity.toUpperCase()}
**Complexity Level**: ${multiplier.complexity}/4
${customRequirements ? `**Custom Requirements**: ${customRequirements}` : ''}
---
## Animation Philosophy
For ${intensity} intensity, animations should feel:
${intensity === 'subtle' ? '- Barely noticeable but present\n- Quick and efficient\n- Professional and understated' : ''}
${intensity === 'moderate' ? '- Noticeable but not distracting\n- Purposeful and meaningful\n- Enhancing without overwhelming' : ''}
${intensity === 'dramatic' ? '- Eye-catching and impressive\n- Bold and expressive\n- Statement-making' : ''}
${intensity === 'cinematic' ? '- Theatrical and immersive\n- Story-telling through motion\n- Memorable and unique' : ''}
---
## Entry Animations
### Recommended Types
${entryAnims.map(name => {
const anim = ENTRY_ANIMATIONS[name as keyof typeof ENTRY_ANIMATIONS];
return `
#### ${anim.name}
- **Initial State**: \`${anim.initial}\`
- **Final State**: \`${anim.final}\`
- **Duration**: ${anim.duration}
- **Easing**: ${anim.easing}
- **Stagger**: ${anim.stagger}
- **Description**: ${anim.description}
`;
}).join('')}
---
## Scroll Animations
### Active Effects
${scrollAnims.map(name => {
const anim = SCROLL_ANIMATIONS[name as keyof typeof SCROLL_ANIMATIONS];
return `
#### ${anim.name}
- **Description**: ${anim.description}
- **Use Case**: ${anim.useCase}
`;
}).join('')}
### Implementation Notes
- Use Intersection Observer for scroll-triggered animations
- Trigger animations when element is 20-30% in viewport
- Consider scroll velocity for parallax effects
- Debounce scroll handlers for performance
---
## Hover & Interaction Effects
### Active Effects
${hoverAnims.map(name => {
const anim = HOVER_ANIMATIONS[name as keyof typeof HOVER_ANIMATIONS];
return `
#### ${anim.name}
- **Effect**: \`${anim.effect}\`
- **Duration**: ${anim.duration}
- **Description**: ${anim.description}
- **Use Case**: ${anim.useCase}
`;
}).join('')}
---
## Loading States
### Recommended Loaders
${Object.entries(LOADING_ANIMATIONS).slice(0, 4).map(([key, anim]) => `
#### ${anim.name}
- **Effect**: ${anim.effect}
- **Implementation**: ${anim.implementation}
- **Use Case**: ${anim.useCase}
`).join('')}
---
## Section-by-Section Choreography
${sectionSpecs.map(spec => `
### ${spec.element}
- **Trigger**: ${spec.trigger}
- **Animation**: ${spec.type}
- **Duration**: ${spec.duration}
- **Delay**: ${spec.delay}
- **Purpose**: ${spec.description}
`).join('')}
---
## Transition Choreography
### Staggered Reveals
${TRANSITION_CHOREOGRAPHY.staggeredFade.description}
- Timing: ${TRANSITION_CHOREOGRAPHY.staggeredFade.timing}
- Direction: ${TRANSITION_CHOREOGRAPHY.staggeredFade.direction}
### Page/View Transitions
${TRANSITION_CHOREOGRAPHY.exitThenEnter.description}
- Timing: ${TRANSITION_CHOREOGRAPHY.exitThenEnter.timing}
- Direction: ${TRANSITION_CHOREOGRAPHY.exitThenEnter.direction}
---
## Easing Reference
${Object.entries(EASING_FUNCTIONS).map(([key, easing]) => `
### ${easing.name}
- **CSS**: \`${easing.css}\`
- **Feel**: ${easing.description}
- **Use For**: ${easing.useCase}
`).join('')}
---
${generateCSSVariables(intensity)}
${generateKeyframeExamples(intensity)}
---
## Performance Guidelines
### GPU-Accelerated Properties
Only animate these for 60fps performance:
- \`transform\` (translate, scale, rotate)
- \`opacity\`
- \`filter\` (blur, brightness)
### Avoid Animating
- \`width\`, \`height\` (use transform: scale instead)
- \`top\`, \`left\`, \`right\`, \`bottom\` (use transform: translate)
- \`margin\`, \`padding\` (causes layout recalculation)
- \`border-width\` (use box-shadow or pseudo-elements)
### Optimization Tips
1. Use \`will-change\` sparingly for complex animations
2. Use \`transform: translateZ(0)\` to force GPU layer
3. Debounce scroll-based animations
4. Use Intersection Observer, not scroll events
5. Prefer CSS animations over JavaScript when possible
---
## Accessibility: Reduced Motion
Always provide reduced motion alternatives:
\`\`\`css
@media (prefers-reduced-motion: reduce) {
/* Replace motion with instant/fade transitions */
.animated-element {
animation: none;
transition: opacity 0.2s ease;
}
/* Disable parallax and scroll-jacking */
.parallax {
transform: none !important;
}
}
\`\`\`
---
*These animation specifications are designed to create a cohesive, polished feel that elevates the interface beyond typical AI-generated designs.*
`;
}