Counters & Rollers
Counters and Rollers animate numeric values as they enter the viewport, offering both smooth counting and rolling digit animations.
They support large numeric values, custom start and end values, and flexible timing and trigger options, with counters additionally supporting formatted numbers, decimals, and negative values.
# Getting started
Animate numeric values on scroll using MotionFlow’s number animation attributes. MotionFlow provides two styles: Counter (smooth number transition) and Roller (digit-by-digit rolling effect).
No initialization required. No configuration required. Just add an attribute and MotionFlow handles everything automatically.
<!-- Counter -->
<span data-mf-count-to="250"></span>
<!-- Roller -->
<span data-mf-roller-to="250"></span>MotionFlow automatically detects these attributes and animates the numbers when they enter the viewport.
# Attributes
MotionFlow counters and rollers are attribute-driven. All number animation behavior can be controlled directly from HTML using data-mf-count-* and data-mf-roller-* attributes, with no JavaScript configuration required.
<!-- Counter -->
<span
data-mf-count-to="250"
data-mf-count-from="50"
data-mf-count-duration="2000"
data-mf-count-once="false"
data-mf-count-repeat="both"
data-mf-count-trigger="top 90%"
></span>
<!-- Roller -->
<span
data-mf-roller-to="250"
data-mf-roller-from="50"
data-mf-roller-duration="2000"
data-mf-roller-once="false"
data-mf-roller-repeat="both"
data-mf-roller-trigger="top 90%"
></span>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-count-to | — | Target value for the animation |
data-mf-count-from | 0 | Starting value of the animation |
data-mf-count-duration | 600 | Animation duration in milliseconds |
data-mf-count-once | false | Run animation only once |
data-mf-count-repeat | both | Controls replay behavior on scroll |
data-mf-count-trigger | top 90% | Defines when animation starts |
# Start value
Define a custom starting value for a counter or roller animation using data-mf-count-from or data-mf-roller-from. This allows you to animate between two meaningful numbers instead of always starting from zero.
<!-- Counter -->
<span data-mf-count-to="1000" data-mf-count-from="500"></span>
<!-- Roller -->
<span data-mf-roller-to="1000" data-mf-roller-from="500"></span>If no start value is provided, MotionFlow automatically starts the animation from 0. The target value is defined using the data-mf-count-to or data-mf-roller-to attribute.
| Value | Usage | Description |
|---|---|---|
| Number | data-mf-count-from="500" | Sets the starting value for a counter animation |
| Number | data-mf-roller-from="500" | Sets the starting value for a roller animation |
# Animation duration
Control how long a counter or roller animation takes to complete using the data-mf-count-duration or data-mf-roller-duration attribute. The value is specified in milliseconds.
<!-- Counter -->
<span data-mf-count-to="1200" data-mf-count-duration="2500"></span>
<!-- Roller -->
<span data-mf-roller-to="1250" data-mf-roller-duration="2500"></span>Higher values result in slower, smoother animations, while lower values create faster, snappier transitions.
| Value | Usage | Description |
|---|---|---|
| Milliseconds | data-mf-count-duration="2500" | Sets the duration of the counter animation |
| Milliseconds | data-mf-roller-duration="2500" | Sets the duration of the roller animation |
# Run once
Control whether a counter or roller animation runs only once using data-mf-count-once or data-mf-roller-once.
<!-- Counter -->
<span data-mf-count-to="300" data-mf-count-once="true"></span>
<!-- Roller -->
<span data-mf-roller-to="450" data-mf-roller-once="true"></span>When enabled, the animation runs only the first time the element enters the viewport and does not replay on subsequent scrolls.
| Value | Usage | Description |
|---|---|---|
| true | data-mf-count-once="true"data-mf-roller-once="true" | Runs the animation only once when the element first enters the viewport |
| false | data-mf-count-once="false"data-mf-roller-once="false" | Allows the animation to replay when the element re-enters the viewport |
# Repeat animation
Control whether a counter or roller animation should reset and replay when leaving the viewport using data-mf-count-repeat or data-mf-roller-repeat.
<!-- Counter -->
<span data-mf-count-to="800" data-mf-count-repeat="both"></span>
<!-- Roller -->
<span data-mf-roller-to="1250" data-mf-roller-repeat="both"></span>Repeat controls when the animation resets after leaving the viewport. This allows counters and rollers to replay when scrolling back into view.
| Value | Usage | Description |
|---|---|---|
| both | data-mf-count-repeat="both"data-mf-roller-repeat="both" | Resets the animation when leaving the viewport from either the top or bottom |
| top | data-mf-count-repeat="top"data-mf-roller-repeat="top" | Resets the animation only when scrolling past the element upward |
| bottom | data-mf-count-repeat="bottom"data-mf-roller-repeat="bottom" | Resets the animation only when scrolling past the element downward |
# Trigger position
Control when a counter or roller animation starts based on scroll position using data-mf-count-trigger or data-mf-roller-trigger.
<!-- Counter -->
<span data-mf-count-to="600" data-mf-count-trigger="top 80%"></span>
<!-- Roller -->
<span data-mf-roller-to="1450" data-mf-roller-trigger="top 80%"></span>The trigger defines when the animation starts relative to the viewport. For example, top 80% means the animation begins when the top of the element reaches 80% of the viewport height.
Below are a few example trigger values. You can customize them as needed.
| Value | Usage | Description |
|---|---|---|
| top 80% | data-mf-count-trigger="top 80%"data-mf-roller-trigger="top 80%" | Starts the animation when the top of the element reaches 80% of the viewport height |
| center 70% | data-mf-count-trigger="center 70%"data-mf-roller-trigger="center 70%" | Starts the animation when the element’s center aligns with 70% of the viewport height |
| bottom 90% | data-mf-count-trigger="bottom 90%"data-mf-roller-trigger="bottom 90%" | Starts the animation when the bottom of the element reaches 90% of the viewport height |
# Counting direction
Counters and rollers automatically determine the animation direction based on the relationship between the start and target values.
If the target value is greater than the start value, the animation counts up. If the target value is smaller, the animation counts down.
<!-- Counter: counts up -->
<span data-mf-count-from="200" data-mf-count-to="800"></span>
<!-- Counter: counts down -->
<span data-mf-count-from="800" data-mf-count-to="200"></span>
<!-- Roller: counts up -->
<span data-mf-roller-from="200" data-mf-roller-to="800"></span>
<!-- Roller: counts down -->
<span data-mf-roller-from="800" data-mf-roller-to="200"></span>No additional attribute is required to control direction for either counters or rollers.
# Negative values
Counters support negative values and can animate smoothly across zero in either direction.
This allows you to count down into negative numbers or animate between positive and negative values without any additional configuration.
<span data-mf-count-to="-250"></span>Negative values are supported by counters only. Rollers do not support negative numbers.
# Decimal values
Counters support decimal values and preserve decimal precision throughout the animation.
This allows smooth transitions between floating-point values.
<span data-mf-count-to="12.75"></span>
<span data-mf-count-from="7.5" data-mf-count-to="10.25"></span>Decimal values are supported by counters only. Rollers work with whole numbers only.
# Number formatting
Counters support formatted numbers with comma separators and preserve the formatting style during animation.
Both US and Indian numbering formats are supported automatically.
<!-- US format -->
<span data-mf-count-to="1,051,345"></span>
<!-- Indian format -->
<span data-mf-count-to="10,51,345"></span>Number formatting is supported by counters only. Rollers do not support comma-separated values.
# Counter & Roller lifecycle
Counters and rollers initialize automatically when they enter the viewport. If elements are hidden, shown later, or updated dynamically, you may need to control their lifecycle manually.
| Method | Method call | Description |
|---|---|---|
| init | MotionFlow.count.init();MotionFlow.roller.init(); | Initializes or re-initializes counters or rollers using the last configuration |
| refresh | MotionFlow.count.refresh();MotionFlow.roller.refresh(); | Recalculates visibility and trigger positions without restarting animations |
| destroy | MotionFlow.count.destroy();MotionFlow.roller.destroy(); | Stops animations, removes event listeners, and resets values to their initial state |
## Example usage
When counters or rollers become visible dynamically — such as inside accordions, tabs, or AJAX-loaded content — refresh or re-initialize them after the layout update.
// Call this after counters become visible
function onLayoutChange() {
MotionFlow.count.refresh();
// Call this after rollers become visible
function onLayoutChange() {
MotionFlow.roller.refresh();
}# Global configuration (optional)
MotionFlow counters and rollers work out of the box with zero JavaScript configuration. By default, all behavior is controlled using HTML attributes.
Global configuration is only required if you want to define shared defaults for counters or rollers instead of repeating the same attributes across multiple elements.
Individual elements can still override these settings using data-mf-count-* and data-mf-roller-* attributes.
MotionFlow.init({
/* --------------------------------
Counters
----------------------------------- */
count: {
from: 0, // Default : 0 | Starting value for counters
duration: 1200, // Default : 1200 | Animation duration (ms)
once: false, // Default : false | Run animation only once
repeat: "both", // Default : both | top | bottom | both
trigger: "top 90%" // Default : top 90%
},
/* --------------------------------
Rollers
----------------------------------- */
roller: {
from: 0, // Default : 0 | Starting value for counters
duration: 1200, // Default : 1200 | Animation duration (ms)
once: false, // Default : false | Run animation only once
repeat: "both", // Default : both | top | bottom | both
trigger: "top 90%" // Default : top 90%
}
});MotionFlow.init({
/* --------------------------------
Counters - Used for number count-up animations
----------------------------------- */
count: {
from: 0, // Default: 0 | Starting value of the counter
duration: 1200, // Default: 1200 | Animation duration in milliseconds
once: false, // Default: false | Animate only the first time it enters the viewport
repeat: "both", // Default: "both" | top | bottom | both — reset behavior on exit
trigger: "top 90%" // Default: "top 90%" | When the counter animation starts
},
/* --------------------------------
Rollers - Used for rolling / odometer-style numbers
----------------------------------- */
roller: {
from: 0, // Default: 0 | Starting value of the roller
duration: 1200, // Default: 1200 | Animation duration in milliseconds
once: false, // Default: false | Animate only the first time it enters the viewport
repeat: "both", // Default: "both" | top | bottom | both — reset behavior on exit
trigger: "top 90%" // Default: "top 90%" | When the roller animation starts
}
});If you don’t call MotionFlow.init(), MotionFlow automatically uses built-in defaults.