MotionFlow logo

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.

Demo
(Counter)
(Roller)
<!-- 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.

Demo
(Counter)
(Roller)
<!-- 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.

AttributeDefaultDescription
data-mf-count-to
data-mf-roller-to
Target value for the animation
data-mf-count-from
data-mf-roller-from
0Starting value of the animation
data-mf-count-duration
data-mf-roller-duration
600Animation duration in milliseconds
data-mf-count-once
data-mf-roller-once
falseRun animation only once
data-mf-count-repeat
data-mf-roller-repeat
bothControls replay behavior on scroll
data-mf-count-trigger
data-mf-roller-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.

Demo
From 500 → 1000 (Counter)
From 500 → 1000 (Roller)
<!-- 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.

ValueUsageDescription
Numberdata-mf-count-from="500"Sets the starting value for a counter animation
Numberdata-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.

Demo
Duration: 2.5s (Counter)
Duration: 2.5s (Roller)
<!-- 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.

ValueUsageDescription
Millisecondsdata-mf-count-duration="2500"Sets the duration of the counter animation
Millisecondsdata-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.

Demo
Runs once (Counter)
Runs once (Roller)
<!-- 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.

ValueUsageDescription
truedata-mf-count-once="true"
data-mf-roller-once="true"
Runs the animation only once when the element first enters the viewport
falsedata-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.

Demo
Repeat: both (Counter)
Repeat: both (Roller)
<!-- 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.

ValueUsageDescription
bothdata-mf-count-repeat="both"
data-mf-roller-repeat="both"
Resets the animation when leaving the viewport from either the top or bottom
topdata-mf-count-repeat="top"
data-mf-roller-repeat="top"
Resets the animation only when scrolling past the element upward
bottomdata-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.

Demo
Trigger: top 80% (Counter)
1450Trigger: top 80% (Roller)
<!-- 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.

ValueUsageDescription
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.

Demo
Counts up (Counter)
Counts down (Counter)
Counts up (Roller)
Counts down (Roller)
<!-- 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.

Demo
<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.

Demo
<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.

Demo
US Format
Indian Format
<!-- 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.

MethodMethod callDescription
initMotionFlow.count.init();
MotionFlow.roller.init();
Initializes or re-initializes counters or rollers using the last configuration
refreshMotionFlow.count.refresh();
MotionFlow.roller.refresh();
Recalculates visibility and trigger positions without restarting animations
destroyMotionFlow.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.