diff --git a/src/components/AggregateCard.tsx b/src/components/AggregateCard.tsx index d18248a..e48249a 100644 --- a/src/components/AggregateCard.tsx +++ b/src/components/AggregateCard.tsx @@ -1,10 +1,8 @@ -/* eslint-disable react-hooks/exhaustive-deps */ - import { LinkIcon } from 'lucide-react'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { ImagePlaceholder } from '@/components/ImagePlaceholder'; @@ -71,55 +69,6 @@ const AggregateCard: React.FC = ({ const [isLoaded, setIsLoaded] = useState(false); const router = useRouter(); - // 视差位置偏移 - const [parallax, setParallax] = useState({ x: 0, y: 0 }); - const cardRef = useRef(null); - - // 图片视差效果 - useEffect(() => { - let requestId: number | null = null; - let lastX = 0; - let lastY = 0; - - const handleMouseMove = (e: MouseEvent) => { - if (!cardRef.current) return; - - const rect = cardRef.current.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - if (Math.abs(x - lastX) > 5 || Math.abs(y - lastY) > 5) { - lastX = x; - lastY = y; - - if (requestId) cancelAnimationFrame(requestId); - requestId = requestAnimationFrame(() => { - const xParallax = (x / rect.width - 0.5) * 10; - const yParallax = (y / rect.height - 0.5) * 10; - setParallax({ x: xParallax, y: yParallax }); - }); - } - }; - - const handleMouseLeave = () => { - if (requestId) cancelAnimationFrame(requestId); - setParallax({ x: 0, y: 0 }); - }; - - if (cardRef.current) { - cardRef.current.addEventListener('mousemove', handleMouseMove); - cardRef.current.addEventListener('mouseleave', handleMouseLeave); - } - - return () => { - if (cardRef.current) { - cardRef.current.removeEventListener('mousemove', handleMouseMove); - cardRef.current.removeEventListener('mouseleave', handleMouseLeave); - } - if (requestId) cancelAnimationFrame(requestId); - }; - }, []); - // 统计 items 中出现次数最多的(非 0) douban_id,用于跳转豆瓣页面 const mostFrequentDoubanId = useMemo(() => { const countMap = new Map(); @@ -175,10 +124,7 @@ const AggregateCard: React.FC = ({ year ? `&year=${encodeURIComponent(year)}` : '' }&type=${mostFrequentEpisodes > 1 ? 'tv' : 'movie'}`} > -
+
{/* 封面图片 2:3 */}
{/* 图片占位符 - 骨架屏效果 */} @@ -188,7 +134,7 @@ const AggregateCard: React.FC = ({ src={first.poster} alt={first.title} fill - className={`object-cover transition-all duration-700 cubic-bezier(0.34,1.56,0.64,1) group-hover:scale-[1.05] + className={`object-cover transition-transform duration-500 cubic-bezier(0.4,0,0.2,1) group-hover:scale-110 ${ isLoaded ? 'opacity-100 scale-100' @@ -197,13 +143,6 @@ const AggregateCard: React.FC = ({ onLoadingComplete={() => setIsLoaded(true)} referrerPolicy='no-referrer' priority={false} - style={{ - transform: `scale(1.05) translate(${parallax.x}px, ${parallax.y}px)`, - transition: 'transform 0.5s cubic-bezier(0.34,1.56,0.64,1)', - willChange: 'transform', - backfaceVisibility: 'hidden', - perspective: '1000px', - }} /> {/* Hover 效果层 */} @@ -211,8 +150,8 @@ const AggregateCard: React.FC = ({ {/* 播放按钮 */}
{ @@ -260,10 +199,8 @@ const AggregateCard: React.FC = ({
{/* 标题 */} - - - {first.title} - + + {first.title}
diff --git a/src/components/DemoCard.tsx b/src/components/DemoCard.tsx index b0003bc..f5a19ee 100644 --- a/src/components/DemoCard.tsx +++ b/src/components/DemoCard.tsx @@ -1,9 +1,7 @@ -/* eslint-disable react-hooks/exhaustive-deps */ - import { Link as LinkIcon, Search } from 'lucide-react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useRef, useState } from 'react'; import { ImagePlaceholder } from '@/components/ImagePlaceholder'; @@ -15,15 +13,12 @@ interface DemoCardProps { type?: string; } -// 优化的搜索图标组件,添加更多动画 function SearchCircle({ className = '', fillColor = 'none', - isHovered = false, }: { className?: string; fillColor?: string; - isHovered?: boolean; }) { return (
- +
@@ -66,59 +49,8 @@ function SearchCircle({ const DemoCard = ({ id, title, poster, rate, type }: DemoCardProps) => { const [hover, setHover] = useState(false); const [isLoaded, setIsLoaded] = useState(false); - const [parallax, setParallax] = useState({ x: 0, y: 0 }); const router = useRouter(); const imgRef = useRef(null); - const cardRef = useRef(null); - - // 图片视差效果 - 优化 Safari 性能 - useEffect(() => { - let requestId: number | null = null; - let lastX = 0; - let lastY = 0; - - const handleMouseMove = (e: MouseEvent) => { - if (!cardRef.current) return; - - const rect = cardRef.current.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - // 只有当移动超过阈值时才更新视差,减少 Safari 中的重绘 - if (Math.abs(x - lastX) > 5 || Math.abs(y - lastY) > 5) { - lastX = x; - lastY = y; - - // 使用 requestAnimationFrame 优化性能 - if (requestId) cancelAnimationFrame(requestId); - requestId = requestAnimationFrame(() => { - // 计算视差偏移量 (-5 到 5 之间) - const xParallax = (x / rect.width - 0.5) * 10; - const yParallax = (y / rect.height - 0.5) * 10; - - setParallax({ x: xParallax, y: yParallax }); - }); - } - }; - - const handleMouseLeave = () => { - if (requestId) cancelAnimationFrame(requestId); - setParallax({ x: 0, y: 0 }); - }; - - if (cardRef.current) { - cardRef.current.addEventListener('mousemove', handleMouseMove); - cardRef.current.addEventListener('mouseleave', handleMouseLeave); - } - - return () => { - if (cardRef.current) { - cardRef.current.removeEventListener('mousemove', handleMouseMove); - cardRef.current.removeEventListener('mouseleave', handleMouseLeave); - } - if (requestId) cancelAnimationFrame(requestId); - }; - }, []); const handleClick = () => { router.push( @@ -128,19 +60,11 @@ const DemoCard = ({ id, title, poster, rate, type }: DemoCardProps) => { return (
{/* 海报图片区域 */} -
+
{/* 图片占位符 - 骨架屏效果 */} @@ -150,28 +74,20 @@ const DemoCard = ({ id, title, poster, rate, type }: DemoCardProps) => { alt={title} fill ref={imgRef} - className={`object-cover transition-all duration-700 cubic-bezier(0.34, 1.56, 0.64, 1) group-hover:scale-[1.05] + className={`object-cover transition-transform duration-500 cubic-bezier(0.4,0,0.2,1) group-hover:scale-110 ${ isLoaded - ? 'opacity-100 scale-100 blur-0' - : 'opacity-0 scale-95 blur-sm' + ? 'opacity-100 scale-100' + : 'opacity-0 scale-95' }`} onLoadingComplete={() => setIsLoaded(true)} referrerPolicy='no-referrer' priority={false} - style={{ - transform: `scale(1.05) translate(${parallax.x}px, ${parallax.y}px)`, - transition: 'transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)', - // 修复 Safari 中可能的渲染问题 - willChange: 'transform', - backfaceVisibility: 'hidden', - perspective: '1000px', - }} /> {/* 评分徽章 - 暗色模式优化 */} {rate && ( -
+
{rate} @@ -179,18 +95,15 @@ const DemoCard = ({ id, title, poster, rate, type }: DemoCardProps) => { )} {/* 悬浮层 - 搜索按钮 */} -
+
setHover(true)} onMouseLeave={() => setHover(false)} - className={`transition-all duration-300 cubic-bezier(0.34, 1.56, 0.64, 1) ${ - hover ? 'scale-110' : 'scale-90' + className={`transition-all duration-300 cubic-bezier(0.4, 0, 0.2, 1) ${ + hover ? 'scale-110 rotate-12' : 'scale-90' }`} > - +
@@ -200,16 +113,16 @@ const DemoCard = ({ id, title, poster, rate, type }: DemoCardProps) => { target='_blank' rel='noopener noreferrer' onClick={(e) => e.stopPropagation()} - className='absolute top-2 left-2 scale-90 group-hover:scale-100 opacity-0 group-hover:opacity-100 transition-all duration-500 cubic-bezier(0.34, 1.56, 0.64, 1) group-hover:translate-y-0 translate-y-[-10px]' + className='absolute top-2 left-2 scale-90 group-hover:scale-100 opacity-0 group-hover:opacity-100 transition-all duration-300 cubic-bezier(0.4, 0, 0.2, 1)' > -
+
{/* 信息层 - 暗色模式优化 */} - + {title} diff --git a/src/components/VideoCard.tsx b/src/components/VideoCard.tsx index f289a40..4c92e56 100644 --- a/src/components/VideoCard.tsx +++ b/src/components/VideoCard.tsx @@ -1,10 +1,8 @@ -/* eslint-disable react-hooks/exhaustive-deps */ - import { Heart, Link as LinkIcon } from 'lucide-react'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { deletePlayRecord, isFavorited, toggleFavorite } from '@/lib/db.client'; @@ -95,8 +93,6 @@ export default function VideoCard({ const [favorited, setFavorited] = useState(false); const [isLoaded, setIsLoaded] = useState(false); const [isDeleting, setIsDeleting] = useState(false); - const [parallax, setParallax] = useState({ x: 0, y: 0 }); - const cardRef = useRef(null); const router = useRouter(); // 检查初始收藏状态 @@ -152,52 +148,6 @@ export default function VideoCard({ } }; - // 图片视差效果 - 参考 DemoCard,优化 Safari 性能 - useEffect(() => { - let requestId: number | null = null; - let lastX = 0; - let lastY = 0; - - const handleMouseMove = (e: MouseEvent) => { - if (!cardRef.current) return; - - const rect = cardRef.current.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - // 只有在移动超过阈值时才更新,减少重绘 - if (Math.abs(x - lastX) > 5 || Math.abs(y - lastY) > 5) { - lastX = x; - lastY = y; - - if (requestId) cancelAnimationFrame(requestId); - requestId = requestAnimationFrame(() => { - const xParallax = (x / rect.width - 0.5) * 10; - const yParallax = (y / rect.height - 0.5) * 10; - setParallax({ x: xParallax, y: yParallax }); - }); - } - }; - - const handleMouseLeave = () => { - if (requestId) cancelAnimationFrame(requestId); - setParallax({ x: 0, y: 0 }); - }; - - if (cardRef.current) { - cardRef.current.addEventListener('mousemove', handleMouseMove); - cardRef.current.addEventListener('mouseleave', handleMouseLeave); - } - - return () => { - if (cardRef.current) { - cardRef.current.removeEventListener('mousemove', handleMouseMove); - cardRef.current.removeEventListener('mouseleave', handleMouseLeave); - } - if (requestId) cancelAnimationFrame(requestId); - }; - }, []); - const hideCheckCircle = from === 'favorites' || from === 'search'; const alwaysShowHeart = from !== 'favorites'; @@ -208,8 +158,7 @@ export default function VideoCard({ )}${year ? `&year=${year}` : ''}${from ? `&from=${from}` : ''}`} >
@@ -222,7 +171,7 @@ export default function VideoCard({ src={poster} alt={title} fill - className={`object-cover transition-all duration-700 cubic-bezier(0.34,1.56,0.64,1) group-hover:scale-[1.05] + className={`object-cover transition-transform duration-500 cubic-bezier(0.4,0,0.2,1) group-hover:scale-110 ${ isLoaded ? 'opacity-100 scale-100' @@ -231,13 +180,6 @@ export default function VideoCard({ onLoadingComplete={() => setIsLoaded(true)} referrerPolicy='no-referrer' priority={false} - style={{ - transform: `scale(1.05) translate(${parallax.x}px, ${parallax.y}px)`, - transition: 'transform 0.5s cubic-bezier(0.34,1.56,0.64,1)', - willChange: 'transform', - backfaceVisibility: 'hidden', - perspective: '1000px', - }} /> {/* Hover 效果层 */}
{ @@ -345,16 +287,14 @@ export default function VideoCard({
)} - {/* 信息层 - 与 DemoCard 对齐的动画 */} - - - {title} - + {/* 信息层 */} + + {title} {/* 来源信息 */} {source && ( - + {source_name}