feat: like button animation

This commit is contained in:
trafficlunar 2025-04-26 14:36:06 +01:00
parent e17b3908ae
commit a2ea5f6aec
2 changed files with 42 additions and 3 deletions

View file

@ -1,5 +1,24 @@
@import "tailwindcss"; @import "tailwindcss";
@theme {
--animate-like: like 0.5s ease;
@keyframes like {
0% {
transform: scale(1);
}
30% {
transform: scale(1.25);
}
60% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
}
body { body {
@apply bg-amber-50; @apply bg-amber-50;

View file

@ -1,8 +1,8 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useState } from "react";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { Icon } from "@iconify/react"; import { Icon, loadIcons } from "@iconify/react";
import { abbreviateNumber } from "@/lib/abbreviation"; import { abbreviateNumber } from "@/lib/abbreviation";
interface Props { interface Props {
@ -18,6 +18,7 @@ interface Props {
export default function LikeButton({ likes, isLiked, miiId, isLoggedIn, disabled, abbreviate, big }: Props) { export default function LikeButton({ likes, isLiked, miiId, isLoggedIn, disabled, abbreviate, big }: Props) {
const [isLikedState, setIsLikedState] = useState(isLiked); const [isLikedState, setIsLikedState] = useState(isLiked);
const [likesState, setLikesState] = useState(likes); const [likesState, setLikesState] = useState(likes);
const [isAnimating, setIsAnimating] = useState(false);
const onClick = async () => { const onClick = async () => {
if (disabled) return; if (disabled) return;
@ -26,6 +27,12 @@ export default function LikeButton({ likes, isLiked, miiId, isLoggedIn, disabled
setIsLikedState(!isLikedState); setIsLikedState(!isLikedState);
setLikesState(isLikedState ? likesState - 1 : likesState + 1); setLikesState(isLikedState ? likesState - 1 : likesState + 1);
// Trigger animation
if (!isLikedState) {
setIsAnimating(true);
setTimeout(() => setIsAnimating(false), 1000); // match animation duration
}
const response = await fetch(`/api/mii/${miiId}/like`, { method: "PATCH" }); const response = await fetch(`/api/mii/${miiId}/like`, { method: "PATCH" });
if (response.ok) { if (response.ok) {
@ -38,9 +45,22 @@ export default function LikeButton({ likes, isLiked, miiId, isLoggedIn, disabled
} }
}; };
// Preload like button icons
useEffect(() => {
loadIcons(["icon-park-solid:like", "icon-park-outline:like"]);
}, []);
return ( return (
<button onClick={onClick} className={`flex items-center gap-2 text-red-400 ${disabled ? "" : "cursor-pointer"} ${big ? "text-3xl" : "text-xl"}`}> <button onClick={onClick} className={`flex items-center gap-2 text-red-400 ${disabled ? "" : "cursor-pointer"} ${big ? "text-3xl" : "text-xl"}`}>
<Icon icon={isLikedState ? "icon-park-solid:like" : "icon-park-outline:like"} /> <div className="relative">
<Icon icon={isLikedState ? "icon-park-solid:like" : "icon-park-outline:like"} className={`${isAnimating ? "animate-like " : ""}`} />
<div
className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 size-6 rounded-full bg-red-400/0 ${
isAnimating ? "bg-red-400/40 animate-ping" : ""
}`}
></div>
</div>
<span>{abbreviate ? abbreviateNumber(likesState) : likesState}</span> <span>{abbreviate ? abbreviateNumber(likesState) : likesState}</span>
</button> </button>
); );