diff --git a/src/theme/Footer/Layout/Counter.tsx b/src/theme/Footer/Layout/Counter.tsx new file mode 100644 index 00000000..2891395d --- /dev/null +++ b/src/theme/Footer/Layout/Counter.tsx @@ -0,0 +1,56 @@ +import React, { useEffect, useRef, useState } from "react"; + +interface CounterProps { + value: number; + duration?: number; // animation duration in ms + suffix?: string; // optional suffix like %, K+, etc. +} + +const Counter: React.FC = ({ value, duration = 1500, suffix = "" }) => { + const [count, setCount] = useState(0); + const [hasAnimated, setHasAnimated] = useState(false); + const ref = useRef(null); + + useEffect(() => { + if (!ref.current) return; + + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + if (entry.isIntersecting && !hasAnimated) { + animateCount(); + setHasAnimated(true); + } + }, + { threshold: 0.5 } // Trigger when 50% of element is visible + ); + + observer.observe(ref.current); + return () => observer.disconnect(); + }, [hasAnimated]); + + const animateCount = () => { + const startTime = performance.now(); + + const step = (now: number) => { + const progress = Math.min((now - startTime) / duration, 1); + const current = Math.floor(progress * value); + setCount(current); + + if (progress < 1) { + requestAnimationFrame(step); + } + }; + + requestAnimationFrame(step); + }; + + return ( +
+ {count.toLocaleString()} + {suffix} +
+ ); +}; + +export default Counter; diff --git a/src/theme/Footer/Layout/index.tsx b/src/theme/Footer/Layout/index.tsx index 952bfeed..e999a4d6 100644 --- a/src/theme/Footer/Layout/index.tsx +++ b/src/theme/Footer/Layout/index.tsx @@ -3,6 +3,7 @@ import React, {type ReactNode, useState, useEffect} from 'react'; import Link from "@docusaurus/Link"; import type {Props} from '@theme/Footer/Layout'; import './enhanced-footer.css'; +import Counter from './Counter'; // Dynamic stats interface interface FooterStats { @@ -105,7 +106,9 @@ export default function FooterLayout({
-
{stats.activeUsers}
+
+ +
Active Learners
@@ -117,7 +120,9 @@ export default function FooterLayout({
-
{stats.tutorials}
+
+ +
Tutorials
@@ -129,7 +134,9 @@ export default function FooterLayout({
-
{stats.successRate}
+
+ +
Success Rate