mirror of
https://github.com/nagaoo0/HabbitGrid.git
synced 2026-01-12 07:54:53 +00:00
Add counter animations
This commit is contained in:
34
src/components/AnimatedCounter.jsx
Normal file
34
src/components/AnimatedCounter.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
/**
|
||||
* AnimatedCounter
|
||||
* Animates a number from 0 (or start) to the target value progressively.
|
||||
* Usage: <AnimatedCounter value={targetNumber} duration={1000} />
|
||||
*/
|
||||
function AnimatedCounter({ value, duration = 1000, start = 0, format = v => v }) {
|
||||
const [displayValue, setDisplayValue] = useState(start);
|
||||
const rafRef = useRef();
|
||||
const startRef = useRef(start);
|
||||
const valueRef = useRef(value);
|
||||
|
||||
useEffect(() => {
|
||||
startRef.current = displayValue;
|
||||
valueRef.current = value;
|
||||
let startTime;
|
||||
function animate(ts) {
|
||||
if (!startTime) startTime = ts;
|
||||
const progress = Math.min((ts - startTime) / duration, 1);
|
||||
const current = Math.round(startRef.current + (valueRef.current - startRef.current) * progress);
|
||||
setDisplayValue(current);
|
||||
if (progress < 1) {
|
||||
rafRef.current = requestAnimationFrame(animate);
|
||||
}
|
||||
}
|
||||
rafRef.current = requestAnimationFrame(animate);
|
||||
return () => cancelAnimationFrame(rafRef.current);
|
||||
}, [value, duration]);
|
||||
|
||||
return <span>{format(displayValue)}</span>;
|
||||
}
|
||||
|
||||
export default AnimatedCounter;
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { GitBranch } from 'lucide-react';
|
||||
import { getCachedGitActivity } from '../lib/git';
|
||||
import { formatDate, isToday, getWeekdayLabel } from '../lib/utils-habit';
|
||||
import AnimatedCounter from './AnimatedCounter';
|
||||
|
||||
const GitActivityGrid = () => {
|
||||
const [{ dailyCounts }, setData] = useState(() => getCachedGitActivity());
|
||||
@@ -73,8 +74,13 @@ const GitActivityGrid = () => {
|
||||
pointerEvents: 'none',
|
||||
visibility: isFuture ? 'hidden' : 'visible',
|
||||
}}
|
||||
title={`${dateStr} • ${count} commits`}
|
||||
/>
|
||||
title={`${dateStr} • `}
|
||||
>
|
||||
{/* Animated commit count for tooltip */}
|
||||
<span style={{ display: 'none' }}>
|
||||
<AnimatedCounter value={count} duration={600} /> commits
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { motion } from 'framer-motion';
|
||||
import { ChevronRight, Flame } from 'lucide-react';
|
||||
import { Button } from './ui/button';
|
||||
import MiniGrid from './MiniGrid';
|
||||
import AnimatedCounter from './AnimatedCounter';
|
||||
|
||||
const HabitCard = ({ habit, onUpdate }) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -27,10 +28,10 @@ const HabitCard = ({ habit, onUpdate }) => {
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-1">
|
||||
<Flame className="w-4 h-4 text-orange-500" />
|
||||
<span>{habit.currentStreak || 0} day streak</span>
|
||||
<span><AnimatedCounter value={habit.currentStreak || 0} duration={800} /> day streak</span>
|
||||
</div>
|
||||
<span>•</span>
|
||||
<span>Personal Record: {habit.longestStreak || 0} days</span>
|
||||
<span>Personal Record: <AnimatedCounter value={habit.longestStreak || 0} duration={800} /> days</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user