Counter Effect
Animated number counting with easing, formatting, and scroll-triggered activation. Perfect for stats and dashboards.
Domma.effects.counter()
The counter() effect smoothly counts from one number to another using requestAnimationFrame with configurable easing. Supports thousands separators, prefixes, suffixes, decimals, and scroll-triggered activation.
// Count from 0 to the number in the element
Domma.effects.counter('.stat-number');
// With full formatting
Domma.effects.counter('.revenue', {
from: 0,
to: 1250000,
duration: 3000,
separator: ',',
prefix: '$',
decimals: 2,
easing: 'ease-out'
});
// Scroll-triggered
Domma.effects.counter('.stat', {
trigger: 'scroll',
threshold: 0.5
});
Demo: Dashboard Stats
Click to animate stat counters with staggered timing.
12500
Users
847
Projects
99
Uptime %
3200000
API Calls
<div class="stat-card">
<div class="stat-number counter-demo-1">12500</div>
<div class="stat-label">Users</div>
</div>
<div class="stat-card">
<div class="stat-number counter-demo-1">847</div>
<div class="stat-label">Projects</div>
</div>
<div class="stat-card">
<div class="stat-number counter-demo-1">99</div>
<div class="stat-label">Uptime %</div>
</div>
<div class="stat-card">
<div class="stat-number counter-demo-1">3200000</div>
<div class="stat-label">API Calls</div>
</div>
<button type="button" class="btn btn-primary" id="counter-start-1">Start Count</button>
<button type="button" class="btn btn-danger" id="counter-restart-1">Restart</button>/**
* Demo: Dashboard Stats - from public/showcase/effects/counter.
*/
export function mount(root) {
// Store original values for restart
const originals1 = [];
$('.counter-demo-1').each((i, el) => originals1.push(el.textContent));
let ctrl1;
// Demo 1: Dashboard Stats
$(root).find('#counter-start-1').on('click', () => {
if (ctrl1) ctrl1.destroy();
$('.counter-demo-1').each((i, el) => { el.textContent = originals1[i]; });
ctrl1 = Domma.effects.counter('.counter-demo-1', {
duration: 2000,
stagger: 200,
separator: ',',
respectMotionPreference: false
});
});
$(root).find('#counter-restart-1').on('click', () => {
if (ctrl1) ctrl1.restart();
});
}Domma.effects.counter('.stat-number', {
duration: 2000,
stagger: 200,
separator: ','
});
Demo: Prefix & Suffix
Add currency symbols, percentage signs, and other formatting.
0
Revenue
0
Satisfaction
0
Temperature
<div class="stat-card">
<div class="stat-number" id="counter-revenue" style="font-size: 2.5rem;">0</div>
<div class="stat-label">Revenue</div>
</div>
<div class="stat-card">
<div class="stat-number" id="counter-percent" style="font-size: 2.5rem;">0</div>
<div class="stat-label">Satisfaction</div>
</div>
<div class="stat-card">
<div class="stat-number" id="counter-temp" style="font-size: 2.5rem;">0</div>
<div class="stat-label">Temperature</div>
</div>
<button type="button" class="btn btn-primary mt-3" id="counter-start-2">Start</button>/**
* Demo: Prefix & Suffix - from public/showcase/effects/counter.
*/
export function mount(root) {
// Demo 2: Prefix & Suffix
$(root).find('#counter-start-2').on('click', () => {
Domma.effects.counter('#counter-revenue', {
to: 84750, prefix: '$', separator: ',', duration: 2000,
respectMotionPreference: false
});
Domma.effects.counter('#counter-percent', {
to: 98.7, suffix: '%', decimals: 1, duration: 2000,
respectMotionPreference: false
});
Domma.effects.counter('#counter-temp', {
to: 23.5, suffix: '°C', decimals: 1, duration: 2000,
respectMotionPreference: false
});
});
}Domma.effects.counter('#revenue', {
to: 84750, prefix: '$', separator: ','
});
Domma.effects.counter('#percent', {
to: 98.7, suffix: '%', decimals: 1
});
Domma.effects.counter('#temp', {
to: 23.5, suffix: '°C', decimals: 1
});
Demo: Easing Comparison
Compare different easing functions counting to 1,000.
0
Linear
0
Ease Out
0
Ease In-Out
<div class="stat-card">
<div class="stat-number" id="counter-linear" style="font-size: 2.5rem;">0</div>
<div class="stat-label">Linear</div>
</div>
<div class="stat-card">
<div class="stat-number" id="counter-easeout" style="font-size: 2.5rem;">0</div>
<div class="stat-label">Ease Out</div>
</div>
<div class="stat-card">
<div class="stat-number" id="counter-easeinout" style="font-size: 2.5rem;">0</div>
<div class="stat-label">Ease In-Out</div>
</div>
<button type="button" class="btn btn-primary mt-3" id="counter-start-3">Start All</button>/**
* Demo: Easing Comparison - from public/showcase/effects/counter.
*/
export function mount(root) {
// Demo 3: Easing Comparison
$(root).find('#counter-start-3').on('click', () => {
$(root).find('#counter-linear').text('0');
$(root).find('#counter-easeout').text('0');
$(root).find('#counter-easeinout').text('0');
Domma.effects.counter('#counter-linear', {
to: 1000, easing: 'linear', duration: 3000, separator: ',',
respectMotionPreference: false
});
Domma.effects.counter('#counter-easeout', {
to: 1000, easing: 'ease-out', duration: 3000, separator: ',',
respectMotionPreference: false
});
Domma.effects.counter('#counter-easeinout', {
to: 1000, easing: 'ease-in-out', duration: 3000, separator: ',',
respectMotionPreference: false
});
});
}Domma.effects.counter('#linear', { to: 1000, easing: 'linear', duration: 3000 });
Domma.effects.counter('#easeout', { to: 1000, easing: 'ease-out', duration: 3000 });
Domma.effects.counter('#easeinout', { to: 1000, easing: 'ease-in-out', duration: 3000 });
Demo: Scroll Triggered
These counters start when they scroll into view. Scroll down if they haven't started yet.
5280
Downloads
142
Contributors
28
Countries
<div class="stat-card">
<div class="stat-number counter-scroll">5280</div>
<div class="stat-label">Downloads</div>
</div>
<div class="stat-card">
<div class="stat-number counter-scroll">142</div>
<div class="stat-label">Contributors</div>
</div>
<div class="stat-card">
<div class="stat-number counter-scroll">28</div>
<div class="stat-label">Countries</div>
</div>/**
* Demo: Scroll Triggered - from public/showcase/effects/counter.
*/
export function mount(root) {
// Demo 4: Scroll triggered
Domma.effects.counter('.counter-scroll', {
trigger: 'scroll',
threshold: 0.5,
stagger: 150,
separator: ',',
respectMotionPreference: false
});
}Domma.effects.counter('.counter-scroll', {
trigger: 'scroll',
threshold: 0.5,
stagger: 150,
separator: ','
});
Demo: Decimal Precision
Count with decimal places for financial or scientific data.
0
Portfolio Value
<div class="stat-number" id="counter-decimal" style="font-size: 2.5rem;">0</div>
<div class="stat-label">Portfolio Value</div>
<button type="button" class="btn btn-primary mt-3" id="counter-start-decimal">Start</button>/**
* Demo: Decimal Precision - from public/showcase/effects/counter.
*/
export function mount(root) {
// Demo 5: Decimal
$(root).find('#counter-start-decimal').on('click', () => {
$(root).find('#counter-decimal').text('0');
Domma.effects.counter('#counter-decimal', {
to: 42867.53,
prefix: '$',
decimals: 2,
separator: ',',
duration: 2500,
respectMotionPreference: false
});
});
}Domma.effects.counter('#counter-decimal', {
to: 42867.53,
prefix: '$',
decimals: 2,
separator: ',',
duration: 2500
});
Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
from |
number | 0 | Start value |
to |
number | null | End value (reads from element if null) |
duration |
number | 2000 | Total count duration in ms |
easing |
string | 'ease-out' | 'linear', 'ease-out', 'ease-in-out' |
decimals |
number | 0 | Decimal places |
separator |
string | ',' | Thousands separator |
prefix |
string | '' | Text before number |
suffix |
string | '' | Text after number |
stagger |
number | 0 | Delay between elements (ms) |
trigger |
string | 'immediate' | 'immediate' or 'scroll' |
threshold |
number | 0.5 | Observer threshold when trigger is scroll |
respectMotionPreference |
boolean | true | Honour prefers-reduced-motion |
onUpdate |
function | null | Callback on each frame (currentValue) |
onComplete |
function | null | Callback when counting completes |