【问题标题】:Pause video in flatlist when out of view不在视野范围内时暂停平面列表中的视频
【发布时间】:2019-02-23 09:01:45
【问题描述】:

我有一个显示视频的平面列表。我希望当视频离开视图时它应该被暂停。我在每个Posts 组件中保持暂停状态。

class Posts extends React.PureComponent {

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

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

}

我正在使用react-native-video

我曾尝试使用 FlatlistonViewableItemsChanged 属性,但它不会改变状态。

我试过 this 。 但这似乎对我不起作用。

我应该如何进行?

【问题讨论】:

    标签: react-native react-native-flatlist react-native-video


    【解决方案1】:

    这是使用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 组件。

    【讨论】:

    • Above 同时播放 flatlist 中的所有视频,而不是一个一个聚焦
    • 它将播放用户完全可见的任何视频。因此,如果您在屏幕上显示三个视频并且完全可见,那么它将播放所有三个视频。您可以更新零食中包含的 InViewPort.js 以定义您所说的“聚焦”的含义,因为它目前检查整个屏幕,但可以对其进行修改以检查屏幕的一部分。
    • 感谢您的回复。问题是它甚至会播放我屏幕上的视频,也就是平面列表底部的视频。我不得不提一下,我没有使用 expo。
    • 这很奇怪,因为它不适合我。
    • @Andrew 我的平面列表包含所有视频,所以,这使得所有视频都可以播放
    【解决方案2】:

    我就是这样做的 在我的卡片组件里面有 video

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

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

    我的 FlatList 将 renderItem 索引作为 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 });
        }
    };
    

    如果有帮助请告诉我

    【讨论】:

    【解决方案3】:

    我终于用 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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多