feat(don): goal widget animations

This commit is contained in:
2026-06-15 13:41:13 +07:00 Unverified
parent be0e496b45
commit 268d8f176d
2 changed files with 22 additions and 10 deletions
+20 -9
View File
@@ -3,6 +3,7 @@
import { sse } from "@/lib/db/sse-endpoints"; import { sse } from "@/lib/db/sse-endpoints";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { getDonateBar } from "./api"; import { getDonateBar } from "./api";
import { motion } from "motion/react";
type Data = Awaited<ReturnType<typeof getDonateBar>>; type Data = Awaited<ReturnType<typeof getDonateBar>>;
@@ -23,10 +24,11 @@ export default function TopDonateWidget() {
refresh: () => location.reload(), refresh: () => location.reload(),
}); });
setInterval(update, 300000); const backupInterval = setInterval(update, 300000);
return () => { return () => {
clean(); clean();
clearInterval(backupInterval);
}; };
}, []); }, []);
@@ -39,22 +41,28 @@ export default function TopDonateWidget() {
count = 0; count = 0;
const el = textRef.current, const el = textRef.current,
parent = el.parentElement!, parent = el.parentElement!,
key = `${bar?.goal}${bar?.amount}`; key = `${bar?.goal}`;
if (stableSize.current[key]) { if (stableSize.current[key]) {
el.classList.add("opacity-100");
el.style.fontSize = `${stableSize.current[key]}px`; el.style.fontSize = `${stableSize.current[key]}px`;
return; return;
} }
el.classList.remove("opacity-100");
const interval = setInterval(() => { const interval = setInterval(() => {
console.log(el.scrollHeight, parent.clientHeight); console.log(el.scrollHeight, parent.clientHeight);
fontSize = (fontSize * parent.clientHeight) / el.scrollHeight; fontSize = (fontSize * parent.clientHeight) / el.scrollHeight;
el.style.fontSize = `${fontSize}px`; el.style.fontSize = `${fontSize}px`;
count++; count++;
if (count > 20) { if (count > 10) {
stableSize.current[key] = fontSize; stableSize.current[key] = fontSize;
el.classList.add("opacity-100");
clearInterval(interval); clearInterval(interval);
} }
}, 100); }, 100);
return () => clearInterval(interval); return () => {
el.classList.add("opacity-100");
clearInterval(interval);
};
}, [bar, resizeTick]); }, [bar, resizeTick]);
useEffect(() => { useEffect(() => {
@@ -68,17 +76,20 @@ export default function TopDonateWidget() {
}, [bar]); }, [bar]);
return bar && bar.amount !== null && bar.goal ? ( return bar && bar.amount !== null && bar.goal ? (
<div className="relative h-full w-full rounded-full bg-[#252525]"> <div className="relative h-full max-h-[20svw] w-full rounded-full bg-[#252525]">
<div className="absolute inset-2 rounded-full bg-black" /> <div className="absolute inset-2 rounded-full bg-black" />
<div <motion.div
className="absolute inset-1.5 rounded-full bg-linear-to-b from-[#FD0000] to-[#830000]" className="absolute inset-1.5 rounded-full bg-linear-to-b from-[#FD0000] to-[#830000]"
style={{ animate={{
right: `${100 - Math.min(1, Number(bar.amount) / bar.goal) * 100}%`, width: `calc(${Math.min(1, Number(bar.amount) / bar.goal) * 100}% - 12px)`,
}}
initial={{
width: 0,
}} }}
/> />
<div <div
ref={textRef} ref={textRef}
className="absolute top-1/2 right-8 -translate-y-1/2 pb-1 font-bold" className="absolute top-1/2 right-8 -translate-y-1/2 pb-1 font-bold opacity-0 transition-opacity"
> >
{bar.goal}฿ {bar.goal}฿
</div> </div>
+2 -1
View File
@@ -22,10 +22,11 @@ export default function TopDonateWidget() {
refresh: () => location.reload(), refresh: () => location.reload(),
}); });
setInterval(update, 300000); const backupInterval = setInterval(update, 300000);
return () => { return () => {
clean(); clean();
clearInterval(backupInterval);
}; };
}, []); }, []);