Skip to content

Commit

Permalink
fix: Sync animations between cards
Browse files Browse the repository at this point in the history
  • Loading branch information
essj committed Feb 12, 2025
1 parent 3ea8495 commit 955a823
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,49 @@
import { useEffect, useState } from "react";
import { MarketSummaryCard } from "./MarketSummaryCard";

// TODO: Use fetched values.
const VALUE_DEPOSIT = 1000000;
const VALUE_BORROW = 75000;

export const MarketSummary = () => {
const [countDeposit, setCountDeposit] = useState<number | null>(null);
const [countBorrow, setCountBorrow] = useState<number | null>(null);

const [isAnimating, setIsAnimating] = useState(false);

useEffect(() => {
// Prevent counter animation when number is too low.
// May need to adjust based on the expected maximum value.
const multiplier = 0.1;
const countDepositMinimum = VALUE_DEPOSIT * multiplier;
const countBorrowMinimum = VALUE_BORROW * multiplier;

if (
countDeposit !== null &&
countBorrow !== null &&
countDeposit > countDepositMinimum &&
countBorrow > countBorrowMinimum
) {
setIsAnimating(true);
}
}, [countDeposit, countBorrow]);

return (
<div className="flex flex-col gap-2 md:flex-row">
{/* TODO: Use fetched values. */}
<MarketSummaryCard label="Total Deposits" value={75000} />
<MarketSummaryCard label="Total Borrow" value={1000000} />
<MarketSummaryCard
label="Total Deposits"
value={VALUE_DEPOSIT}
count={countDeposit}
setCount={setCountDeposit}
isAnimating={isAnimating}
/>
<MarketSummaryCard
label="Total Borrow"
value={VALUE_BORROW}
count={countBorrow}
setCount={setCountBorrow}
isAnimating={isAnimating}
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,37 +1,65 @@
import { Skeleton } from "@repo/ui/shadcn-primitives";
import Big from "big.js";
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import { FormattedNumber } from "react-intl";
import { Card } from "../Card";

interface MarketSummaryCardProps {
label: React.ReactNode;
value: number;
count: number | null;
setCount: (value: number) => void;
/**
* Sync animation state between multiple cards.
*/
isAnimating: boolean;
}

export const MarketSummaryCard = ({ label, value }: MarketSummaryCardProps) => {
const [count, setCount] = useState<number | null>(null);

export const MarketSummaryCard = ({
label,
value,
count,
setCount,
isAnimating,
}: MarketSummaryCardProps) => {
useEffect(() => {
let isMounted = true;

const counter = (minimum: number, maximum: number) => {
for (let count = minimum; count <= maximum; count++) {
setTimeout(() => {
if (isMounted) {
setCount(count);
}
}, 5000);
}
let i = minimum;

const updateCount = () => {
if (isMounted && i <= maximum) {
setCount(i);

// Increment counter based on proximity to maximum so multiple cards have synced animations.
const range = maximum - minimum;
const progress = (i - minimum) / range;
const step = Math.max(1, (Math.log10(1 + 9 * progress) * range) / 10);

i += Math.ceil(step);

setTimeout(updateCount, 10);
}

if (isMounted && i > maximum) {
setCount(maximum);
}
};

updateCount();

return () => {
isMounted = false;
};
};

const minimum = value - 10000;
counter(minimum < 0 ? 0 : minimum, value);
counter(0, value);

return () => {
isMounted = false;
};
}, [value]);
}, [value, setCount]);

return (
<Card className="flex flex-col rounded-xl p-5 space-y-1">
Expand All @@ -48,7 +76,7 @@ export const MarketSummaryCard = ({ label, value }: MarketSummaryCardProps) => {
/>
</span>
<span className="text-3xl/8 font-dot w-full absolute top-0 left-0">
<Skeleton isLoading={count === null}>
<Skeleton isLoading={!count || !isAnimating}>
<span>
<FormattedNumber
value={+new Big(count ?? 0).toFixed()}
Expand Down

0 comments on commit 955a823

Please sign in to comment.