'use client'; import { useEffect, useState, useCallback, useRef } from 'react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; import { getTMDBImageUrl, getGenreNames, type TMDBItem } from '@/lib/tmdb.client'; import { ChevronLeft, ChevronRight, Play } from 'lucide-react'; interface BannerCarouselProps { autoPlayInterval?: number; // 自动播放间隔(毫秒) } export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarouselProps) { const router = useRouter(); const [items, setItems] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const [isLoading, setIsLoading] = useState(true); const [isPaused, setIsPaused] = useState(false); const [skipNextAutoPlay, setSkipNextAutoPlay] = useState(false); // 跳过下一次自动播放 const touchStartX = useRef(0); const touchEndX = useRef(0); const isManualChange = useRef(false); // 标记是否为手动切换 // LocalStorage 缓存配置 const LOCALSTORAGE_KEY = 'tmdb_trending_cache'; const LOCALSTORAGE_DURATION = 24 * 60 * 60 * 1000; // 1天 // 跳转到播放页面 const handlePlay = (title: string) => { router.push(`/play?title=${encodeURIComponent(title)}`); }; // 获取热门内容 useEffect(() => { const fetchTrending = async () => { try { // 先检查 localStorage 缓存 const cached = localStorage.getItem(LOCALSTORAGE_KEY); if (cached) { try { const { data, timestamp } = JSON.parse(cached); const now = Date.now(); // 如果缓存未过期,直接使用 if (now - timestamp < LOCALSTORAGE_DURATION) { setItems(data); setIsLoading(false); return; } } catch (e) { // 缓存解析失败,继续请求 API console.error('解析缓存数据失败:', e); } } // 从 API 获取数据 const response = await fetch('/api/tmdb/trending'); const result = await response.json(); if (result.code === 200 && result.list.length > 0) { setItems(result.list); // 保存到 localStorage try { localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify({ data: result.list, timestamp: Date.now() })); } catch (e) { // localStorage 可能已满,忽略错误 console.error('保存到 localStorage 失败:', e); } } } catch (error) { console.error('获取热门内容失败:', error); } finally { setIsLoading(false); } }; fetchTrending(); }, [LOCALSTORAGE_KEY, LOCALSTORAGE_DURATION]); // 自动播放 useEffect(() => { if (!items.length || isPaused) return; const timer = setInterval(() => { // 如果设置了跳过标志,跳过这一次自动播放 if (skipNextAutoPlay) { setSkipNextAutoPlay(false); return; } setCurrentIndex((prev) => (prev + 1) % items.length); }, autoPlayInterval); return () => clearInterval(timer); }, [items.length, isPaused, autoPlayInterval, skipNextAutoPlay]); const goToPrevious = useCallback(() => { isManualChange.current = true; setSkipNextAutoPlay(true); setCurrentIndex((prev) => (prev - 1 + items.length) % items.length); setTimeout(() => { isManualChange.current = false; }, 100); }, [items.length]); const goToNext = useCallback(() => { isManualChange.current = true; setSkipNextAutoPlay(true); setCurrentIndex((prev) => (prev + 1) % items.length); setTimeout(() => { isManualChange.current = false; }, 100); }, [items.length]); const goToSlide = useCallback((index: number) => { isManualChange.current = true; setSkipNextAutoPlay(true); setCurrentIndex(index); setTimeout(() => { isManualChange.current = false; }, 100); }, []); // 触摸事件处理 const handleTouchStart = (e: React.TouchEvent) => { // 防止在手动切换过程中触发 if (isManualChange.current) return; touchStartX.current = e.touches[0].clientX; touchEndX.current = 0; // 重置结束位置 }; const handleTouchMove = (e: React.TouchEvent) => { // 防止在手动切换过程中触发 if (isManualChange.current) return; touchEndX.current = e.touches[0].clientX; }; const handleTouchEnd = () => { // 防止在手动切换过程中触发 if (isManualChange.current) return; if (!touchStartX.current) return; // 如果有滑动,则执行滑动逻辑 if (touchEndX.current !== 0) { const distance = touchStartX.current - touchEndX.current; const minSwipeDistance = 50; // 最小滑动距离 if (Math.abs(distance) > minSwipeDistance) { if (distance > 0) { // 向左滑动,显示下一张 goToNext(); } else { // 向右滑动,显示上一张 goToPrevious(); } } } // 重置 touchStartX.current = 0; touchEndX.current = 0; }; if (isLoading) { return (
); } if (!items.length) { return null; } const currentItem = items[currentIndex]; return (
setIsPaused(true)} onMouseLeave={() => setIsPaused(false)} onTouchStart={handleTouchStart} onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} onClick={() => { // 移动端点击整个轮播图跳转 if (window.innerWidth < 768) { handlePlay(currentItem.title); } }} > {/* 背景图片 */}
{items.map((item, index) => (
{item.title} {/* 渐变遮罩 */}
))}
{/* 内容信息 */}

{currentItem.title}

{currentItem.vote_average.toFixed(1)} {getGenreNames(currentItem.genre_ids, 3).map(genre => ( {genre} ))} {currentItem.release_date && ( {currentItem.release_date} )}
{/* PC端播放按钮 */} {currentItem.overview && (

{currentItem.overview}

)}
{/* 左右切换按钮 - 只在桌面端显示 */} {/* 指示器 */}
{items.map((_, index) => (
); }