You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native自定义视频控件问题:快进/快退及进度滑块功能失效

React Native自定义视频控件问题:快进/快退及进度滑块功能失效

嘿,我仔细扒了你的代码,发现了几个导致快进/快退和滑块功能罢工的关键点,咱们一个个来修复:

一、进度滑块失效的核心问题

1. 视频时长获取逻辑踩坑

你用useEffect监听videoRef.current来拿时长,但useRef的current是引用类型,React根本检测不到它的变化,所以这个useEffect大概率只在组件挂载时跑一次,要是视频加载慢,拿到的时长就是0,滑块自然动不了。

修复方案:改用视频自带的onPlaybackStatusUpdate事件获取时长,这个事件会在视频状态变化时触发(包括加载完成),靠谱多了:

<Video
  ref={videoRef}
  style={styles.media}
  source={{uri:media}}
  useNativeControls={false}
  resizeMode={ResizeMode.COVER}
  onPlaybackStatusUpdate={(status) => {
    setIsPlaying(status.isPlaying);
    // 只有时长有效时才更新
    if (status.durationMillis && status.durationMillis > 0) {
      setVideoDuration(status.durationMillis / 1000);
    }
  }}
  isLooping={true}
/>

同时删掉原来那个获取时长的useEffect哈。

2. 滑块更新和拖动打架

你用setInterval每秒更新滑块位置,但用户拖动滑块时,这个定时器会强行把滑块值拉回当前播放进度,导致滑块“跳回去”,看起来就像没反应。

修复方案:更新滑块时判断sliderBeingDragged状态,用户拖动时就暂停更新:

useEffect(() => {
  const updateSliderPosition = async () => {
    // 拖动时不更新滑块,避免冲突
    if (sliderBeingDragged || !videoRef.current) return;
    const status = await videoRef.current.getStatusAsync();
    if (status.durationMillis > 0) {
      const progress = status.positionMillis / status.durationMillis;
      setSliderValue(progress);
    }
  };

  const intervalId = setInterval(updateSliderPosition, 1000);
  return () => clearInterval(intervalId);
}, [sliderBeingDragged, videoRef.current]);

3. 滑块跳转单位搞错了

setPositionAsync接收的是毫秒参数,但你现在传的是秒(videoDuration是秒,newPosition = value * videoDuration),相当于只跳了预期位置的1/1000,肉眼根本看不出变化。

修复方案:把计算后的位置转成毫秒:

const onSlidingComplete = async (value) => {
  setSliderBeingDragged(false);
  setSliderValue(value);
  if (videoRef.current && videoDuration > 0) {
    const newPositionMillis = value * videoDuration * 1000; // 转成毫秒
    await videoRef.current.setPositionAsync(newPositionMillis);
  }
};

二、双击快进/快退失效的问题

1. 异步逻辑写错了

你在Math.min和Math.max里直接嵌套await ...then(),这种写法会导致await没正确等待Promise完成,拿到的是Promise对象而非实际位置值,计算出来的新位置根本不对。

修复方案:先拿到播放状态,再计算新位置:

useEffect(() => {
  const handleDoubleTap = async () => {
    if (!videoRef.current || !videoDuration) return;
    
    const status = await videoRef.current.getStatusAsync();
    const currentPositionSec = status.positionMillis / 1000;
    let newPositionSec = currentPositionSec;

    if (doubleTapRight) {
      newPositionSec = Math.min(currentPositionSec + 5, videoDuration);
    } else if (doubleTapLeft) {
      newPositionSec = Math.max(currentPositionSec - 5, 0);
    }

    if (newPositionSec !== currentPositionSec) {
      await videoRef.current.setPositionAsync(newPositionSec * 1000); // 转毫秒
      await videoRef.current.playAsync();
    }
  };

  handleDoubleTap();
}, [doubleTapRight, doubleTapLeft, videoDuration]);

2. 双击状态逻辑可以简化

你用setTimeout重置doubleTapRight/Left,其实可以直接在手势回调里处理,省掉两个状态变量,逻辑更清爽:

<TapGestureHandler
  onHandlerStateChange={async ({ nativeEvent }) => {
    if (nativeEvent.state === State.ACTIVE && videoRef.current && videoDuration) {
      const status = await videoRef.current.getStatusAsync();
      const currentPositionSec = status.positionMillis / 1000;
      const newPositionSec = Math.min(currentPositionSec + 5, videoDuration);
      await videoRef.current.setPositionAsync(newPositionSec * 1000);
      await videoRef.current.playAsync();
    }
  }}
  numberOfTaps={2}
  maxDelayMs={300}
>
  <View style={styles.rightDoubleTapArea}></View>
</TapGestureHandler>

左边的双击手势也照这个逻辑改就行。

最后总结

把上面这些修改点套进你的代码里,滑块和双击快进/快退应该就能正常工作了。记得测试下视频加载慢的情况,确保时长能正确获取到哦~

备注:内容来源于stack exchange,提问作者elvis obondo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 12:53:00