# GSAP Animation System This project uses GSAP for scroll-driven and UI animations, designed for low-friction animated storytelling. ## Architecture ### 1. Shared Effects Library (`gsap-effects.js`) Contains reusable animation effects and emotional presets. ### 2. Content Animations (`gsap-shortcode-init.js`) Handles scroll-triggered animations in markdown/blog posts via shortcodes. ### 3. UI Component Animations (`mix-nav-animations.js`) Handles interactive animations for navigation, buttons, and UI elements. --- ## For Content Authors (Markdown/Posts) ### Basic Usage Simple fade-in animation: ```markdown {% gsapScrollAnim { "animationType": "fadeIn" } %} [{ "src": "/path/to/image.jpg", "alt": "Description" }] {% endgsapScrollAnim %} ``` ### Available Animation Types - `fadeIn` - Fade in from below - `fadeInUp` - Fade in from further below - `fadeInDown` - Fade in from above - `scaleIn` - Scale up from small - `slideInLeft` - Slide in from left - `slideInRight` - Slide in from right - `parallax` - Parallax scroll effect - `stagger` - Multiple items animate in sequence - `zoomIn` - Zoom into image focal point - `zoomOut` - Zoom out from focal point - `shake` - Shake back and forth - `tremble` - Subtle continuous trembling - `wobble` - Wobble rotation - `pulse` - Continuous pulsing scale ### Zoom Animations with Focal Points ```markdown {% gsapScrollAnim { "animationType": "zoomIn", "focalX": 30, "focalY": 40, "startZoom": 1, "endZoom": 2.5, "scrub": true } %} [{ "src": "/path/to/high-res-image.jpg", "alt": "Image to zoom" }] {% endgsapScrollAnim %} ``` - `focalX` / `focalY`: 0-100 (percentage of image dimensions) - `startZoom` / `endZoom`: Scale values (1 = normal size) --- ## Emotional Presets (New!) Create emotional storytelling without technical details: ### Jumpscare ```markdown {% gsapScrollAnim { "emotion": "jumpscare" } %} [{ "src": "/scary-image.jpg", "alt": "Boo!" }] {% endgsapScrollAnim %} ``` Sudden appearance + shake + tremble (like an arrow hitting its mark) ### Anticipation ```markdown {% gsapScrollAnim { "emotion": "anticipation", "scrub": false } %} [{ "src": "/windup.jpg", "alt": "Getting ready" }] {% endgsapScrollAnim %} ``` Pull back, then spring forward (like winding up before a punch) ### Dread ```markdown {% gsapScrollAnim { "emotion": "dread" } %} [{ "src": "/ominous.jpg", "alt": "Something's coming" }] {% endgsapScrollAnim %} ``` Slow reveal with unsettling movement ### Relief ```markdown {% gsapScrollAnim { "emotion": "relief" } %} [{ "src": "/safe-now.jpg", "alt": "Phew" }] {% endgsapScrollAnim %} ``` Gentle fade in with settling motion ### Tension ```markdown {% gsapScrollAnim { "emotion": "tension" } %} [{ "src": "/suspense.jpg", "alt": "Building suspense" }] {% endgsapScrollAnim %} ``` Slow zoom with subtle shake ### Excitement ```markdown {% gsapScrollAnim { "emotion": "excitement", "scrub": false } %} [{ "src": "/celebration.jpg", "alt": "Yay!" }] {% endgsapScrollAnim %} ``` Bouncy entrance with energy --- ## Effect Composition (Advanced) Combine multiple effects to create custom emotions: ```markdown {% gsapScrollAnim { "effects": ["fadeIn", "shake", "tremble"] } %} [{ "src": "/custom-combo.jpg", "alt": "Custom animation" }] {% endgsapScrollAnim %} ``` Effects apply in sequence and can overlap. --- ## Scroll Control Options ### `scrub` (default: `true`) - `true` - Animation tied to scroll position (smooth) - `false` - Animation plays once when triggered ### `scrollStart` (default: `"top 80%"`) When animation begins: - `"top 80%"` - When element's top hits 80% down viewport - `"center center"` - When element center hits viewport center - `"bottom 20%"` - When element bottom hits 20% down viewport ### `scrollEnd` (default: `"bottom 20%"`) When animation completes (for scrubbed animations) ### `pin` (default: `false`) Pin the element in place during animation: ```markdown {% gsapScrollAnim { "animationType": "zoomIn", "pin": true, "scrollEnd": "+=500" } %} ``` ### `markers` (default: `false`) Show debug markers (for development): ```markdown {% gsapScrollAnim { "animationType": "fadeIn", "markers": true } %} ``` --- ## Multiple Images ```markdown {% gsapScrollAnim { "animationType": "stagger" } %} [{ "src": "/image1.jpg", "alt": "First", "caption": "Image 1" }, { "src": "/image2.jpg", "alt": "Second", "caption": "Image 2" }, { "src": "/image3.jpg", "alt": "Third", "caption": "Image 3" }] {% endgsapScrollAnim %} ``` --- ## Tips for Storytelling 1. **Use emotions first** - `"emotion": "jumpscare"` is easier than combining effects manually 2. **Scrub for slow reveals** - Set `"scrub": true` for scroll-controlled drama 3. **No scrub for punchy moments** - Set `"scrub": false` for quick actions 4. **Pin for focus** - Use `"pin": true` to hold attention on an element 5. **Zoom needs high-res** - Zoom animations automatically request larger image sizes 6. **Compose for unique feels** - Combine effects when presets don't fit: `"effects": ["fadeIn", "wobble"]` --- ## For Developers ### Adding New Effects Edit [`gsap-effects.js`](../src/assets/scripts/bundle/gsap-effects.js): ```javascript export const effects = { myNewEffect: (element, config = {}) => ({ from: { opacity: 0, rotationY: 90 }, to: { opacity: 1, rotationY: 0, ease: 'power2.out', ...config } }) }; ``` ### Adding Emotional Presets ```javascript export const emotions = { myEmotion: (element, config = {}) => { const tl = gsap.timeline(); tl.from(element, { /* initial state */ }) .to(element, { /* first animation */ }) .to(element, { /* second animation */ }, '-=0.5'); // overlap return tl; } }; ``` ### UI Component Animations Create component-specific files like [`mix-nav-animations.js`](../src/assets/scripts/bundle/mix-nav-animations.js): ```javascript import gsap from 'gsap'; import { shouldAnimate } from './gsap-effects.js'; function initMyComponentAnimations() { if (!shouldAnimate()) return; document.querySelectorAll('.my-element').forEach(el => { el.addEventListener('mouseenter', () => { gsap.to(el, { scale: 1.1, duration: 0.2 }); }); }); } // Turbo-compatible initialization document.addEventListener('DOMContentLoaded', initMyComponentAnimations); if (window.Turbo) { document.addEventListener('turbo:load', initMyComponentAnimations); } ``` --- ## Accessibility All animations respect `prefers-reduced-motion`. Users with this preference will see static content without animations. --- ## Debugging Enable markers to see scroll trigger points: ```markdown {% gsapScrollAnim { "animationType": "fadeIn", "markers": true } %} ``` Check browser console for warnings about missing animation types or configuration errors.