diff --git a/src/components/BannerCarousel.tsx b/src/components/BannerCarousel.tsx index cc2dae6..5d22ee5 100644 --- a/src/components/BannerCarousel.tsx +++ b/src/components/BannerCarousel.tsx @@ -1,11 +1,21 @@ 'use client'; -import { ChevronLeft, ChevronRight, Play, Volume2, VolumeX } from 'lucide-react'; +import { + ChevronLeft, + ChevronRight, + Play, + Volume2, + VolumeX, +} from 'lucide-react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; -import { useCallback, useEffect, useRef,useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; -import { type TMDBItem,getGenreNames, getTMDBImageUrl } from '@/lib/tmdb.client'; +import { + type TMDBItem, + getGenreNames, + getTMDBImageUrl, +} from '@/lib/tmdb.client'; import { getDoubanDetail } from '@/lib/douban.client'; import ProxyImage from '@/components/ProxyImage'; @@ -15,6 +25,21 @@ interface BannerCarouselProps { delayLoad?: boolean; // 是否延迟加载(等页面加载完毕后再加载) } +type HomeBannerHeightScale = '1' | '1.5' | '2'; + +const bannerHeightClassMap: Record = { + '1': 'h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px]', + '1.5': 'h-[300px] sm:h-[450px] md:h-[600px] lg:h-[750px]', + '2': 'h-[400px] sm:h-[600px] md:h-[800px] lg:h-[1000px]', +}; + +const getSavedBannerHeightScale = (): HomeBannerHeightScale => { + if (typeof window === 'undefined') return '1'; + + const saved = localStorage.getItem('homeBannerHeightScale'); + return saved === '1.5' || saved === '2' ? saved : '1'; +}; + // 扩展TMDBItem类型以支持TX数据源的额外字段 interface BannerItem extends TMDBItem { subtitle?: string; // TX数据源的子标题 @@ -23,7 +48,10 @@ interface BannerItem extends TMDBItem { genres?: string[]; // 豆瓣数据源的类型标签 } -export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = false }: BannerCarouselProps) { +export default function BannerCarousel({ + autoPlayInterval = 5000, + delayLoad = false, +}: BannerCarouselProps) { const router = useRouter(); const [items, setItems] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); @@ -36,6 +64,8 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa const [dataSource, setDataSource] = useState(''); // 当前数据源 const [trailersLoaded, setTrailersLoaded] = useState(false); // 预告片是否已加载 const [isMuted, setIsMuted] = useState(true); // 视频是否静音(默认静音) + const [bannerHeightScale, setBannerHeightScale] = + useState('1'); // 轮播图高度倍率 const videoRef = useRef(null); // 视频元素引用 const videoRefs = useRef>(new Map()); // 所有视频元素的引用 const touchStartX = useRef(0); @@ -95,6 +125,20 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa if (setting !== null) { setEnableTrailers(setting === 'true'); } + + setBannerHeightScale(getSavedBannerHeightScale()); + + const handleHomeModulesUpdated = () => { + setBannerHeightScale(getSavedBannerHeightScale()); + }; + + window.addEventListener('homeModulesUpdated', handleHomeModulesUpdated); + return () => { + window.removeEventListener( + 'homeModulesUpdated', + handleHomeModulesUpdated + ); + }; }, []); // 延迟加载:等待页面加载完毕后再开始加载轮播图数据 @@ -204,10 +248,13 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa // 保存到 localStorage(使用数据源特定的key) try { - localStorage.setItem(cacheKey, JSON.stringify({ - data: result.list, - timestamp: Date.now() - })); + localStorage.setItem( + cacheKey, + JSON.stringify({ + data: result.list, + timestamp: Date.now(), + }) + ); } catch (e) { // localStorage 可能已满,忽略错误 console.error('保存到 localStorage 失败:', e); @@ -227,7 +274,12 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa // 前端获取豆瓣预告片 useEffect(() => { // 只有在启用预告片、数据源是豆瓣、有数据且未加载预告片时才执行 - if (!enableTrailers || dataSource !== 'Douban' || items.length === 0 || trailersLoaded) { + if ( + !enableTrailers || + dataSource !== 'Douban' || + items.length === 0 || + trailersLoaded + ) { return; } @@ -241,9 +293,10 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa const detail = await getDoubanDetail(item.id.toString()); // 获取预告片链接(取第一个) - const trailerUrl = detail.trailers && detail.trailers.length > 0 - ? detail.trailers[0].video_url - : null; + const trailerUrl = + detail.trailers && detail.trailers.length > 0 + ? detail.trailers[0].video_url + : null; return { ...item, @@ -298,7 +351,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa setSkipNextAutoPlay(false); return; } - + setCurrentIndex((prev) => (prev + 1) % items.length); }, autoPlayInterval); @@ -350,7 +403,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa // 防止在手动切换过程中触发 if (isManualChange.current) return; if (!touchStartX.current) return; - + // 如果有滑动,则执行滑动逻辑 if (touchEndX.current !== 0) { const distance = touchStartX.current - touchEndX.current; @@ -374,13 +427,15 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa if (isLoading || !shouldLoad) { return ( -
+
MoonTVPlus
@@ -395,7 +450,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa return (
setIsPaused(true)} onMouseLeave={() => setIsPaused(false)} onTouchStart={handleTouchStart} @@ -409,7 +464,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = fa }} > {/* 背景图片或视频 */} -
+
{items.map((item, index) => (
{item.trailer_url && enableTrailers ? ( /* 显示豆瓣直链视频 */ -
+
) : item.video_key && isYouTubeAccessible && enableTrailers ? ( /* 显示YouTube视频 */ -
+