当 FlatList 不在视图中时,如何暂停视频

12

我有一个展示视频的FlatList。 我希望当一个视频离开视图时,它应该被暂停。 我在每个 Posts 组件中维护暂停状态。

class Posts extends React.PureComponent {

constructor() {
 super()
 this.state = {
   pause: true,
 }

 return(){
  <Video
    pause={this.state.pause}
    //other props
  />
 }

}

我正在使用react-native-video

我尝试使用FlatlistonViewableItemsChanged属性,但它没有改变状态。

我尝试了这个。但是对我来说似乎没有用。

我该如何继续?

3个回答

14
这里是使用react-native-inviewport的一种可能的解决方案。这个依赖只有一个包含组件的索引文件,当视图在视口中时,该组件具有回调功能。它可以很容易地修改以适应您的需求。
我构建了一个非常简单的应用程序,其中包含一个FlatList。 FlatList中的第11项是一个视频。 这意味着当应用程序渲染时,视频不会播放,因为它已经离开了屏幕,一旦视频完全进入视口,它就会开始播放。

App.js

import * as React from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
import VideoPlayer from './VideoPlayer';

export default class App extends React.Component {

  state = {
    data: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
  }

  renderItem = ({item, index}) => {
    if (index === 10) {
      return <VideoPlayer />
    } else {
      return (
        <View style={{height: 100, backgroundColor: '#336699', justifyContent: 'center', alignItems: 'center'}}>
          <Text>{index}</Text>
        </View>
      )
    }
  }
  keyExtractor = (item, index) => `${index}`;

  render() {
    return (
      <View style={styles.container}>
        <FlatList
          data={this.state.data}
          renderItem={this.renderItem}
          keyExtractor={this.keyExtractor}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  }
});

VideoPlayer.js

这是一个包含视频组件的组件。该视频被包裹在InViewPort组件中,并具有回调函数。当该组件完全在视口中时,回调函数返回true,而当该组件未完全在视口中时,则返回false。回调函数调用this.handlePlaying,其中根据布尔值调用this.playVideothis.pauseVideo

import React, {Component} from 'react';
import { View, StyleSheet } from 'react-native';
import { Video } from 'expo-av';
import InViewPort from './InViewPort';
//import InViewPort from 'react-native-inviewport; // this wouldn't work in the snack so I just copied the file and added it manually.

export default class VideoPlayer extends React.Component {

  pauseVideo = () => {
    if(this.video) {
      this.video.pauseAsync();
    }
  }

  playVideo = () => {
    if(this.video) {
      this.video.playAsync();
    }
  }

  handlePlaying = (isVisible) => {
    isVisible ? this.playVideo() : this.pauseVideo();
  }

  render() {
      return (
        <View style={styles.container}>
         <InViewPort onChange={this.handlePlaying}>
          <Video
            ref={ref => {this.video = ref}}
            source={{ uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4' }}
            rate={1.0}
            volume={1.0}
            isMuted={false}
            resizeMode="cover"
            shouldPlay
            style={{ width: 300, height: 300 }}
          />
          </InViewPort>
        </View>
      )
  }  
}

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center'
  }
});

这里有一个示例演示其工作原理 https://snack.expo.io/@andypandy/video-only-playing-when-in-viewport

需要注意的是,如果视频没有完全出现在视窗内,则不会播放。我相信可以通过对react-native-inviewport 进行适当调整来实现部分出现在视窗中时播放视频的功能,例如向InViewPort组件传递视频高度。


它将播放任何完全可见于用户的视频。因此,如果您有三个视频在屏幕上,并且完全可见,则会播放所有三个视频。您可以更新包含在小吃中的InViewPort.js以定义您所谓的“焦点”,因为当前它检查整个屏幕,但可以修改为检查屏幕的一部分。 - Andrew
@Andrew,我的平面列表包含所有的视频,这样就可以播放所有的视频。 - Pradnyanand Milind Pohare
@PradnyanandMilindPohare 如果视频完全可见,则上述解决方案将自动播放该视频。显然,您还有进一步的问题。我建议您将相关部分写成一个新问题,以便您可以清楚地表达您需要做的所有事情,因为SO上的评论并不真正允许进行详细的讨论。首先,代码在这里无法很好地格式化;其次,您的字符限制为600个,因此很难完全表达自己。 - Andrew
遇到了同样的问题,这个包解决了它,谢谢@Andrew! - GeekDev
非常感谢。这个方法可行。我之前一直在苦恼scrollView等问题,但这个方法真的有效。谢谢。 - Muhammad Talha
显示剩余5条评论

