fix: 修复图文动图播放被重置与进度停滞问题

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-04 01:41:47 +08:00
co-authored by Cursor
parent 10f2520279
commit bcf1e6e349
3 changed files with 83 additions and 75 deletions
+30 -25
View File
@@ -84,12 +84,15 @@ export function useImageCarousel({
>({});
const [mediaSyncToken, setMediaSyncToken] = useState(0);
const imagesRef = useRef(images);
imagesRef.current = images;
const segStartRef = useRef<number | null>(null);
const idxRef = useRef<number>(0);
const timerRef = useRef<number | null>(null);
const lastUiUpdateRef = useRef(0);
const segProgressRef = useRef(0);
const totalProgressRef = useRef(0);
const prevImageKeyRef = useRef<string | null>(null);
const fallbackDuration = normalizeDuration(segmentMs, 5000);
const segmentDurations = useMemo(
@@ -178,7 +181,8 @@ export function useImageCarousel({
const advanceSegment = useCallback(
(fromIndex: number): void => {
if (!images.length || idxRef.current !== fromIndex) return;
const currentImages = imagesRef.current;
if (!currentImages.length || idxRef.current !== fromIndex) return;
const now = performance.now();
@@ -189,7 +193,7 @@ export function useImageCarousel({
return;
}
if (fromIndex >= images.length - 1) {
if (fromIndex >= currentImages.length - 1) {
if (loopMode === "sequential" && neighbors?.next) {
router.push(`/aweme/${neighbors.next.aweme_id}`);
return;
@@ -199,7 +203,7 @@ export function useImageCarousel({
segStartRef.current = now;
lastUiUpdateRef.current = now;
syncProgress(0, 0, true);
if (images.length === 1) {
if (currentImages.length === 1) {
setMediaSyncToken((token) => token + 1);
}
return;
@@ -212,12 +216,12 @@ export function useImageCarousel({
lastUiUpdateRef.current = now;
syncProgress(nextIndex, 0, true);
},
[images.length, loopMode, neighbors?.next, router, syncProgress],
[loopMode, neighbors?.next, router, syncProgress],
);
const handleAnimatedProgress = useCallback(
(imageId: string, progress: number): void => {
const currentImage = images[idxRef.current];
const currentImage = imagesRef.current[idxRef.current];
if (!currentImage || currentImage.id !== imageId) return;
const now = performance.now();
@@ -233,23 +237,24 @@ export function useImageCarousel({
syncProgress(idxRef.current, clamped, wrapped);
}
},
[images, syncProgress],
[syncProgress],
);
const handleAnimatedEnded = useCallback(
(imageId: string): void => {
const currentImage = images[idxRef.current];
const currentImage = imagesRef.current[idxRef.current];
if (!currentImage || currentImage.id !== imageId) return;
advanceSegment(idxRef.current);
},
[advanceSegment, images],
[advanceSegment],
);
const goToIndex = useCallback(
(index: number, segmentProgress = 0): void => {
if (!images.length) return;
const count = imagesRef.current.length;
if (!count) return;
const nextIndex = Math.max(0, Math.min(images.length - 1, index));
const nextIndex = Math.max(0, Math.min(count - 1, index));
const nextSegmentProgress = Math.max(0, Math.min(1, segmentProgress));
const now = performance.now();
const segmentDuration = segmentDurations[nextIndex] ?? fallbackDuration;
@@ -261,12 +266,12 @@ export function useImageCarousel({
syncProgress(nextIndex, nextSegmentProgress, true);
setMediaSyncToken((token) => token + 1);
},
[fallbackDuration, images.length, segmentDurations, syncProgress],
[fallbackDuration, segmentDurations, syncProgress],
);
const seekTo = useCallback(
(ratio: number): void => {
if (!images.length || totalDurationMs <= 0) return;
if (!imagesRef.current.length || totalDurationMs <= 0) return;
const clampedRatio = Math.max(0, Math.min(1, ratio));
const targetMs = Math.min(
@@ -298,22 +303,22 @@ export function useImageCarousel({
targetDuration > 0 ? segmentElapsedMs / targetDuration : 0;
goToIndex(targetIndex, nextSegmentProgress);
},
[
fallbackDuration,
goToIndex,
images.length,
segmentDurations,
totalDurationMs,
],
[fallbackDuration, goToIndex, segmentDurations, totalDurationMs],
);
useEffect(() => {
idxRef.current = idx;
}, [idx]);
// 切换作品或图片集合变化时重置所有轮播状态
// 仅当真正切换到另一个作品(imageKey 变化)时重置状态,避免首次挂载覆盖子组件已上报的 ready 状态
useEffect(() => {
if (!imageKey) return;
if (prevImageKeyRef.current === null) {
prevImageKeyRef.current = imageKey;
return;
}
if (prevImageKeyRef.current === imageKey) return;
prevImageKeyRef.current = imageKey;
idxRef.current = 0;
setIdx(0);
segStartRef.current = null;
@@ -356,7 +361,7 @@ export function useImageCarousel({
: false;
useEffect(() => {
if (!images?.length || !segmentDurations.length || totalDurationMs <= 0) {
if (!images.length || !segmentDurations.length || totalDurationMs <= 0) {
return;
}
@@ -373,10 +378,10 @@ export function useImageCarousel({
const now = performance.now();
const currentSegmentDuration = segmentDurations[idx] ?? fallbackDuration;
segStartRef.current = now - segProgressRef.current * currentSegmentDuration;
lastUiUpdateRef.current = now;
const tick = (): void => {
if (!images?.length) return;
const currentImages = imagesRef.current;
if (!currentImages.length) return;
const ts = performance.now();
const start = segStartRef.current ?? ts;
@@ -407,7 +412,7 @@ export function useImageCarousel({
advanceSegment,
fallbackDuration,
idx,
images,
images.length,
isCurrentAnimated,
isCurrentStaticReady,
isPlaying,