Tween - Triggers & Scroll
Interactive triggers and scroll-linked animations
Make animations respond to user interactions and scroll events with powerful trigger modes and scroll integration.
Trigger Modes
Control when animations play with built-in trigger modes.
hover
Type: 'hover'
Play animation on hover, reverse on hover end.
FasterMotion.dom({
target: '.button',
to: { scale: 1.1 },
duration: 300,
trigger: 'hover'
});Try it: Hover Trigger
FasterMotion.dom({
target: '.button',
to: { scale: 1.1 },
duration: 300,
trigger: 'hover'
});click
Type: 'click'
Play animation on click.
FasterMotion.dom({
target: '.button',
to: { rotation: 360 },
duration: 500,
trigger: 'click'
});Try it: Click Trigger
FasterMotion.dom({
target: '.button',
to: { rotation: 360 },
duration: 500,
trigger: 'click'
});visible
Type: 'visible'
Play animation when element enters viewport.
FasterMotion.dom({
target: '.card',
from: { opacity: 0, y: 50 },
to: { opacity: 1, y: 0 },
duration: 800,
trigger: 'visible'
});Try it: Visible Trigger
FasterMotion.dom({
target: '.card',
from: { opacity: 0, y: 50 },
to: { opacity: 1, y: 0 },
duration: 800,
trigger: 'visible'
});manual
Type: 'manual' | Default
Animation starts immediately (default behavior).
FasterMotion.dom({
target: '#box',
to: { x: 200 },
duration: 1000,
trigger: 'manual' // Optional, this is default
});Scroll Animations
Animate based on scroll position.
scroll
Type: 'scroll'
Link animation progress to scroll position.
FasterMotion.dom({
target: '#parallax',
to: { y: 200 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom', // When element top hits viewport bottom
end: 'bottom top' // When element bottom hits viewport top
}
});scrollOptions
Type: object
Configure scroll-linked animation behavior.
FasterMotion.dom({
target: '#header',
to: { opacity: 0, y: -100 },
trigger: 'scroll',
scrollOptions: {
start: 'top top', // Animation starts when element top hits viewport top
end: 'bottom top', // Animation ends when element bottom hits viewport top
scrub: true // Smooth scrubbing
}
});start
Type: string | Default: 'top bottom'
When animation begins. Format: 'element viewport'
Element positions:
'top'- Top of element'center'- Center of element'bottom'- Bottom of element- Offset:
'top+100px','center-50px'
Viewport positions:
'top'- Top of viewport'center'- Center of viewport'bottom'- Bottom of viewport
// Start when element center hits viewport center
FasterMotion.dom({
target: '#box',
to: { scale: 2 },
trigger: 'scroll',
scrollOptions: {
start: 'center center'
}
});end
Type: string | Default: 'bottom top'
When animation completes. Same format as start.
// End when element top hits viewport top
FasterMotion.dom({
target: '#box',
to: { opacity: 0 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'top top'
}
});scrub
Type: boolean | number | Default: false
Smooth scrolling interpolation.
// Instant scrubbing
FasterMotion.dom({
target: '#box',
to: { rotation: 360 },
trigger: 'scroll',
scrollOptions: {
scrub: true
}
});
// Smooth with 0.5s delay
FasterMotion.dom({
target: '#box',
to: { rotation: 360 },
trigger: 'scroll',
scrollOptions: {
scrub: 0.5
}
});Common Patterns
Hover Scale Button
FasterMotion.dom({
target: '.cta-button',
to: { scale: 1.05 },
duration: 200,
trigger: 'hover',
ease: 'curve2.out'
});Try it: Hover Scale Button
FasterMotion.dom({
target: '.cta-button',
to: { scale: 1.05 },
duration: 200,
trigger: 'hover',
ease: 'curve2.out'
});Click Burst Effect
FasterMotion.dom({
target: '.like-button',
to: { scale: 1.3 },
duration: 300,
trigger: 'click',
yoyo: true,
ease: 'elastic.out'
});Try it: Click Burst Effect
FasterMotion.dom({
target: '.like-button',
to: { scale: 1.3 },
duration: 300,
trigger: 'click',
yoyo: true,
ease: 'elastic.out'
});Fade In On Scroll
FasterMotion.dom({
target: '.feature-card',
from: { opacity: 0, y: 30 },
to: { opacity: 1, y: 0 },
duration: 600,
trigger: 'visible',
ease: 'curve2.out'
});Try it: Fade In on Scroll
FasterMotion.dom({
target: '.feature-card',
from: { opacity: 0, y: 30 },
to: { opacity: 1, y: 0 },
duration: 600,
trigger: 'visible',
ease: 'curve2.out'
});Parallax Background
FasterMotion.dom({
target: '.parallax-bg',
to: { y: -200 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});Try it: Parallax Background
FasterMotion.dom({
target: '.parallax-bg',
to: { y: -200 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});Scroll Progress Bar
FasterMotion.dom({
target: '.progress-bar',
to: { scaleX: 1 },
trigger: 'scroll',
scrollOptions: {
start: 'top top',
end: 'bottom bottom',
scrub: true
}
});Try it: Scroll Progress Bar
FasterMotion.dom({
target: '.progress-bar',
to: { scaleX: 1 },
trigger: 'scroll',
scrollOptions: {
start: 'top top',
end: 'bottom bottom',
scrub: true
}
});Sticky Header Fade
FasterMotion.dom({
target: '.header',
to: { opacity: 0.8, y: -10 },
trigger: 'scroll',
scrollOptions: {
start: 'top top',
end: 'top+=100px top',
scrub: true
}
});Image Reveal on View
FasterMotion.dom({
target: '.hero-image',
from: { scale: 1.2, opacity: 0 },
to: { scale: 1, opacity: 1 },
duration: 1000,
trigger: 'visible',
ease: 'curve2.out'
});Advanced Scroll Patterns
Pin and Animate
// Animate while element is pinned
FasterMotion.dom({
target: '.pinned-section',
to: { rotation: 360 },
trigger: 'scroll',
scrollOptions: {
start: 'top top',
end: 'bottom top',
scrub: true,
pin: true
}
});Multi-Stage Scroll
// First stage: fade in
FasterMotion.dom({
target: '.content',
from: { opacity: 0 },
to: { opacity: 1 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'top center',
scrub: true
}
});
// Second stage: slide up
FasterMotion.dom({
target: '.content',
to: { y: -100 },
trigger: 'scroll',
scrollOptions: {
start: 'center center',
end: 'bottom top',
scrub: true
}
});Horizontal Scroll Section
FasterMotion.dom({
target: '.horizontal-container',
to: { x: -1000 },
trigger: 'scroll',
scrollOptions: {
start: 'top top',
end: 'bottom bottom',
scrub: true,
pin: true
}
});Text Reveal on Scroll
// Stagger text lines
const lines = document.querySelectorAll('.text-line');
lines.forEach((line, i) => {
FasterMotion.dom({
target: line,
from: { opacity: 0, y: 20 },
to: { opacity: 1, y: 0 },
duration: 600,
delay: i * 100,
trigger: 'visible',
ease: 'curve2.out'
});
});Zoom on Scroll
FasterMotion.dom({
target: '.zoom-image',
from: { scale: 1 },
to: { scale: 1.5 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'center center',
scrub: true
}
});Try it: Zoom on Scroll
FasterMotion.dom({
target: '.zoom-image',
from: { scale: 1 },
to: { scale: 1.5 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'center center',
scrub: true
}
});Trigger Combinations
Hover + Click
// Hover effect
const hoverAnim = FasterMotion.dom({
target: '.interactive-card',
to: { y: -5, boxShadow: '0 10px 30px rgba(0,0,0,0.2)' },
duration: 300,
trigger: 'hover'
});
// Click effect
const clickAnim = FasterMotion.dom({
target: '.interactive-card',
to: { scale: 0.95 },
duration: 150,
trigger: 'click',
yoyo: true
});Scroll + Visible
// Trigger on visible
FasterMotion.dom({
target: '.section',
from: { opacity: 0 },
to: { opacity: 1 },
duration: 600,
trigger: 'visible'
});
// Then link to scroll
setTimeout(() => {
FasterMotion.dom({
target: '.section .content',
to: { y: -50 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});
}, 650);TypeScript Support
import { FasterMotion } from 'faster-motion';
// Typed trigger modes
FasterMotion.dom({
target: '#box',
to: { scale: 1.2 },
duration: 300,
trigger: 'hover' as const
});
// Typed scroll options
FasterMotion.dom({
target: '#parallax',
to: { y: 200 },
trigger: 'scroll' as const,
scrollOptions: {
start: 'top bottom',
end: 'bottom top',
scrub: true
}
});Performance Tips
Debounce Scroll Events
// Use scrub for smooth performance
FasterMotion.dom({
target: '#box',
to: { rotation: 360 },
trigger: 'scroll',
scrollOptions: {
scrub: 0.3 // Smooth with 300ms interpolation
}
});Limit Scroll Range
// Animate only when in view
FasterMotion.dom({
target: '#box',
to: { x: 200 },
trigger: 'scroll',
scrollOptions: {
start: 'top bottom',
end: 'bottom top' // Limited range
}
});Use will-change
.scroll-animated {
will-change: transform;
}Common Mistakes
Avoid
// Multiple conflicting triggers
FasterMotion.dom({
target: '#box',
to: { x: 100 },
trigger: 'hover'
});
FasterMotion.dom({
target: '#box',
to: { x: 200 },
trigger: 'click'
}); // Conflicts!
// Scroll without scrub for smooth animations
FasterMotion.dom({
target: '#box',
to: { y: 100 },
trigger: 'scroll'
// Missing scrub: true
});Better
// Manage state for multiple interactions
let currentTrigger = 'idle';
const triggers = {
hover: () => {
if (currentTrigger !== 'click') {
FasterMotion.dom({ target: '#box', to: { x: 100 } });
currentTrigger = 'hover';
}
},
click: () => {
FasterMotion.dom({ target: '#box', to: { x: 200 } });
currentTrigger = 'click';
}
};
// Always use scrub for scroll
FasterMotion.dom({
target: '#box',
to: { y: 100 },
trigger: 'scroll',
scrollOptions: { scrub: true }
});See Also
- Tween Basics - Core animation API
- Advanced Features - Callbacks, playback control
- Timeline - Sequence animations
- Easing - Easing functions