Scroll Animations
Animate elements as they enter the viewport using simple HTML attributes. Create fades, slides, zooms, flips, rotations, and more — with full control over timing, easing, delay, and repeat behavior.
# Getting started
MotionFlow works out of the box. Simply include the MotionFlow CSS and JavaScript files, then start using data-mf-animation attributes in your HTML.
No initialization required. No configuration required. No JavaScript required.
<div data-mf-animation="fade-up">I animate on scroll</div>MotionFlow automatically detects these attributes and applies animations when elements enter the viewport.
# Animation attributes
MotionFlow is attribute-first. All animation behavior can be controlled directly from HTML using data-mf-* attributes, without any JavaScript configuration.
<div
data-mf-animation="fade-up"
data-mf-animation-duration="600"
data-mf-animation-delay="0"
data-mf-animation-distance="100"
data-mf-animation-easing="ease"
data-mf-animation-once="false"
data-mf-animation-repeat="both"
data-mf-animation-trigger="top 90%"
>
All animation attributes
</div>You don’t need to add every attribute. MotionFlow has default values for all attributes. Default values are listed in the table below. Add an attribute only when you want to override the default.
| Attribute | Default | Description |
|---|---|---|
data-mf-animation | — | Defines the animation preset applied when the element enters the viewport |
data-mf-animation-duration | 600 | Duration of the animation in milliseconds |
data-mf-animation-delay | 0 | Delay before the animation starts after entering the viewport |
data-mf-animation-distance | 100 | Controls how far the element moves during the animation (in pixels) |
data-mf-animation-easing | ease | Controls the timing curve using MotionFlow easing presets |
data-mf-animation-once | false | Runs the animation only the first time the element appears |
data-mf-animation-repeat | both | Controls how the animation resets when leaving and re-entering the viewport |
data-mf-animation-trigger | top 90% | Defines when the animation starts relative to the viewport position |
# Animation types
MotionFlow includes a set of built-in animation presets that control how elements visually enter the viewport. These presets are applied using the data-mf-animation attribute.
<div data-mf-animation="fade-up">Fade up</div>
<div data-mf-animation="zoom-in">Zoom in</div>
<div data-mf-animation="rotate">Rotate</div>
<div data-mf-animation="hinge">Hinge</div>| Animation | Usage | Description |
|---|---|---|
| fade | data-mf-animation="fade" | Simple fade in with no movement |
| fade-up | data-mf-animation="fade-up" | Fades in while moving upward |
| fade-down | data-mf-animation="fade-down" | Fades in while moving downward |
| fade-left | data-mf-animation="fade-left" | Fades in while moving from left |
| fade-right | data-mf-animation="fade-right" | Fades in while moving from right |
| fade-up-left | data-mf-animation="fade-up-left" | Fades in from bottom-left |
| fade-up-right | data-mf-animation="fade-up-right" | Fades in from bottom-right |
| fade-down-left | data-mf-animation="fade-down-left" | Fades in from top-left |
| fade-down-right | data-mf-animation="fade-down-right" | Fades in from top-right |
| slide-up | data-mf-animation="slide-up" | Slides upward into view |
| slide-down | data-mf-animation="slide-down" | Slides downward into view |
| slide-left | data-mf-animation="slide-left" | Slides in from left |
| slide-right | data-mf-animation="slide-right" | Slides in from right |
| zoom-in | data-mf-animation="zoom-in" | Scales up while fading in |
| zoom-out | data-mf-animation="zoom-out" | Scales down while fading in |
| zoom-in-up | data-mf-animation="zoom-in-up" | Zooms in from bottom |
| zoom-in-down | data-mf-animation="zoom-in-down" | Zooms in from top |
| zoom-in-left | data-mf-animation="zoom-in-left" | Zooms in from left |
| zoom-in-right | data-mf-animation="zoom-in-right" | Zooms in from right |
| zoom-out-up | data-mf-animation="zoom-out-up" | Zooms out upward |
| zoom-out-down | data-mf-animation="zoom-out-down" | Zooms out downward |
| zoom-out-left | data-mf-animation="zoom-out-left" | Zooms out to left |
| zoom-out-right | data-mf-animation="zoom-out-right" | Zooms out to right |
| flip-up | data-mf-animation="flip-up" | Flips upward along X-axis |
| flip-down | data-mf-animation="flip-down" | Flips downward along X-axis |
| flip-left | data-mf-animation="flip-left" | Flips from left along Y-axis |
| flip-right | data-mf-animation="flip-right" | Flips from right along Y-axis |
| rotate | data-mf-animation="rotate" | Rotates into place |
| rotate-up-left | data-mf-animation="rotate-up-left" | Rotates upward from left |
| rotate-up-right | data-mf-animation="rotate-up-right" | Rotates upward from right |
| rotate-down-left | data-mf-animation="rotate-down-left" | Rotates downward from left |
| rotate-down-right | data-mf-animation="rotate-down-right" | Rotates downward from right |
| blur | data-mf-animation="blur" | Fades in while removing blur |
| blur-up | data-mf-animation="blur-up" | Blurs + fades in while moving upward |
| blur-down | data-mf-animation="blur-down" | Blurs + fades in while moving downward |
| blur-left | data-mf-animation="blur-left" | Blurs + fades in while moving from left |
| blur-right | data-mf-animation="blur-right" | Blurs + fades in while moving from right |
| hinge | data-mf-animation="hinge" | Hinged rotation from the top (center origin) |
| hinge-left | data-mf-animation="hinge-left" | Hinged rotation from top-left corner |
| hinge-right | data-mf-animation="hinge-right" | Hinged rotation from top-right corner |
| lightspeed-left | data-mf-animation="lightspeed-left" | Fast skewed slide-in from left |
| lightspeed-right | data-mf-animation="lightspeed-right" | Fast skewed slide-in from right |
| roll-left | data-mf-animation="roll-left" | Rolls in from left while rotating |
| roll-right | data-mf-animation="roll-right" | Rolls in from right while rotating |
| back-in-up | data-mf-animation="back-in-up" | Enters from below, pauses briefly, then scales to full size |
| back-in-down | data-mf-animation="back-in-down" | Enters from above, pauses briefly, then scales to full size |
| back-in-left | data-mf-animation="back-in-left" | Enters from the left, pauses briefly, then scales to full size |
| back-in-right | data-mf-animation="back-in-right" | Enters from the right, pauses briefly, then scales to full size |
| bounce | data-mf-animation="bounce" | Bounces into place with subtle vertical movement |
| bounce-up | data-mf-animation="bounce-up" | Bounces upward from below the viewport |
| bounce-down | data-mf-animation="bounce-down" | Bounces downward from above the viewport |
| bounce-left | data-mf-animation="bounce-left" | Bounces in from the left side |
| bounce-right | data-mf-animation="bounce-right" | Bounces in from the right side |
| bounce-scale | data-mf-animation="bounce-scale" | Bounces using scale only (no directional movement) |
| bounce-rotate | data-mf-animation="bounce-rotate" | Bounces into place with rotation and scaling |
# Animation duration
Control how long an animation takes to complete using the data-mf-animation-duration attribute. The value is defined in milliseconds.
<div
data-mf-animation="fade-up"
data-mf-animation-duration="1500"
>
Slow animation
</div>Higher values result in slower, smoother animations, while lower values create snappier transitions. Duration applies to all animation presets consistently.
| Attribute | Default | Description |
|---|---|---|
| data-mf-animation-duration | 600 | Duration of the animation in milliseconds |
# Animation delay
Control when an animation starts after the element enters the viewport using the data-mf-animation-delay attribute. The value is defined in milliseconds.
<div
data-mf-animation="fade-up"
data-mf-animation-delay="400"
>
Delayed animation
</div>Delay affects only the start time of an animation — it does not change animation speed. This is useful for creating staggered layouts, visual rhythm, and controlled motion sequences.
| Attribute | Default | Description |
|---|---|---|
| data-mf-animation-delay | 0 | Delay before animation starts (milliseconds) |
# Animation distance
Control how far an element moves during an animation using the data-mf-animation-distance attribute. The value is defined in pixels.
<div
data-mf-animation="fade-up"
data-mf-animation-distance="200"
>
Larger movement
</div>Distance controls the starting offset of an animation. Higher values create more dramatic motion, while lower values result in subtle, minimal movement. Distance applies consistently across all directional animation presets.
| Attribute | Default | Description |
|---|---|---|
| data-mf-animation-distance | 100 | Distance (in pixels) the element travels during animation |
# Animation easing
Control the timing curve of animations using the data-mf-animation-easing attribute. Easing defines how the animation accelerates and slows down over time.
<div
data-mf-animation="fade-up"
data-mf-animation-easing="ease-out-back"
>
Smooth easing
</div>Easing affects the feel of motion. Linear easings feel mechanical, while curved easings feel more natural and polished. MotionFlow supports a rich set of predefined easing presets.
| Attribute | Default | Description |
|---|---|---|
| data-mf-animation-easing | ease | CSS easing function controlling animation timing |
## Built-in easing presets
MotionFlow provides a set of predefined easing presets that can be applied directly using the data-mf-animation-easing attribute. Choose the preset name and add it to your HTML — no custom curves required.
| Easing preset | Usage |
|---|---|
| linear | data-mf-animation-easing="linear" |
| ease | data-mf-animation-easing="ease" |
| ease-in | data-mf-animation-easing="ease-in" |
| ease-out | data-mf-animation-easing="ease-out" |
| ease-in-out | data-mf-animation-easing="ease-in-out" |
| ease-in-back | data-mf-animation-easing="ease-in-back" |
| ease-out-back | data-mf-animation-easing="ease-out-back" |
| ease-in-out-back | data-mf-animation-easing="ease-in-out-back" |
| ease-in-sine | data-mf-animation-easing="ease-in-sine" |
| ease-out-sine | data-mf-animation-easing="ease-out-sine" |
| ease-in-out-sine | data-mf-animation-easing="ease-in-out-sine" |
| ease-in-quad | data-mf-animation-easing="ease-in-quad" |
| ease-out-quad | data-mf-animation-easing="ease-out-quad" |
| ease-in-out-quad | data-mf-animation-easing="ease-in-out-quad" |
| ease-in-cubic | data-mf-animation-easing="ease-in-cubic" |
| ease-out-cubic | data-mf-animation-easing="ease-out-cubic" |
| ease-in-out-cubic | data-mf-animation-easing="ease-in-out-cubic" |
| ease-in-quart | data-mf-animation-easing="ease-in-quart" |
| ease-out-quart | data-mf-animation-easing="ease-out-quart" |
| ease-in-out-quart | data-mf-animation-easing="ease-in-out-quart" |
| ease-in-quint | data-mf-animation-easing="ease-in-quint" |
| ease-out-quint | data-mf-animation-easing="ease-out-quint" |
| ease-in-out-quint | data-mf-animation-easing="ease-in-out-quint" |
| ease-in-expo | data-mf-animation-easing="ease-in-expo" |
| ease-out-expo | data-mf-animation-easing="ease-out-expo" |
| ease-in-out-expo | data-mf-animation-easing="ease-in-out-expo" |
| ease-in-circ | data-mf-animation-easing="ease-in-circ" |
| ease-out-circ | data-mf-animation-easing="ease-out-circ" |
| ease-in-out-circ | data-mf-animation-easing="ease-in-out-circ" |
| ease-in-elastic | data-mf-animation-easing="ease-in-elastic" |
| ease-out-elastic | data-mf-animation-easing="ease-out-elastic" |
| ease-in-out-elastic | data-mf-animation-easing="ease-in-out-elastic" |
| ease-in-bounce | data-mf-animation-easing="ease-in-bounce" |
| ease-out-bounce | data-mf-animation-easing="ease-out-bounce" |
| ease-in-out-bounce | data-mf-animation-easing="ease-in-out-bounce" |
# Run animation once
By default, scroll animations replay when elements re-enter the viewport. Use the data-mf-animation-once attribute to run an animation only the first time it appears.
<div
data-mf-animation="fade-up"
data-mf-animation-once="true"
>
Animate only once
</div>When data-mf-animation-once="true" is set, the animation will not replay even if the element leaves and re-enters the viewport.
| Attribute | Default | Description |
|---|---|---|
| data-mf-animation-once | false | Run animation only the first time the element enters the viewport |
# Repeat behavior
Control how animations behave when elements leave and re-enter the viewport using the data-mf-animation-repeat attribute. Repeat behavior only applies when data-mf-animation-once is set to false.
<div
data-mf-animation="fade-up"
data-mf-animation-repeat="both"
>
Re-animates on scroll
</div>The data-mf-animation-repeat attribute controls how an animation replays when an element leaves and re-enters the viewport. The animation always runs once on first entry, and repeat behavior applies only when data-mf-animation-once="false".
| Attribute | Default | Description |
|---|---|---|
| data-mf-animation-repeat | both | Controls how the animation replays when entering and leaving the viewport |
Repeat values
| Value | Usage | Description |
|---|---|---|
| both | data-mf-animation-repeat="both" | Re-runs the animation when the element re-enters the viewport from either direction |
| top | data-mf-animation-repeat="top" | Animate only when scrolling down into view |
| bottom | data-mf-animation-repeat="bottom" | Animate only when scrolling back up into view |
# Trigger position
Control when an animation should start as an element enters the viewport using the data-mf-animation-trigger attribute. The trigger defines the relationship between the element’s position and the viewport.
<div
data-mf-animation="fade-up"
data-mf-animation-trigger="top 70%"
>
Triggered at 70% viewport
</div>The data-mf-animation-trigger attribute defines when an animation should start by comparing a position on the element with a position inside the viewport.
It follows this format: element-position viewport-position and when the specified point on the element intersects the specified point in the viewport, the animation is triggered.
| Attribute | Default | Description |
|---|---|---|
| data-mf-animation-trigger | top 90% | Defines when the animation should start relative to the viewport |
## Common trigger examples
| Trigger value | What it means |
|---|---|
| top 70% | The animation starts when the top of the element reaches 70% down the viewport height. |
| top center | The animation starts when the top of the element aligns with the center of the viewport. |
| center center | The animation starts when the center of the element reaches the center of the viewport. |
| bottom 90% | The animation starts when the bottom of the element reaches 90% of the viewport height. |
| center 100% | The animation starts when the center of the element reaches the bottom edge of the viewport. |
| top 0% | The animation starts immediately when the top of the element touches the top of the viewport. |
# Stagger animations
Stagger animations allow multiple elements to animate one after another as they enter the viewport. Instead of applying animation settings to each element manually, MotionFlow lets you define stagger behavior on a parent container.
When the parent element reaches its scroll trigger point, MotionFlow starts animating each direct child sequentially. Animation, delay, and timing are automatically calculated — no JavaScript or manual delay calculation required.
<div
data-mf-stagger-animation="fade-up"
data-mf-stagger-gap="120"
>
<div>Item One</div>
<div>Item Two</div>
<div>Item Three</div>
</div>Stagger delay is calculated automatically using the formula:
final delay = staggerDelay + (index × staggerGap)
Stagger animations fully support scroll animation type, duration, delay, distance, easing, once, repeat behavior and trigger.
## Stagger attributes
Stagger animations are applied on a parent element and automatically propagate to its children.
| Attribute | Default | Description |
|---|---|---|
data-mf-stagger-animation | — | Animation type applied to each direct child |
data-mf-stagger-delay | 0 | Base delay (in milliseconds) before the first child animates |
data-mf-stagger-gap | 100 | Delay gap (in milliseconds) added between each child animation |
data-mf-stagger-distance | 100 | Animation distance (in pixels) for all children |
data-mf-stagger-duration | 600 | Animation duration (ms) for all children |
data-mf-stagger-easing | ease | Easing function applied to all children |
data-mf-stagger-once | false | Animate children only once |
data-mf-stagger-repeat | both | Controls when the stagger group resets (top, bottom, both) |
data-mf-stagger-trigger | top 90% | Scroll trigger point for the entire stagger group |
To exclude a specific child from the stagger sequence, add data-mf-stagger-ignore to that element.
Only direct children of the stagger container are animated. Children with their own data-mf-animationattribute are automatically skipped.
# Scroll animation lifecycle
Scroll animations work automatically in most cases. When elements are shown, hidden, or added dynamically, you may need manual control.
| Method | Method call | Description |
|---|---|---|
| init | MotionFlow.scroll.init(); | Initializes or re-initializes scroll animations using the last configuration |
| refresh | MotionFlow.scroll.refresh(); | Recalculates scroll trigger positions without restarting animations |
| destroy | MotionFlow.scroll.destroy(); | Destroys scroll animations without affecting other modules |
## Example usage
When scroll-animated elements become visible dynamically, refresh the scroll engine after the layout update.
// Call this after content becomes visible or layout changes
function onLayoutChange() {
MotionFlow.scroll.refresh();
}
# Global configuration (optional)
MotionFlow does not require any JavaScript configuration to work. However, you can define global defaults if you want to override the default values or avoid repeating the same attributes across multiple elements.
Individual elements can still override these settings using data-mf-* attributes.
MotionFlow.init({
animation: {
/* --------------------------------------------------
Base Scroll Animations (for [data-mf-animation])
----------------------------------------------------- */
duration: 600, // Default: 600 | Animation duration in milliseconds
delay: 0, // Default: 0 | Delay before animation starts (ms)
distance: 100, // Default: 100 | Translate distance in pixels
easing: "ease", // Default: "ease" | Built-in MotionFlow easing presets
once: false, // Default: false | true → animate only once // false → animate every time element enters viewport
repeat: "both", // Default: "both" | Values: top | bottom | both // Reset animation when leaving viewport
trigger: "top 90%", // Default: "top 90%" | Format: <element-anchor> <viewport-anchor> // Element anchor: top | center | bottom // Viewport anchor: % | top | center | bottom
/* --------------------------------------------------
Stagger Scroll Animations (for [data-mf-stagger-animation])
- Applied on parent - Automatically propagates to direct children
----------------------------------------------------- */
stagger: {
delay: 0, // Default: 0 | Base delay before first child animates (ms)
gap: 100, // Default: 100 | Delay gap between staggered children (ms)
duration: 600, // Default: 600 | Duration override for stagger items (ms)
distance: 100, // Default: 100 | Distance override for stagger items (px)
easing: "ease", // Default: "ease" | Easing override for stagger items
once: false, // Default: false | true → animate stagger items only once // false → animate on every scroll entry
repeat: "both", // Default: "both" | Reset behavior for stagger group
trigger: "top 90%" // Default: "top 90%" | Scroll trigger override for stagger group
}
}
});If you don’t call MotionFlow.init(), MotionFlow uses built-in defaults automatically.