React中通过MUI滑块设置HTML5 video的currentTime总是重置为0
解决MUI Slider控制HTML5 Video进度重置为0的问题
你遇到的问题核心来自两个关键错误,直接导致滑块拖动时视频进度被重置为0:
1. 错误的currentTime赋值类型
HTML5 Video的currentTime属性要求接收数字类型的值,但你在handleChange里把newValue转成了字符串:
videoRef.current.currentTime = `${newValue}`; // 错误:字符串赋值
当传入字符串时,浏览器解析失败会自动将currentTime设为0,这是进度重置的直接原因。
2. 重复绑定事件导致状态冲突
你的video元素同时绑定了onProgress和onTimeUpdate到同一个handleTimeUpdate函数:
<video onProgress={handleTimeUpdate} <!-- 多余绑定 --> onTimeUpdate={handleTimeUpdate} ... />
onProgress是在视频加载缓冲进度变化时触发的,此时视频的currentTime可能还处于初始状态,会错误地将videoTime重置为0,覆盖滑块设置的进度。
修复方案
修正handleChange函数
直接传入数字类型的newValue:
const handleChange = (e, newValue) => { videoRef.current.currentTime = newValue; // 移除字符串转换 setVideoTime(newValue); };
移除多余的onProgress绑定
只保留onTimeUpdate监听播放进度变化:
<video id="viewer" style={{ aspectRatio: "16/9", borderRadius: '4px' }} width="100%" height="100%" ref={videoRef} src={src} onLoadedMetadata={handleVideoLoaded} onTimeUpdate={handleTimeUpdate} />
额外优化:视频结束逻辑
原代码中视频结束判断可能因精度问题提前触发,增加误差阈值避免误判:
const handleTimeUpdate = (e) => { const current = e.target.currentTime; setVideoTime(current); // 增加0.1秒误差阈值,确保视频确实播放完毕 if (current >= duration - 0.1) { handlePause(); setIsPlaying(false); setVideoTime(0); } };
完整修正后的组件代码
import React, { useState, useRef, useEffect, memo } from 'react'; import { Stack, IconButton, Typography } from '@mui/material'; import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import PauseIcon from '@mui/icons-material/Pause'; import ContentCutIcon from '@mui/icons-material/ContentCut'; import Slider from '@mui/material/Slider'; import { useSelector } from 'react-redux'; import { VideoPlayerGrayColor } from 'theme/colors'; import { calculateTimeWithSeconds } from 'utils/helperFunction'; import { STAGING_URL } from 'utils/constants'; function VideoViewer({ handleTrim }) { const activeMedia = useSelector(state => state?.mediaLibrary?.activeMedia) const videoRef = useRef(null); const [videoTime, setVideoTime] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const [duration, setDuration] = useState(0); const [src, setSrc] = useState(`${STAGING_URL}${activeMedia?.uploaded_file}`) const handleChange = (e, newValue) => { videoRef.current.currentTime = newValue; setVideoTime(newValue); }; const handleTimeUpdate = (e) => { const current = e.target.currentTime; setVideoTime(current); if (current >= duration - 0.1) { handlePause(); setIsPlaying(false); setVideoTime(0); } }; const handlePause = () => { setIsPlaying(false); videoRef.current.pause(); }; const togglePlayPause = () => { if (videoRef.current.paused) { if (videoRef.current.currentTime >= duration - 0.1) { videoRef.current.currentTime = 0; } videoRef.current.play(); } else { videoRef.current.pause(); } setIsPlaying(p => !p); }; const handleVideoLoaded = () => { const duration = videoRef.current?.duration || 0; setDuration(duration); }; useEffect(() => { setSrc(`${STAGING_URL}${activeMedia?.uploaded_file}`); }, [activeMedia]) return ( <Stack width="100%" height="100%" gap={1}> <IconButton onClick={handleTrim} sx={{ borderRadius: '50%', width: 30, padding: '8px 20px', border: "1px solid #E1E1E6" }}> <ContentCutIcon /> </IconButton> <video id="viewer" style={{ aspectRatio: "16/9", borderRadius: '4px' }} width="100%" height="100%" ref={videoRef} src={src} onLoadedMetadata={handleVideoLoaded} onTimeUpdate={handleTimeUpdate} /> <Stack direction="row" justifyContent="flex-end" alignItems="center"> <Stack direction="row" minWidth="55%" justifyContent="space-between" alignItems="center"> <IconButton onClick={togglePlayPause} disableRipple> {isPlaying ? <PauseIcon sx={{ color: VideoPlayerGrayColor, fontSize: "2rem" }} /> : <PlayArrowIcon sx={{ color: VideoPlayerGrayColor, fontSize: "2rem" }} />} </IconButton> <Stack minWidth="25%"> <Slider value={videoTime} onChange={handleChange} scale={calculateTimeWithSeconds} valueLabelDisplay="auto" max={duration} /> <Typography textAlign="end" color={() => VideoPlayerGrayColor} fontSize={12} fontWeight={500}>{calculateTimeWithSeconds(videoTime)} / {calculateTimeWithSeconds(duration)}</Typography> </Stack> </Stack> </Stack> </Stack> ); } export default memo(VideoViewer);
内容的提问来源于stack exchange,提问作者Meer Humza
相关产品推荐
相关产品推荐

