6.7 KiB
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:
{% gsapScrollAnim {
"animationType": "fadeIn"
} %}
[{
"src": "/path/to/image.jpg",
"alt": "Description"
}]
{% endgsapScrollAnim %}
Available Animation Types
fadeIn- Fade in from belowfadeInUp- Fade in from further belowfadeInDown- Fade in from abovescaleIn- Scale up from smallslideInLeft- Slide in from leftslideInRight- Slide in from rightparallax- Parallax scroll effectstagger- Multiple items animate in sequencezoomIn- Zoom into image focal pointzoomOut- Zoom out from focal pointshake- Shake back and forthtremble- Subtle continuous tremblingwobble- Wobble rotationpulse- Continuous pulsing scale
Zoom Animations with Focal Points
{% 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
{% gsapScrollAnim {
"emotion": "jumpscare"
} %}
[{ "src": "/scary-image.jpg", "alt": "Boo!" }]
{% endgsapScrollAnim %}
Sudden appearance + shake + tremble (like an arrow hitting its mark)
Anticipation
{% gsapScrollAnim {
"emotion": "anticipation",
"scrub": false
} %}
[{ "src": "/windup.jpg", "alt": "Getting ready" }]
{% endgsapScrollAnim %}
Pull back, then spring forward (like winding up before a punch)
Dread
{% gsapScrollAnim {
"emotion": "dread"
} %}
[{ "src": "/ominous.jpg", "alt": "Something's coming" }]
{% endgsapScrollAnim %}
Slow reveal with unsettling movement
Relief
{% gsapScrollAnim {
"emotion": "relief"
} %}
[{ "src": "/safe-now.jpg", "alt": "Phew" }]
{% endgsapScrollAnim %}
Gentle fade in with settling motion
Tension
{% gsapScrollAnim {
"emotion": "tension"
} %}
[{ "src": "/suspense.jpg", "alt": "Building suspense" }]
{% endgsapScrollAnim %}
Slow zoom with subtle shake
Excitement
{% 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:
{% 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:
{% gsapScrollAnim {
"animationType": "zoomIn",
"pin": true,
"scrollEnd": "+=500"
} %}
markers (default: false)
Show debug markers (for development):
{% gsapScrollAnim {
"animationType": "fadeIn",
"markers": true
} %}
Multiple Images
{% 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
- Use emotions first -
"emotion": "jumpscare"is easier than combining effects manually - Scrub for slow reveals - Set
"scrub": truefor scroll-controlled drama - No scrub for punchy moments - Set
"scrub": falsefor quick actions - Pin for focus - Use
"pin": trueto hold attention on an element - Zoom needs high-res - Zoom animations automatically request larger image sizes
- Compose for unique feels - Combine effects when presets don't fit:
"effects": ["fadeIn", "wobble"]
For Developers
Adding New Effects
Edit gsap-effects.js:
export const effects = {
myNewEffect: (element, config = {}) => ({
from: {
opacity: 0,
rotationY: 90
},
to: {
opacity: 1,
rotationY: 0,
ease: 'power2.out',
...config
}
})
};
Adding Emotional Presets
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:
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:
{% gsapScrollAnim {
"animationType": "fadeIn",
"markers": true
} %}
Check browser console for warnings about missing animation types or configuration errors.