ui: 滑卡互动增强 — like 徽章动画、分类标签可读性、进度数字区分

- RestaurantCard: like 徽章实时弹跳动画,首次出现滑入效果
- RestaurantCard: 分类标签改为白底黑字,图片上始终清晰
- SwipeDeck: 进度数字加背景色块包裹,与用户名视觉分离
This commit is contained in:
2026-02-26 16:32:00 +08:00
parent 9759db54ca
commit 37eb7f07d7
2 changed files with 45 additions and 12 deletions
+36 -8
View File
@@ -1,6 +1,7 @@
"use client";
import { useCallback, useState, useEffect } from "react";
import { useCallback, useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Star, MapPin, Clock, ExternalLink, Flame, Bookmark, ChevronLeft, ChevronRight } from "lucide-react";
import { Restaurant } from "@/types";
import { getUserId, isRegistered } from "@/lib/userId";
@@ -118,6 +119,17 @@ function ImageGallery({ images, name }: { images: string[]; name: string }) {
export default function RestaurantCard({ restaurant, likeCount = 0 }: RestaurantCardProps) {
const [favorited, setFavorited] = useState(false);
const [likeBounce, setLikeBounce] = useState(false);
const prevLikeRef = useRef(likeCount);
useEffect(() => {
if (likeCount > prevLikeRef.current) {
setLikeBounce(true);
const t = setTimeout(() => setLikeBounce(false), 600);
return () => clearTimeout(t);
}
prevLikeRef.current = likeCount;
}, [likeCount]);
const images = restaurant.images?.filter(Boolean);
const hasImage = images && images.length > 0;
@@ -156,16 +168,32 @@ export default function RestaurantCard({ restaurant, likeCount = 0 }: Restaurant
<div className="absolute bottom-3 left-4 flex items-center gap-1.5">
{restaurant.category && (
<span className="rounded-full bg-elevated/90 px-2.5 py-0.5 text-xs font-semibold text-gray-200 shadow-sm backdrop-blur-sm">
<span className="rounded-full bg-white/80 px-2.5 py-0.5 text-xs font-semibold text-gray-800 shadow-sm backdrop-blur-sm">
{restaurant.category}
</span>
)}
{likeCount > 0 && (
<span className="flex items-center gap-0.5 rounded-full bg-rose-500/90 px-2 py-0.5 text-xs font-semibold text-white shadow-sm backdrop-blur-sm">
<Flame size={11} />
{likeCount}
</span>
)}
<AnimatePresence>
{likeCount > 0 && (
<motion.span
key="like-badge"
className="flex items-center gap-0.5 rounded-full bg-rose-500/90 px-2 py-0.5 text-xs font-semibold text-white shadow-sm backdrop-blur-sm"
initial={{ opacity: 0, scale: 0.5, x: -8 }}
animate={{
opacity: 1,
scale: likeBounce ? [1, 1.3, 1] : 1,
x: 0,
}}
exit={{ opacity: 0, scale: 0.5 }}
transition={likeBounce
? { scale: { duration: 0.4, ease: "easeInOut" }, default: { type: "spring", stiffness: 400, damping: 20 } }
: { type: "spring", stiffness: 400, damping: 20 }
}
>
<Flame size={11} className={likeBounce ? "animate-pulse" : ""} />
{likeCount}
</motion.span>
)}
</AnimatePresence>
</div>
</div>