Components
Animated Number
Animated numeric value with smooth transitions.
Animated Number
The AnimatedNumber component animates numeric values with smooth transitions. It's perfect for displaying counters, statistics, and any numerical data that benefits from animation.
Usage
Current Count
1,234
<template>
<div class="p-6 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-xl max-w-md mx-auto">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 shadow mb-6">
<div class="text-center">
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">Current Count</div>
<div class="text-4xl font-bold text-blue-600 dark:text-blue-400">
<MAnimatedNumber
:value="count"
:spring-options="{ stiffness: 150, damping: 20 }"
/>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const count = ref(1234)
let intervalId = null
const startTimer = () => {
intervalId = setInterval(() => {
// Add 1-10 to simulate live updates
const increment = Math.floor(Math.random() * 10) + 1
count.value += increment
}, 1000)
}
const stopTimer = () => {
if (intervalId) {
clearInterval(intervalId)
intervalId = null
}
}
onMounted(() => {
startTimer()
})
onUnmounted(() => {
stopTimer()
})
</script>
Props
value
number required
Target value to animate to.
class
string
CSS classes to apply to the component.
springOptions
SpringOptions
Spring animation options.
as
string
HTML element to render as.
format
function
Custom formatting function.
Examples
With Custom Duration
Custom Duration Counter
2-second animation duration
Target Value
123,456
Set Target Value
Auto Increment
<template>
<div class="p-4 space-y-4">
<div class="text-center">
<div class="text-sm text-gray-500 mb-1">2-Second Animation</div>
<div class="text-2xl font-bold">
<MAnimatedNumber :value="targetValue" :duration="2" />
</div>
</div>
<div class="grid grid-cols-2 gap-2">
<UButton @click="setTarget(10000)" size="xs">10K</UButton>
<UButton @click="setTarget(50000)" size="xs" variant="outline">50K</UButton>
<UButton @click="setTarget(100000)" size="xs" color="green">100K</UButton>
<UButton @click="setTarget(0)" size="xs" color="red" variant="outline">Reset</UButton>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">Auto Increment:</span>
<UToggle v-model="autoIncrement" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const targetValue = ref(123456)
const autoIncrement = ref(false)
let intervalId = null
const setTarget = (value) => {
targetValue.value = value
}
const startAutoIncrement = () => {
intervalId = setInterval(() => {
targetValue.value += 1000
}, 3000)
}
const stopAutoIncrement = () => {
if (intervalId) {
clearInterval(intervalId)
intervalId = null
}
}
onMounted(() => {
// Auto increment is initially disabled
})
onUnmounted(() => {
stopAutoIncrement()
})
</script>
With Custom Easing
Easing Animation
easeInOut easing function
Ease Value
100
Animation Controls
Auto Cycle
Between 0-1000
<template>
<div class="p-4 space-y-4">
<div class="text-center">
<div class="text-sm text-gray-500 mb-1">EaseInOut Animation</div>
<div class="text-2xl font-bold">
<MAnimatedNumber :value="easeValue" easing="easeInOut" />
</div>
</div>
<div class="grid grid-cols-2 gap-2">
<UButton @click="setEaseValue(0)" size="xs" color="red">To 0</UButton>
<UButton @click="setEaseValue(100)" size="xs" color="primary">To 100</UButton>
<UButton @click="setEaseValue(500)" size="xs" variant="outline">To 500</UButton>
<UButton @click="setEaseValue(1000)" size="xs" color="green">To 1000</UButton>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">Auto Cycle:</span>
<UToggle v-model="autoCycle" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const easeValue = ref(100)
const autoCycle = ref(false)
let intervalId = null
let direction = 1
const setEaseValue = (value) => {
easeValue.value = value
}
const startAutoCycle = () => {
intervalId = setInterval(() => {
if (direction === 1) {
easeValue.value += 100
if (easeValue.value >= 1000) {
direction = -1
}
} else {
easeValue.value -= 100
if (easeValue.value <= 0) {
direction = 1
}
}
}, 2000)
}
const stopAutoCycle = () => {
if (intervalId) {
clearInterval(intervalId)
intervalId = null
}
direction = 1
}
onMounted(() => {
// Auto cycle is initially disabled
})
onUnmounted(() => {
stopAutoCycle()
})
</script>
With Custom Styling
Styled Counter
Custom styling with live updates
Styled Value
42
Control Panel
Live Updates
Automatic styling
<template>
<div class="p-4 space-y-4">
<div class="text-center">
<div class="text-sm text-gray-500 mb-1">Styled Value</div>
<div>
<MAnimatedNumber
:value="styledValue"
as="div"
class="text-3xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"
/>
</div>
</div>
<div class="grid grid-cols-2 gap-2">
<UButton @click="updateStyledValue(styledValue + 25)" size="xs">+25</UButton>
<UButton @click="updateStyledValue(styledValue - 25)" size="xs" variant="outline">-25</UButton>
<UButton @click="updateStyledValue(styledValue + 100)" size="xs" color="green">+100</UButton>
<UButton @click="updateStyledValue(42)" size="xs" color="red" variant="outline">Reset</UButton>
</div>
<div class="flex items-center justify-between">
<span class="text-sm">Live Updates:</span>
<UToggle v-model="liveUpdates" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const styledValue = ref(42)
const liveUpdates = ref(true)
let intervalId = null
const updateStyledValue = (value) => {
styledValue.value = value
}
const startLiveUpdates = () => {
intervalId = setInterval(() => {
const change = Math.floor(Math.random() * 10) - 5
styledValue.value += change
}, 1500)
}
const stopLiveUpdates = () => {
if (intervalId) {
clearInterval(intervalId)
intervalId = null
}
}
onMounted(() => {
if (liveUpdates.value) {
startLiveUpdates()
}
})
onUnmounted(() => {
stopLiveUpdates()
})
</script>
With Trigger
Interactive Counter
Manual increment controls
Current Score
0
Game Controls
Total Actions: 0
<template>
<div class="p-4 space-y-4">
<div class="text-center">
<div class="text-sm text-gray-500 mb-1">Interactive Counter</div>
<div class="text-2xl font-bold">
<MAnimatedNumber :value="score" />
</div>
</div>
<div class="grid grid-cols-2 gap-2">
<UButton @click="increaseScore(10)" size="xs">+10</UButton>
<UButton @click="increaseScore(50)" size="xs" variant="outline">+50</UButton>
<UButton @click="increaseScore(100)" size="xs" color="green">+100</UButton>
<UButton @click="resetScore" size="xs" color="red" variant="outline">Reset</UButton>
</div>
<div class="text-center text-sm text-gray-500">
Actions: {{ actionCount }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const score = ref(0)
const actionCount = ref(0)
const increaseScore = (points) => {
score.value += points
actionCount.value++
}
const resetScore = () => {
score.value = 0
actionCount.value = 0
}
</script>
Dashboard Example
Live Dashboard Counter
Real-time counter with automatic updates and manual interactions
Total Page Views
1,234
+3/sec
Control Panel
Auto Update
Automatic page view simulation
Manual Actions
Started
8:55:11 AM
Updates
0
Manual
0
<template>
<div class="p-6 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-xl max-w-2xl mx-auto">
<div class="text-center mb-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Live Dashboard Counter</h2>
<p class="text-gray-600 dark:text-gray-400">
Real-time counter with automatic updates and manual interactions
</p>
</div>
<!-- Main Counter Display -->
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 shadow-lg mb-6">
<div class="text-center">
<div class="text-sm text-gray-500 dark:text-gray-400 mb-1">Total Page Views</div>
<div class="text-5xl font-bold text-blue-600 dark:text-blue-400 mb-2">
<MAnimatedNumber
:value="pageViews"
:spring-options="{ stiffness: 100, damping: 15 }"
/>
</div>
<div class="flex items-center justify-center text-sm text-green-600 dark:text-green-400">
<UIcon name="i-lucide-trending-up" class="w-4 h-4 mr-1" />
<span>+{{ viewsPerSecond }}/sec</span>
</div>
</div>
</div>
<!-- Control Panel -->
<div class="bg-white dark:bg-gray-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Control Panel</h3>
<!-- Auto-update Toggle -->
<div class="flex items-center justify-between mb-6">
<div>
<div class="font-medium text-gray-900 dark:text-white">Auto Update</div>
<div class="text-sm text-gray-500 dark:text-gray-400">Automatic page view simulation</div>
</div>
<UToggle
v-model="isAutoUpdating"
@update:model-value="toggleAutoUpdate"
/>
</div>
<!-- Manual Controls -->
<div class="space-y-4">
<div class="font-medium text-gray-900 dark:text-white">Manual Actions</div>
<div class="grid grid-cols-2 gap-3">
<UButton
@click="addViews(10)"
color="primary"
size="sm"
>
+10 Views
</UButton>
<UButton
@click="addViews(50)"
color="primary"
variant="outline"
size="sm"
>
+50 Views
</UButton>
<UButton
@click="addViews(100)"
color="green"
size="sm"
>
+100 Views
</UButton>
<UButton
@click="resetViews"
color="red"
variant="outline"
size="sm"
>
Reset
</UButton>
</div>
</div>
<!-- Stats Info -->
<div class="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="grid grid-cols-3 gap-4 text-center">
<div>
<div class="text-sm text-gray-500 dark:text-gray-400">Started</div>
<div class="font-medium text-gray-900 dark:text-white">{{ startTime }}</div>
</div>
<div>
<div class="text-sm text-gray-500 dark:text-gray-400">Updates</div>
<div class="font-medium text-gray-900 dark:text-white">{{ updateCount }}</div>
</div>
<div>
<div class="text-sm text-gray-500 dark:text-gray-400">Manual</div>
<div class="font-medium text-gray-900 dark:text-white">{{ manualActions }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const pageViews = ref(1234)
const isAutoUpdating = ref(true)
const viewsPerSecond = ref(3)
const updateCount = ref(0)
const manualActions = ref(0)
const startTime = ref(new Date().toLocaleTimeString())
let intervalId = null
// Add views manually
const addViews = (amount) => {
pageViews.value += amount
manualActions.value++
}
// Reset views
const resetViews = () => {
pageViews.value = 0
updateCount.value = 0
manualActions.value = 0
startTime.value = new Date().toLocaleTimeString()
}
// Toggle auto update
const toggleAutoUpdate = (enabled) => {
if (enabled) {
startAutoUpdate()
} else {
stopAutoUpdate()
}
}
// Start auto update
const startAutoUpdate = () => {
stopAutoUpdate() // Clear any existing interval
intervalId = setInterval(() => {
// Add random views per second (1-5)
const randomViews = Math.floor(Math.random() * 5) + 1
pageViews.value += randomViews
updateCount.value++
}, 1000)
}
// Stop auto update
const stopAutoUpdate = () => {
if (intervalId) {
clearInterval(intervalId)
intervalId = null
}
}
// Initialize
onMounted(() => {
if (isAutoUpdating.value) {
startAutoUpdate()
}
})
// Cleanup
onUnmounted(() => {
stopAutoUpdate()
})
</script>