309 lines
6.7 KiB
Markdown
309 lines
6.7 KiB
Markdown
# 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.
|