115 lines
2.9 KiB
TypeScript
115 lines
2.9 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import type { RefObject } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import type { ImageData, LoopMode, Neighbors } from "../types";
|
|
|
|
interface UseImageCarouselProps {
|
|
images: ImageData["images"];
|
|
isPlaying: boolean;
|
|
loopMode: LoopMode;
|
|
neighbors: Neighbors;
|
|
volume: number;
|
|
audioRef: RefObject<HTMLAudioElement | null>;
|
|
scrollerRef: RefObject<HTMLDivElement | null>;
|
|
setProgress: (progress: number) => void;
|
|
/** 单张图片显示时长(毫秒),默认 5000ms */
|
|
segmentMs?: number;
|
|
}
|
|
|
|
export function useImageCarousel({
|
|
images,
|
|
isPlaying,
|
|
loopMode,
|
|
neighbors,
|
|
volume,
|
|
audioRef,
|
|
scrollerRef,
|
|
setProgress,
|
|
segmentMs = 5000,
|
|
}: UseImageCarouselProps) {
|
|
const router = useRouter();
|
|
const [idx, setIdx] = useState(0);
|
|
const [segProgress, setSegProgress] = useState(0);
|
|
|
|
const segStartRef = useRef<number | null>(null);
|
|
const idxRef = useRef<number>(0);
|
|
const rafRef = useRef<number | null>(null);
|
|
|
|
useEffect(() => {
|
|
idxRef.current = idx;
|
|
}, [idx]);
|
|
|
|
// BGM 控制
|
|
useEffect(() => {
|
|
const el = audioRef.current;
|
|
if (!el) return;
|
|
el.volume = volume;
|
|
if (isPlaying) {
|
|
el.play().catch(() => {});
|
|
} else {
|
|
el.pause();
|
|
}
|
|
}, [audioRef, isPlaying, volume]);
|
|
|
|
// 自动切页
|
|
useEffect(() => {
|
|
if (!images?.length) return;
|
|
|
|
if (segStartRef.current == null) segStartRef.current = performance.now();
|
|
let lastTs = performance.now();
|
|
|
|
const tick = (ts: number) => {
|
|
if (!images?.length) return;
|
|
|
|
if (!isPlaying) segStartRef.current! += ts - lastTs;
|
|
lastTs = ts;
|
|
|
|
let start = segStartRef.current!;
|
|
let localIdx = idxRef.current;
|
|
|
|
let elapsed = ts - start;
|
|
while (elapsed >= segmentMs) {
|
|
elapsed -= segmentMs;
|
|
|
|
if (localIdx >= images.length - 1) {
|
|
if (loopMode === "sequential" && neighbors?.next) {
|
|
router.push(`/aweme/${neighbors.next.aweme_id}`);
|
|
return;
|
|
}
|
|
localIdx = 0;
|
|
} else {
|
|
localIdx = localIdx + 1;
|
|
}
|
|
}
|
|
segStartRef.current = ts - elapsed;
|
|
|
|
if (localIdx !== idxRef.current) {
|
|
idxRef.current = localIdx;
|
|
setIdx(localIdx);
|
|
const el = scrollerRef.current;
|
|
if (el) el.scrollTo({ left: localIdx * el.clientWidth, behavior: "smooth" });
|
|
}
|
|
|
|
const localSeg = Math.max(0, Math.min(1, elapsed / segmentMs));
|
|
setSegProgress(localSeg);
|
|
setProgress((localIdx + localSeg) / images.length);
|
|
|
|
rafRef.current = requestAnimationFrame(tick);
|
|
};
|
|
|
|
rafRef.current = requestAnimationFrame(tick);
|
|
return () => {
|
|
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
rafRef.current = null;
|
|
};
|
|
}, [images?.length, isPlaying, loopMode, neighbors?.next, router, scrollerRef, setProgress, segmentMs]);
|
|
|
|
return {
|
|
idx,
|
|
setIdx,
|
|
segProgress,
|
|
segStartRef,
|
|
idxRef,
|
|
};
|
|
}
|