A simple animated number animation.
1"use client"
2
3import { useState } from "react"
4import { Minus, Plus } from "lucide-react"
5import { toast } from "sonner"
6
7import { Button } from "@/components/ui/button"
8import { Slider } from "@/components/ui/slider"
9import { AnimatedNumber } from "@/components/ui/animated-number"
10import { GradientHeading } from "@/components/ui/gradient-heading"
11import {
12 TextureCardContent,
13 TextureCardHeader,
14 TextureCardStyled,
15} from "@/components/ui/texture-card"
16
17function PrecisionExample() {
18 const [value, setValue] = useState(14.5678)
19
20 return (
21 <TextureCardStyled>
22 <TextureCardHeader className="pl-3">
23 <GradientHeading size="xxs">Precision</GradientHeading>
24 </TextureCardHeader>
25 <TextureCardContent>
26 <div className="flex gap-2">
27 <div
28 className="text-2xl font-bold"
29 style={{ minWidth: "80px", textAlign: "left" }}
30 >
31 <AnimatedNumber value={value} precision={2} />
32 </div>
33 <Button
34 size="sm"
35 variant="ghost"
36 className="border border-primary/10 rounded-full ml-auto py-5"
37 onClick={() => setValue(value + 13.456)}
38 >
39 <Plus className="h-4 w-4" />
40 </Button>
41 </div>
42 </TextureCardContent>
43 </TextureCardStyled>
44 )
45}
46
47function FormatExample() {
48 const [value, setValue] = useState(10)
49
50 const customFormat = (num: number) => `$${num.toFixed(2)}`
51
52 return (
53 <TextureCardStyled>
54 <TextureCardHeader className="pl-3">
55 <GradientHeading size="xxs">Format</GradientHeading>
56 </TextureCardHeader>
57 <TextureCardContent>
58 <div className="flex gap-2">
59 <div
60 className="text-2xl font-bold"
61 style={{ minWidth: "120px", textAlign: "left" }}
62 >
63 <AnimatedNumber value={value} format={customFormat} />
64 </div>
65 <Button
66 size="sm"
67 variant="ghost"
68 className="border border-primary/10 rounded-full ml-auto py-5"
69 onClick={() => setValue(value + 50)}
70 >
71 <Plus className="h-4 w-4" />
72 </Button>
73 </div>
74 </TextureCardContent>
75 </TextureCardStyled>
76 )
77}
78
79function HooksExample() {
80 const [value, setValue] = useState(10)
81
82 const handleAnimationStart = () => {
83 toast("🏁 Animation started ")
84 }
85
86 const handleAnimationComplete = () => {
87 toast("✅ Animation completed ")
88 }
89
90 return (
91 <TextureCardStyled>
92 <TextureCardHeader className="pl-3">
93 <GradientHeading size="xxs">Callbacks</GradientHeading>
94 </TextureCardHeader>
95 <TextureCardContent>
96 <div className="flex gap-2">
97 <div
98 className="text-2xl font-bold"
99 style={{ minWidth: "50px", textAlign: "left" }}
100 >
101 <AnimatedNumber
102 value={value}
103 onAnimationStart={handleAnimationStart}
104 onAnimationComplete={handleAnimationComplete}
105 />
106 </div>
107 <Button
108 size="sm"
109 variant="ghost"
110 className="border border-primary/10 rounded-full ml-auto py-5"
111 onClick={() => setValue(value + 20)}
112 >
113 <Plus className="h-4 w-4" />
114 </Button>
115 </div>
116 </TextureCardContent>
117 </TextureCardStyled>
118 )
119}
120
121function CustomSpringExample() {
122 const [value, setValue] = useState(1000)
123 const [mass, setMass] = useState(1)
124 const [stiffness, setStiffness] = useState(100)
125 const [damping, setDamping] = useState(40)
126
127 const handleValueChange =
128 (setter: (value: number) => void, minValue: number) =>
129 (values: number[]) => {
130 const newValue = Math.max(values[0], minValue)
131 setter(newValue)
132 }
133
134 return (
135 <TextureCardStyled className="w-full">
136 <TextureCardHeader className="px-3">
137 <GradientHeading size="sm">Custom Spring Properties</GradientHeading>
138 </TextureCardHeader>
139 <TextureCardContent className="flex flex-col sm:flex-row justify-between items-center gap-8">
140 <div
141 className="text-6xl font-bold mr-auto flex"
142 style={{ minWidth: "150px", textAlign: "right" }}
143 >
144 <AnimatedNumber
145 value={value}
146 mass={mass}
147 stiffness={stiffness}
148 damping={damping}
149 />
150 </div>
151
152 <div className="flex flex-col gap-3 px-2">
153 <Button
154 className="border border-primary/10 rounded-full py-5"
155 onClick={() => setValue(value + 500)}
156 >
157 <Plus className="h-4 w-4 mr-2" />
158 Increase
159 </Button>
160 <Button
161 className="border border-primary/10 rounded-full py-5"
162 disabled={value <= 500}
163 onClick={() => setValue(value - 300)}
164 >
165 <Minus className="h-4 w-4 mr-2" />
166 Decrease
167 </Button>
168 </div>
169 <div className="ml-auto w-full">
170 <div className="flex flex-col gap-4">
171 <div className="ml-auto w-full">
172 <label>Mass: {mass}</label>
173 <Slider
174 defaultValue={[mass]}
175 max={5}
176 step={0.1}
177 onValueChange={handleValueChange(setMass, 0.1)}
178 />
179 </div>
180 <div className="ml-auto w-full">
181 <label>Stiffness: {stiffness}</label>
182 <Slider
183 defaultValue={[stiffness]}
184 max={200}
185 step={1}
186 onValueChange={handleValueChange(setStiffness, 1)}
187 />
188 </div>
189 <div className="ml-auto w-full">
190 <label>Damping: {damping}</label>
191 <Slider
192 defaultValue={[damping]}
193 max={50}
194 step={1}
195 onValueChange={handleValueChange(setDamping, 1)}
196 />
197 </div>
198 </div>
199 </div>
200 </TextureCardContent>
201 </TextureCardStyled>
202 )
203}
204
205export function AnimatedNumberExamples() {
206 return (
207 <div className=" max-w-xl gap-4 py-6 mx-auto ">
208 <div className="w-full flex flex-col gap-2 justify-between">
209 <CustomSpringExample />
210 <div className="flex flex-col sm:flex-row gap-2">
211 <PrecisionExample />
212 <FormatExample />
213 <HooksExample />
214 </div>
215 </div>
216 </div>
217 )
218}