From a0a783c42523459e7c9d2e093e58f39feda1a468 Mon Sep 17 00:00:00 2001 From: Rocktim Saikia Date: Mon, 29 Jul 2024 01:20:28 +0530 Subject: [PATCH] feat: add animated-counter component for dynamic values closes #1 --- README.md | 2 +- src/App.tsx | 13 ++++++-- src/components/animated-counter.tsx | 46 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 src/components/animated-counter.tsx diff --git a/README.md b/README.md index f05d49e..3bafd8c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Compound Interest Calculator -A minimal compound interest calculator that displays the total return of a compounded investment over a specified time period. +A minimal compound interest investment calculator that displays the total returns over a specified time period, interest rate and compounding frequency. [![Deployment](https://github.com/rocktimsaikia/compound-interest-calculator/actions/workflows/deploy.yml/badge.svg)](https://github.com/rocktimsaikia/compound-interest-calculator/actions/workflows/deploy.yml) diff --git a/src/App.tsx b/src/App.tsx index b23de99..0ae6f06 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; import compounder from "compounder"; +import AnimatedCounter from "./components/animated-counter"; type CompoundingFrequency = "quarterly" | "semi-annually" | "annually"; @@ -196,15 +197,21 @@ export default function App() {

Principal Amount

-

₹{principalAmount.toLocaleString("en-IN")}

+
+ ₹ +

Total Interest

-

₹{totalInterest.toLocaleString("en-IN")}

+

+ ₹ +

Total Amount

-

₹{totalAmount.toLocaleString("en-IN")}

+

+ ₹ +

diff --git a/src/components/animated-counter.tsx b/src/components/animated-counter.tsx new file mode 100644 index 0000000..325fea7 --- /dev/null +++ b/src/components/animated-counter.tsx @@ -0,0 +1,46 @@ +import { useEffect, useRef, useState } from "react"; + +interface Props { + start: number; + duration?: number; + locale?: string; +} + +export default function AnimatedCounter({ + start, + duration = 200, + locale, +}: Props) { + const [count, setCount] = useState(start); // State to keep track of the current count + const prevStartRef = useRef(start); // Ref to store the previous start value + + // Update the previous start value when start changes + useEffect(() => { + prevStartRef.current = start; + }, [start]); + + // Previous start value + const prevStart = prevStartRef.current; + + // Animate the count when the start value changes + useEffect(() => { + // If start value changes, animate the count + if (prevStart !== start) { + let startTimestamp: DOMHighResTimeStamp; + + const step = (timestamp: DOMHighResTimeStamp) => { + if (!startTimestamp) startTimestamp = timestamp; + const progress = Math.min((timestamp - startTimestamp) / duration, 1); // Calculate progress + setCount(Math.floor(progress * (start - prevStart) + prevStart)); // Update count based on progress + + if (progress < 1) { + window.requestAnimationFrame(step); // Continue animation + } + }; + + window.requestAnimationFrame(step); // Start animation + } + }, [start, duration]); + + return {locale ? count.toLocaleString(locale) : count}; // Render the current count +}