7

这里是我简单地完成了技巧,在我的带有 视频 的卡片组件内。

<Video ...
    paused={currentIndex !== currentVisibleIndex}
/>

currentIndex和currentVisibleIndex都是从父组件FlatList传递给组件的。

我的FlatList将renderItem index作为currentIndex传递。

<FlatList
    data={[...]}
    renderItem={({ item, index }) => (
    <GalleryCard
        {...item}
        currentIndex={index}
        currentVisibleIndex={currentVisibleIndex}
        />
        )}
    onViewableItemsChanged={this.onViewableItemsChanged}
    viewabilityConfig={{
        viewAreaCoveragePercentThreshold: 90
    }}

最后,这是如何计算currentVisibleIndex的方法,请确保阅读viewabilityConfig

onViewableItemsChanged = ({ viewableItems, changed }) => {
    if (viewableItems && viewableItems.length > 0) {
        this.setState({ currentVisibleIndex: viewableItems[0].index });
    }
};

如果这对您有帮助,请让我知道。


请您检查一下这个场景。 https://dev59.com/zbvoa4cB1Zd3GeqP87XI - Lucifer_Latif

0

我最终使用了redux完成了这个任务。不确定这是否是正确的方法。

Home.js

_renderItem = ({item, index}) => <Posts item={item} />

viewableItemsChanged = (props) => {
    let {changed} = props;
    let changedPostArray = []; 
    changed.map((v,i) => {
      let {isViewable, item} = v;
      if(!isViewable) {
        let {post_id, type} = item;
        if(type === 1)
          changedPostArray.push(post_id);
      }
    });
    if(changedPostArray.length != 0)
      this.props.sendPostToPause(changedPostArray);
  }

render() {
    return(
      <View style={{flex: 1}} >
          <FlatList
            ref={(ref) => this.homeList = ref}
            refreshing={this.state.refreshing}
            onRefresh={async () => {
              await this.setState({refreshing: true, postsId: [0], radiusId: 0, followingId: 0});
              this.getPosts();
            }}
            onEndReached={async () => {
              if(!this.state.isEnd) {
                await this.setState({isPaginate: true})
                this.getPosts()
              }
            }}
            onEndReachedThreshold={0.2}
            removeClippedSubviews={true}
            contentContainerStyle={{paddingBottom: 80}}
            data={this.state.followersRes}            
            renderItem={this._renderItem}
            viewabilityConfig={this.viewabilityConfig}
            onViewableItemsChanged={this.viewableItemsChanged}
          />
        </View>
        <Footer callback={this.scrollToTopAndRefresh} />
      </View>
    )
}
export default connect(null, {sendPostToPause})(Home);

Posts.js

class Posts extends React.PureComponent {

constructor(){
 super()
 this.state: {
  pause: false
 }
}

componentDidUpdate(prevProps) {
    if(prevProps != this.props) {
      this.props.postIdArray.map((v) => {
        if(v === prevProps.item.post_id) {
          this.setState({pause: true})
        }
      })
    }
  }

render(){
 return(
  <Video
    pause={this.state.pause}
    //Other props
  />
 )
}


}

const mapStateToProps = (state) => {
  const {postId} = state.reducers;
  return {
    postIdArray: postId
  }
}

export default connect(mapStateToProps, {sendPostToPause})(withNavigation(Posts));

每当触发 viewableItemsChanged 事件时,我会将更改的帖子 ID 添加到数组中,并使用帖子 ID 数组调用操作。 在 Posts 组件中,我会检查帖子 ID 是否匹配,如果匹配,则将暂停状态设置为 true。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接