React Native中FlatList加载视频时多视频同步停止问题如何解决?
问题排查方法
- 第一步:在
_onViewableItemsChanged回调中打印每次变更的item信息、isViewable属性,确认滑动时是否有滑出屏幕的视频项触发了状态更新 - 第二步:监听
videoPlayback全局状态的变更时机,确认是否只要有视频滑出可视区,该状态就会被设为false - 第三步:检查所有
PostContainer组件的paused属性绑定逻辑,确认是否所有视频都共用了同一个videoPlayback状态控制播放暂停
问题根因
当前所有视频组件共享同一个全局布尔类型的videoPlayback状态,只要任意一个视频滑出屏幕触发isViewable = false,就会把全局状态改为false,导致所有绑定了该状态的视频同步暂停,这就是屏幕中央的视频也跟着停止的核心原因。
解决方案
把全局布尔状态改为存储当前播放视频的唯一ID,单个视频自行判断是否应该播放:
- 替换全局状态类型,将原来的布尔值改为存储当前播放的
postId,默认值为null - 修改可视区变更回调逻辑:视频进入可视区时将当前项ID设为播放ID,视频滑出时只有滑出的是当前播放的视频才重置状态
- 修改
PostContainer逻辑,通过自身ID和当前播放ID的匹配结果控制播放暂停 - 若需要支持双列两个视频同时播放,可将状态改为存储可见视频ID的集合(Set类型)即可
修改后参考代码
FlatList 部分代码
// 替换原布尔状态,存储当前正在播放的视频postId const [playingPostId, setPlayingPostId] = useState(null); const viewabilityConfig = { itemVisiblePercentThreshold: 80, }; const _onViewableItemsChanged = useCallback((props) => { const changed = props.changed; changed.forEach((item) => { // 跳过非视频项 if (!item.item.vimeoId) return; const currentPostId = item.item.postId; if (item.isViewable) { // 进入可视区,设置为当前播放项 setPlayingPostId(currentPostId); } else { // 滑出可视区时,只有当前滑出的是正在播放的项才重置状态 setPlayingPostId(prev => prev === currentPostId ? null : prev); } }); }, []); // ... <FlatList ref={ref} numColumns={2} data={posts} keyExtractor={(item: any) => `${item.postId}`} viewabilityConfig={viewabilityConfig} onViewableItemsChanged={_onViewableItemsChanged} renderItem={({ item, index }) => ( <PostContainer item={item} playingPostId={playingPostId} navigation={navigation} index={index} /> )} />
PostContainer 部分代码
const PostContainer = ({ item, playingPostId }) => { const videoPlayer = useRef(null); // 判断当前项是否需要播放 const isPlaying = playingPostId === item.postId; useFocusEffect( React.useCallback(() => { if (isPlaying) { videoPlayer?.current?.seek(0); } }, [isPlaying]), ); return ( <View> {/* 图片组件渲染逻辑 */} {item.vimeoId && ( <StyledVideo ref={videoPlayer} // 仅当自身ID是当前播放ID时才不暂停 paused={!isPlaying} repeat={true} resizeMode={"cover"} volume={0} source={{ uri: item.vimeoThumbnail, }} /> )} </View> ); };
可选优化(支持双列视频同时播放)
如果需要支持两个同时满足可视条件的视频都播放,仅需要调整状态类型为Set即可:
// 状态改为存储所有可见的视频ID集合 const [visibleVideoIds, setVisibleVideoIds] = useState(new Set()); const _onViewableItemsChanged = useCallback((props) => { const changed = props.changed; changed.forEach((item) => { if (!item.item.vimeoId) return; const currentPostId = item.item.postId; setVisibleVideoIds(prev => { const newSet = new Set(prev); if (item.isViewable) { newSet.add(currentPostId); } else { newSet.delete(currentPostId); } return newSet; }) }); }, []); // PostContainer内判断逻辑改为 const isPlaying = visibleVideoIds.has(item.postId);
内容的提问来源于stack exchange,提问作者rudenick
相关产品推荐
相关产品推荐

