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

React Native + Expo AV实现视频滚动时居中/聚焦自动播放需求

实现Expo AV视频组件的屏幕中心自动播放/暂停方案

核心思路

通过检测视频组件是否处于屏幕中心可视区域,结合Expo AV的播放API自动控制播放状态;若为TV端场景,可直接通过焦点事件处理。

具体实现步骤

1. 基础依赖与状态准备

导入React Native核心API,新增状态记录视频是否处于中心区域:

import { ResizeMode, Video } from 'expo-av';
import { ControlsOverLay } from './controlsOverLay';
import { Dimensions, useEffect, useState } from 'react';

// 获取屏幕尺寸
const { height: screenHeight } = Dimensions.get('window');
// 定义中心触发区域(可按需调整,这里取屏幕中间40%高度范围)
const CENTER_TRIGGER_RANGE = screenHeight * 0.4;

export const AutoPlayVideo = ({ item, props, ref, playback, playbackStatus }) => {
  const [isInCenter, setIsInCenter] = useState(false);
  const videoRef = ref;

  // 检测视频是否处于屏幕中心区域
  const checkPosition = () => {
    videoRef.current?.measure((_x, _y, _width, height, _pageX, pageY) => {
      const videoCenterY = pageY + height / 2;
      const screenCenterY = screenHeight / 2;
      // 判断视频中心是否在触发范围内
      const inRange = Math.abs(videoCenterY - screenCenterY) <= CENTER_TRIGGER_RANGE / 2;
      setIsInCenter(inRange);
    });
  };

2. 监听位置变化事件

根据视频所在容器类型添加对应监听:

  • 滚动列表场景(FlatList/ScrollView):在父组件的onScroll事件中调用检测方法,建议加节流优化性能:
// 父组件FlatList示例
import { throttle } from 'lodash';

<FlatList
  data={videoList}
  renderItem={({ item, index }) => <AutoPlayVideo item={item} ref={videoRefs[index]} checkPosition={checkPosition} />}
  onScroll={throttle(() => {
    videoList.forEach((_, index) => videoRefs[index].current?.checkPosition());
  }, 100)}
/>
  • 单个视频组件场景:监听组件挂载与屏幕尺寸变化:
useEffect(() => {
  // 组件挂载时检测一次
  checkPosition();
  // 监听屏幕尺寸变化
  const subscription = Dimensions.addEventListener('change', checkPosition);
  return () => subscription?.remove();
}, []);

3. 根据位置状态控制播放

监听isInCenter状态变化,调用Expo AV的播放/暂停API:

useEffect(() => {
  if (!videoRef.current) return;

  const updatePlayback = async () => {
    try {
      if (isInCenter) {
        await videoRef.current.playAsync();
      } else {
        await videoRef.current.pauseAsync();
      }
    } catch (err) {
      console.error('播放状态更新失败:', err);
    }
  };

  updatePlayback();
}, [isInCenter]);

4. 修改原组件属性

将原固定的shouldPlay改为由isInCenter控制,确保状态同步:

return (
  <>
    <Video
      ref={videoRef}
      source={{ uri: item?.videoLink }}
      style={{
        width: props.width,
        alignSelf: 'center',
        aspectRatio: 1
      }}
      posterStyle={{
        width: props.width,
        aspectRatio: 1,
        alignSelf: 'center',
        resizeMode: ResizeMode.COVER
      }}
      posterSource={{ uri: item.thumbLink }}
      usePoster={true}
      shouldPlay={isInCenter} // 由位置状态控制播放
      resizeMode={ResizeMode.COVER}
      onPlaybackStatusUpdate={playbackStatus}
    />

    {/* 原有ControlsOverLay组件保持不变 */}
    <ControlsOverLay
      isBuffering={playback.isBuffering}
      playerIndex={props.playerIndex}
      visible>
      {playback.isBuffering && <ActivityIndicator size={'large'} />}
      {playback.isLoaded && playback.isPlaying && (
        <PauseIconPressable onPress={() => videoRef.current.pauseAsync()} />
      )}
      {!playback.isBuffering &&
        playback.isLoaded &&
        !playback.isPlaying &&
        !playback.didJustFinish && (
          <PlayIconPressable onPress={() => videoRef.current.playAsync()} />
        )}
      {playback.didJustFinish && (
        <ReplayIconPressable
          onPress={() => {
            videoRef.current.replayAsync();
          }}
        />
      )}
    </ControlsOverLay>
  </>
);
};

5. TV端焦点场景适配

如果是TV端应用,直接通过焦点事件控制更精准:

<Video
  onFocus={() => videoRef.current?.playAsync()}
  onBlur={() => videoRef.current?.pauseAsync()}
  // 其他属性不变
/>

注意事项

  • 性能优化:滚动时的位置检测建议添加节流,避免频繁触发measure导致性能下降。
  • 权限处理:确保已申请Audio权限(Expo AV播放需要),否则可能播放失败。
  • 异常捕获:播放/暂停操作需包裹try-catch,处理加载失败等异常情况。

内容的提问来源于stack exchange,提问作者David Pham

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 18:37:34