Merge pull request #48 from OuOumm/main

feat(components): 添加图片骨架屏组件并优化卡片加载体验
This commit is contained in:
senshinya
2025-07-03 22:09:06 +08:00
committed by GitHub
4 changed files with 96 additions and 16 deletions

View File

@@ -6,6 +6,8 @@ import React, { useEffect, useState } from 'react';
import { deletePlayRecord, isFavorited, toggleFavorite } from '@/lib/db.client';
import { ImagePlaceholder } from '@/components/ImagePlaceholder';
interface VideoCardProps {
id: string;
source: string;
@@ -89,6 +91,7 @@ export default function VideoCard({
}: VideoCardProps) {
const [playHover, setPlayHover] = useState(false);
const [favorited, setFavorited] = useState(false);
const [isLoaded, setIsLoaded] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const router = useRouter();
@@ -161,15 +164,24 @@ export default function VideoCard({
>
{/* 海报图片容器 */}
<div className='relative aspect-[2/3] w-full overflow-hidden rounded-md group-hover:scale-[1.02] transition-all duration-400 cubic-bezier(0.4,0,0.2,1)'>
{/* 图片占位符 - 骨架屏效果 */}
<ImagePlaceholder aspectRatio='aspect-[2/3]' />
<Image
src={poster}
alt={title}
fill
className='object-cover transition-transform duration-500 cubic-bezier(0.4,0,0.2,1) group-hover:scale-110'
loading='lazy'
className={`object-cover transition-transform duration-500 cubic-bezier(0.4,0,0.2,1) group-hover:scale-110
${
isLoaded
? 'opacity-100 scale-100'
: 'opacity-0 scale-95'
}`}
onLoadingComplete={() => setIsLoaded(true)}
referrerPolicy='no-referrer'
priority={false}
/>
{/* Hover 效果层 */}
<div
className={`absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent ${
@@ -230,7 +242,6 @@ export default function VideoCard({
</span>
</div>
</div>
{/* 继续观看 - 集数矩形展示框 */}
{episodes && episodes > 1 && currentEpisode && (
<div className='absolute top-2 right-2 min-w-[1.875rem] h-5 sm:h-7 sm:min-w-[2.5rem] bg-green-500/90 dark:bg-green-600/90 rounded-md flex items-center justify-center px-2 shadow-md text-[0.55rem] sm:text-xs'>
@@ -243,7 +254,6 @@ export default function VideoCard({
</span>
</div>
)}
{/* 搜索非聚合 - 集数圆形展示框 */}
{episodes && episodes > 1 && !currentEpisode && (
<div className='absolute top-2 right-2 w-4 h-4 sm:w-7 sm:h-7 rounded-full bg-green-500/90 dark:bg-green-600/90 flex items-center justify-center shadow-md text-[0.55rem] sm:text-xs'>
@@ -252,7 +262,6 @@ export default function VideoCard({
</span>
</div>
)}
{/* 豆瓣链接按钮 */}
{douban_id && from === 'search' && (
<a