美文网首页
react-native-video-controls 视频播放

react-native-video-controls 视频播放

作者: 物联白菜 | 来源:发表于2021-05-19 20:09 被阅读0次

    参考:https://www.jianshu.com/p/2db4e3e2c343

    效果图:


    11621426060_.pic_hd.jpg

    因为是修改第三库,所以播放器中缺少的icon需要自己添加

    
    import VideoPlayer from 'react-native-video-controls';
    
    
    <View style={[Styles.fullScreen,]}>
            {
                this.state.videoUrl === '' ?
                    <View style={{flex:1,position:'absolute',height:200,width:'100%',zIndex: 1,justifyContent:'center',alignItems:'center'}}>
                        <Text style={{color:'#fff',}}>视频加载失败</Text>
                        {/*<Image source={{uri:this.state.lesson_image_url}} style={{height:200,width:'100%',position:'absolute',opacity:1,}}/>*/}
                    </View>
                        :null
            }
    
            {
                        <VideoPlayer
                            // key={id}
                            source={{uri: this.state.videoUrl}}   // Can be a URL or a local file.
                            // source={{uri: 'http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4'}}   // Can be a URL or a local file.
                            ref={(ref) => {
                                this.player = ref
                            }}                                      // Store reference
                            style={[Styles.fullScreen,]}
                            // poster={'https://oss.aliyuncs.com/tpsite2/jingdu.xyd.qushiyun.com/2ab919f692a79dab55405cf3a2bc9e44.jpg'}  //加载视频时要显示的图像值:带有海报URL的字符串,例如“https://baconmockup.com/300/200/”
                            poster={this.state.lesson_image_url}  //加载视频时要显示的图像值:带有海报URL的字符串,例如“https://baconmockup.com/300/200/”
                            posterResizeMode={'cover'} //确定当帧与原始视频尺寸不匹配时如何调整海报图像的大小。contain、center、cover、none、repeat、stretch
                            rate={this.state.rate}  //视频播放的速率。 0.0 - 暂停播放  1.0 - 正常速率播放
                            paused={this.state.paused}  //控制播放器是否暂停。
                            // paused={item.paused}  //控制播放器是否暂停。
                            playWhenInactive={true}   //在通知或控制中心位于视频前面时是否应继续播放媒体。
                            volume={this.state.volume}  //调整音量
                            muted={this.state.muted}  //控制音频是否静音。
                            resizeMode={this.state.resizeMode}   //确定当帧与原始视频尺寸不匹配时如何调整视频大小。 "none"(默认) - 不匹配大小 contain、cover、stretch
                            fullscreen={this.state.isFullScreen}
                            fullscreenAutorotate={true}
                            fullscreenOrientation={'landscape'}
    
                            onPause={()=>this.onPause()}
                            onPlay={()=>this.onPlay()}
                            onBack={()=>this.onBack()}
                            onEnterFullscreen={()=>this.onEnterFullscreen()}
                            onExitFullscreen={()=>this.onExitFullscreen()}
                            onOpenMore={()=>this.onOpenMore()}   //这是我修改第三方库自己添加上去的方法
                            onLoad={this.onLoad}    //加载媒体并准备播放时调用的回调函数。
                            onLoadStart={this.onLoadStart}    //媒体开始加载时调用的回调函数。
                            onProgress={this.onProgress}   //视频播放过程中每个间隔进度单位(通常不足一秒,你可以打印日志测试下)调用的回调,其中包含有关媒体当前正在播放的位置的信息。
                            onBuffer={this.onBuffer}                // 远程视频缓冲时的回调
                            onError={this.videoError}               // 播放失败后的回调
                            onEnd={this.onEnd}                      // 播放完成后的回调
                            // onTimedMetadata={this.onTimedMetadata}                      // 当定时元数据可用时调用的回调函数.
                            onAudioBecomingNoisy={this.onAudioBecomingNoisy}
                            onAudioFocusChanged={this.onAudioFocusChanged}
                            repeat={false}  //视频放完后是否重播
                        />
            }
        </View>
    
    

    VedioPlayer.js

    import React, {Component} from 'react';
    import Video from 'react-native-video';
    import {
      TouchableWithoutFeedback,
      TouchableHighlight,
      ImageBackground,
      PanResponder,
      StyleSheet,
      Animated,
      SafeAreaView,
      Easing,
      Image,
      View,
      Text,
    } from 'react-native';
    import padStart from 'lodash/padStart';
    
    export default class VideoPlayer extends Component {
      static defaultProps = {
        toggleResizeModeOnFullscreen: true,
        controlAnimationTiming: 500,
        doubleTapTime: 130,
        playInBackground: false,
        playWhenInactive: false,
        resizeMode: 'contain',
        isFullscreen: false,
        showOnStart: true,
        paused: false,
        repeat: false,
        muted: false,
        volume: 1,
        title: '',
        rate: 1,
      };
    
      constructor(props) {
        super(props);
    
        /**
         * All of our values that are updated by the
         * methods and listeners in this class
         */
        this.state = {
          // Video
          resizeMode: this.props.resizeMode,
          paused: this.props.paused,
          poster: this.props.poster,
          muted: this.props.muted,
          volume: this.props.volume,
          rate: this.props.rate,
          // Controls
    
          isFullscreen:
              this.props.isFullScreen || this.props.resizeMode === 'cover' || false,
          showTimeRemaining: true,
          volumeTrackWidth: 0,
          volumeFillWidth: 0,
          seekerFillWidth: 0,
          showControls: this.props.showOnStart,
          volumePosition: 0,
          seekerPosition: 0,
          volumeOffset: 0,
          seekerOffset: 0,
          seeking: false,
          originallyPaused: false,
          scrubbing: false,
          loading: false,
          currentTime: 0,
          error: false,
          duration: 0,
        };
    
        /**
         * Any options that can be set at init.
         */
        this.opts = {
          playWhenInactive: this.props.playWhenInactive,
          playInBackground: this.props.playInBackground,
          repeat: this.props.repeat,
          title: this.props.title,
        };
    
        /**
         * Our app listeners and associated methods
         */
        this.events = {
          onError: this.props.onError || this._onError.bind(this),
          onBack: this.props.onBack || this._onBack.bind(this),
          onEnd: this.props.onEnd || this._onEnd.bind(this),
          onScreenTouch: this._onScreenTouch.bind(this),
          onEnterFullscreen: this.props.onEnterFullscreen,
          onExitFullscreen: this.props.onExitFullscreen,
          onShowControls: this.props.onShowControls,
          onHideControls: this.props.onHideControls,
          onLoadStart: this._onLoadStart.bind(this),
          onProgress: this._onProgress.bind(this),
          onSeek: this._onSeek.bind(this),
          onLoad: this._onLoad.bind(this),
          onPause: this.props.onPause,
          onPlay: this.props.onPlay,
          onOpenMore: this.props.onOpenMore,
        };
    
        /**
         * Functions used throughout the application
         */
        this.methods = {
          toggleFullscreen: this._toggleFullscreen.bind(this),
          togglePlayPause: this._togglePlayPause.bind(this),
          toggleControls: this._toggleControls.bind(this),
          toggleTimer: this._toggleTimer.bind(this),
        };
    
        /**
         * Player information
         */
        this.player = {
          controlTimeoutDelay: this.props.controlTimeout || 15000,
          volumePanResponder: PanResponder,
          seekPanResponder: PanResponder,
          controlTimeout: null,
          tapActionTimeout: null,
          volumeWidth: 150,
          iconOffset: 0,
          seekerWidth: 0,
          ref: Video,
          scrubbingTimeStep: this.props.scrubbing || 0,
          tapAnywhereToPause: this.props.tapAnywhereToPause,
        };
    
        /**
         * Various animations
         */
        const initialValue = this.props.showOnStart ? 1 : 0;
    
        this.animations = {
          bottomControl: {
            marginBottom: new Animated.Value(0),
            opacity: new Animated.Value(initialValue),
          },
          topControl: {
            marginTop: new Animated.Value(0),
            opacity: new Animated.Value(initialValue),
          },
          video: {
            opacity: new Animated.Value(1),
          },
          loader: {
            rotate: new Animated.Value(0),
            MAX_VALUE: 360,
          },
        };
    
        /**
         * Various styles that be added...
         */
        this.styles = {
          videoStyle: this.props.videoStyle || {},
          containerStyle: this.props.style || {},
        };
      }
    
      /**
       | -------------------------------------------------------
       | Events
       | -------------------------------------------------------
       |
       | These are the events that the <Video> component uses
       | and can be overridden by assigning it as a prop.
       | It is suggested that you override onEnd.
       |
       */
    
      /**
       * When load starts we display a loading icon
       * and show the controls.
       */
      _onLoadStart() {
        let state = this.state;
        state.loading = true;
        this.loadAnimation();
        this.setState(state);
    
        if (typeof this.props.onLoadStart === 'function') {
          this.props.onLoadStart(...arguments);
        }
      }
    
      /**
       * When load is finished we hide the load icon
       * and hide the controls. We also set the
       * video duration.
       *
       * @param {object} data The video meta data
       */
      _onLoad(data = {}) {
        let state = this.state;
    
        state.duration = data.duration;
        state.loading = false;
        this.setState(state);
    
        if (state.showControls) {
          this.setControlTimeout();
        }
    
        if (typeof this.props.onLoad === 'function') {
          this.props.onLoad(...arguments);
        }
      }
    
      /**
       * For onprogress we fire listeners that
       * update our seekbar and timer.
       *
       * @param {object} data The video meta data
       */
      _onProgress(data = {}) {
        let state = this.state;
        if (!state.scrubbing) {
          state.currentTime = data.currentTime;
    
          if (!state.seeking) {
            const position = this.calculateSeekerPosition();
            this.setSeekerPosition(position);
          }
    
          if (typeof this.props.onProgress === 'function') {
            this.props.onProgress(...arguments);
          }
    
          this.setState(state);
        }
      }
    
      /**
       * For onSeek we clear scrubbing if set.
       *
       * @param {object} data The video meta data
       */
      _onSeek(data = {}) {
        let state = this.state;
        if (state.scrubbing) {
          state.scrubbing = false;
          state.currentTime = data.currentTime;
    
          // Seeking may be false here if the user released the seek bar while the player was still processing
          // the last seek command. In this case, perform the steps that have been postponed.
          if (!state.seeking) {
            this.setControlTimeout();
            state.paused = state.originallyPaused;
          }
    
          this.setState(state);
        }
      }
    
      /**
       * It is suggested that you override this
       * command so your app knows what to do.
       * Either close the video or go to a
       * new page.
       */
      _onEnd() {}
    
      /**
       * Set the error state to true which then
       * changes our renderError function
       *
       * @param {object} err  Err obj returned from <Video> component
       */
      _onError(err) {
        let state = this.state;
        state.error = true;
        state.loading = false;
    
        this.setState(state);
      }
    
      /**
       * This is a single and double tap listener
       * when the user taps the screen anywhere.
       * One tap toggles controls and/or toggles pause,
       * two toggles fullscreen mode.
       */
      _onScreenTouch() {
        if (this.player.tapActionTimeout) {
          clearTimeout(this.player.tapActionTimeout);
          this.player.tapActionTimeout = 0;
          this.methods.toggleFullscreen();
          const state = this.state;
          if (state.showControls) {
            this.resetControlTimeout();
          }
        } else {
          this.player.tapActionTimeout = setTimeout(() => {
            const state = this.state;
            if (this.player.tapAnywhereToPause && state.showControls) {
              this.methods.togglePlayPause();
              this.resetControlTimeout();
            } else {
              this.methods.toggleControls();
            }
            this.player.tapActionTimeout = 0;
          }, this.props.doubleTapTime);
        }
      }
    
      /**
       | -------------------------------------------------------
       | Methods
       | -------------------------------------------------------
       |
       | These are all of our functions that interact with
       | various parts of the class. Anything from
       | calculating time remaining in a video
       | to handling control operations.
       |
       */
    
      /**
       * Set a timeout when the controls are shown
       * that hides them after a length of time.
       * Default is 15s
       */
      setControlTimeout() {
        this.player.controlTimeout = setTimeout(() => {
          this._hideControls();
        }, this.player.controlTimeoutDelay);
      }
    
      /**
       * Clear the hide controls timeout.
       */
      clearControlTimeout() {
        clearTimeout(this.player.controlTimeout);
      }
    
      /**
       * Reset the timer completely
       */
      resetControlTimeout() {
        this.clearControlTimeout();
        this.setControlTimeout();
      }
    
      /**
       * Animation to hide controls. We fade the
       * display to 0 then move them off the
       * screen so they're not interactable
       */
      hideControlAnimation() {
        Animated.parallel([
          Animated.timing(this.animations.topControl.opacity, {
            toValue: 0,
            duration: this.props.controlAnimationTiming,
            useNativeDriver: false,
          }),
          Animated.timing(this.animations.topControl.marginTop, {
            toValue: -100,
            duration: this.props.controlAnimationTiming,
            useNativeDriver: false,
          }),
          Animated.timing(this.animations.bottomControl.opacity, {
            toValue: 0,
            duration: this.props.controlAnimationTiming,
            useNativeDriver: false,
          }),
          Animated.timing(this.animations.bottomControl.marginBottom, {
            toValue: -100,
            duration: this.props.controlAnimationTiming,
            useNativeDriver: false,
          }),
        ]).start();
      }
    
      /**
       * Animation to show controls...opposite of
       * above...move onto the screen and then
       * fade in.
       */
      showControlAnimation() {
        Animated.parallel([
          Animated.timing(this.animations.topControl.opacity, {
            toValue: 1,
            useNativeDriver: false,
            duration: this.props.controlAnimationTiming,
          }),
          Animated.timing(this.animations.topControl.marginTop, {
            toValue: 0,
            useNativeDriver: false,
            duration: this.props.controlAnimationTiming,
          }),
          Animated.timing(this.animations.bottomControl.opacity, {
            toValue: 1,
            useNativeDriver: false,
            duration: this.props.controlAnimationTiming,
          }),
          Animated.timing(this.animations.bottomControl.marginBottom, {
            toValue: 0,
            useNativeDriver: false,
            duration: this.props.controlAnimationTiming,
          }),
        ]).start();
      }
    
      /**
       * Loop animation to spin loader icon. If not loading then stop loop.
       */
      loadAnimation() {
        if (this.state.loading) {
          Animated.sequence([
            Animated.timing(this.animations.loader.rotate, {
              toValue: this.animations.loader.MAX_VALUE,
              duration: 1500,
              easing: Easing.linear,
              useNativeDriver: false,
            }),
            Animated.timing(this.animations.loader.rotate, {
              toValue: 0,
              duration: 0,
              easing: Easing.linear,
              useNativeDriver: false,
            }),
          ]).start(this.loadAnimation.bind(this));
        }
      }
    
      /**
       * Function to hide the controls. Sets our
       * state then calls the animation.
       */
      _hideControls() {
        if (this.mounted) {
          let state = this.state;
          state.showControls = false;
          this.hideControlAnimation();
          typeof this.events.onHideControls === 'function' &&
          this.events.onHideControls();
    
          this.setState(state);
        }
      }
    
      /**
       * Function to toggle controls based on
       * current state.
       */
      _toggleControls() {
        let state = this.state;
        state.showControls = !state.showControls;
    
        if (state.showControls) {
          this.showControlAnimation();
          this.setControlTimeout();
          typeof this.events.onShowControls === 'function' &&
          this.events.onShowControls();
        } else {
          this.hideControlAnimation();
          this.clearControlTimeout();
          typeof this.events.onHideControls === 'function' &&
          this.events.onHideControls();
        }
    
        this.setState(state);
      }
    
      /**
       * Toggle fullscreen changes resizeMode on
       * the <Video> component then updates the
       * isFullscreen state.
       */
      _toggleFullscreen() {
        let state = this.state;
    
        state.isFullscreen = !state.isFullscreen;
    
        if (this.props.toggleResizeModeOnFullscreen) {
          state.resizeMode = state.isFullscreen === true ? 'cover' : 'contain';
        }
    
        if (state.isFullscreen) {
          typeof this.events.onEnterFullscreen === 'function' &&
          this.events.onEnterFullscreen();
        } else {
          typeof this.events.onExitFullscreen === 'function' &&
          this.events.onExitFullscreen();
        }
    
        this.setState(state);
      }
    
      /**
       * Toggle playing state on <Video> component
       */
      _togglePlayPause() {
        let state = this.state;
        state.paused = !state.paused;
    
        if (state.paused) {
          typeof this.events.onPause === 'function' && this.events.onPause();
        } else {
          typeof this.events.onPlay === 'function' && this.events.onPlay();
        }
    
        this.setState(state);
      }
    
      /**
       * Toggle between showing time remaining or
       * video duration in the timer control
       */
      _toggleTimer() {
        let state = this.state;
        state.showTimeRemaining = !state.showTimeRemaining;
        this.setState(state);
      }
    
      /**
       * The default 'onBack' function pops the navigator
       * and as such the video player requires a
       * navigator prop by default.
       */
      _onBack() {
        if (this.props.navigator && this.props.navigator.pop) {
          this.props.navigator.pop();
        } else {
          console.warn(
              'Warning: _onBack requires navigator property to function. Either modify the onBack prop or pass a navigator prop',
          );
        }
      }
    
      /**
       * Calculate the time to show in the timer area
       * based on if they want to see time remaining
       * or duration. Formatted to look as 00:00.
       */
      calculateTime() {
        if (this.state.showTimeRemaining) {
          const time = this.state.duration - this.state.currentTime;
          return `${this.formatTime(time)}`;
        }
    
        return this.formatTime(this.state.currentTime);
      }
    
      /**
       * Format a time string as mm:ss
       *
       * @param {int} time time in milliseconds
       * @return {string} formatted time string in mm:ss format
       */
      formatTime(time = 0) {
        const symbol = this.state.showRemainingTime ? '-' : '';
        time = Math.min(Math.max(time, 0), this.state.duration);
    
        const formattedMinutes = padStart(Math.floor(time / 60).toFixed(0), 2, 0);
        const formattedSeconds = padStart(Math.floor(time % 60).toFixed(0), 2, 0);
    
        return `${symbol}${formattedMinutes}:${formattedSeconds}`;
      }
    
      formatSeconds(second) {
        let h = 0, i = 0, s = parseInt(second);
        if (s > 60) {
          i = parseInt(s / 60);
          s = parseInt(s % 60);
        }
        // 补零
        let zero = function (v) {
          return (v >> 0) < 10 ? "0" + v : v;
        };
        // return [zero(h), zero(i), zero(s)].join(":");    //00:00:00
        return [zero(i), zero(s)].join(":");    //00:00
      }
    
      /**
       * Set the position of the seekbar's components
       * (both fill and handle) according to the
       * position supplied.
       *
       * @param {float} position position in px of seeker handle}
       */
      setSeekerPosition(position = 0) {
        let state = this.state;
        position = this.constrainToSeekerMinMax(position);
    
        state.seekerFillWidth = position;
        state.seekerPosition = position;
    
        if (!state.seeking) {
          state.seekerOffset = position;
        }
    
        this.setState(state);
      }
    
      /**
       * Constrain the location of the seeker to the
       * min/max value based on how big the
       * seeker is.
       *
       * @param {float} val position of seeker handle in px
       * @return {float} constrained position of seeker handle in px
       */
      constrainToSeekerMinMax(val = 0) {
        if (val <= 0) {
          return 0;
        } else if (val >= this.player.seekerWidth) {
          return this.player.seekerWidth;
        }
        return val;
      }
    
      /**
       * Calculate the position that the seeker should be
       * at along its track.
       *
       * @return {float} position of seeker handle in px based on currentTime
       */
      calculateSeekerPosition() {
        const percent = this.state.currentTime / this.state.duration;
        return this.player.seekerWidth * percent;
      }
    
      /**
       * Return the time that the video should be at
       * based on where the seeker handle is.
       *
       * @return {float} time in ms based on seekerPosition.
       */
      calculateTimeFromSeekerPosition() {
        const percent = this.state.seekerPosition / this.player.seekerWidth;
        return this.state.duration * percent;
      }
    
      /**
       * Seek to a time in the video.
       *
       * @param {float} time time to seek to in ms
       */
      seekTo(time = 0) {
        let state = this.state;
        state.currentTime = time;
        this.player.ref.seek(time);
        this.setState(state);
      }
    
      /**
       * Set the position of the volume slider
       *
       * @param {float} position position of the volume handle in px
       */
      setVolumePosition(position = 0) {
        let state = this.state;
        position = this.constrainToVolumeMinMax(position);
        state.volumePosition = position + this.player.iconOffset;
        state.volumeFillWidth = position;
    
        state.volumeTrackWidth = this.player.volumeWidth - state.volumeFillWidth;
    
        if (state.volumeFillWidth < 0) {
          state.volumeFillWidth = 0;
        }
    
        if (state.volumeTrackWidth > 150) {
          state.volumeTrackWidth = 150;
        }
    
        this.setState(state);
      }
    
      /**
       * Constrain the volume bar to the min/max of
       * its track's width.
       *
       * @param {float} val position of the volume handle in px
       * @return {float} contrained position of the volume handle in px
       */
      constrainToVolumeMinMax(val = 0) {
        if (val <= 0) {
          return 0;
        } else if (val >= this.player.volumeWidth + 9) {
          return this.player.volumeWidth + 9;
        }
        return val;
      }
    
      /**
       * Get the volume based on the position of the
       * volume object.
       *
       * @return {float} volume level based on volume handle position
       */
      calculateVolumeFromVolumePosition() {
        return this.state.volumePosition / this.player.volumeWidth;
      }
    
      /**
       * Get the position of the volume handle based
       * on the volume
       *
       * @return {float} volume handle position in px based on volume
       */
      calculateVolumePositionFromVolume() {
        return this.player.volumeWidth * this.state.volume;
      }
    
      /**
       | -------------------------------------------------------
       | React Component functions
       | -------------------------------------------------------
       |
       | Here we're initializing our listeners and getting
       | the component ready using the built-in React
       | Component methods
       |
       */
    
      /**
       * Before mounting, init our seekbar and volume bar
       * pan responders.
       */
      UNSAFE_componentWillMount() {
        this.initSeekPanResponder();
        this.initVolumePanResponder();
      }
    
      /**
       * To allow basic playback management from the outside
       * we have to handle possible props changes to state changes
       */
      UNSAFE_componentWillReceiveProps(nextProps) {
        // console.log('UNSAFE_componentWillReceiveProps====视频组件的==',nextProps)
        if (this.state.paused !== nextProps.paused) {
          this.setState({
            paused: nextProps.paused,
            poster: nextProps.poster,
          });
        }
    
        if (this.styles.videoStyle !== nextProps.videoStyle) {
          this.styles.videoStyle = nextProps.videoStyle;
        }
    
        if (this.styles.containerStyle !== nextProps.style) {
          this.styles.containerStyle = nextProps.style;
        }
      }
    
      /**
       * Upon mounting, calculate the position of the volume
       * bar based on the volume property supplied to it.
       */
      componentDidMount() {
        const position = this.calculateVolumePositionFromVolume();
        let state = this.state;
        this.setVolumePosition(position);
        state.volumeOffset = position;
        this.mounted = true;
    
        this.setState(state);
      }
    
      /**
       * When the component is about to unmount kill the
       * timeout less it fire in the prev/next scene
       */
      componentWillUnmount() {
        this.mounted = false;
        this.clearControlTimeout();
      }
    
      /**
       * Get our seekbar responder going
       */
      initSeekPanResponder() {
        this.player.seekPanResponder = PanResponder.create({
          // Ask to be the responder.
          onStartShouldSetPanResponder: (evt, gestureState) => true,
          onMoveShouldSetPanResponder: (evt, gestureState) => true,
    
          /**
           * When we start the pan tell the machine that we're
           * seeking. This stops it from updating the seekbar
           * position in the onProgress listener.
           */
          onPanResponderGrant: (evt, gestureState) => {
            let state = this.state;
            this.clearControlTimeout();
            const position = evt.nativeEvent.locationX;
            this.setSeekerPosition(position);
            state.seeking = true;
            state.originallyPaused = state.paused;
            state.scrubbing = false;
            if (this.player.scrubbingTimeStep > 0) {
              state.paused = true;
            }
            this.setState(state);
          },
    
          /**
           * When panning, update the seekbar position, duh.
           */
          onPanResponderMove: (evt, gestureState) => {
            const position = this.state.seekerOffset + gestureState.dx;
            this.setSeekerPosition(position);
            let state = this.state;
    
            if (
                this.player.scrubbingTimeStep > 0 &&
                !state.loading &&
                !state.scrubbing
            ) {
              const time = this.calculateTimeFromSeekerPosition();
              const timeDifference = Math.abs(state.currentTime - time) * 1000;
    
              if (
                  time < state.duration &&
                  timeDifference >= this.player.scrubbingTimeStep
              ) {
                state.scrubbing = true;
    
                this.setState(state);
                setTimeout(() => {
                  this.player.ref.seek(time, this.player.scrubbingTimeStep);
                }, 1);
              }
            }
          },
    
          /**
           * On release we update the time and seek to it in the video.
           * If you seek to the end of the video we fire the
           * onEnd callback
           */
          onPanResponderRelease: (evt, gestureState) => {
            const time = this.calculateTimeFromSeekerPosition();
            let state = this.state;
            if (time >= state.duration && !state.loading) {
              state.paused = true;
              this.events.onEnd();
            } else if (state.scrubbing) {
              state.seeking = false;
            } else {
              this.seekTo(time);
              this.setControlTimeout();
              state.paused = state.originallyPaused;
              state.seeking = false;
            }
            this.setState(state);
          },
        });
      }
    
      /**
       * Initialize the volume pan responder.
       */
      initVolumePanResponder() {
        this.player.volumePanResponder = PanResponder.create({
          onStartShouldSetPanResponder: (evt, gestureState) => true,
          onMoveShouldSetPanResponder: (evt, gestureState) => true,
          onPanResponderGrant: (evt, gestureState) => {
            this.clearControlTimeout();
          },
    
          /**
           * Update the volume as we change the position.
           * If we go to 0 then turn on the mute prop
           * to avoid that weird static-y sound.
           */
          onPanResponderMove: (evt, gestureState) => {
            let state = this.state;
            const position = this.state.volumeOffset + gestureState.dx;
    
            this.setVolumePosition(position);
            state.volume = this.calculateVolumeFromVolumePosition();
    
            if (state.volume <= 0) {
              state.muted = true;
            } else {
              state.muted = false;
            }
    
            this.setState(state);
          },
    
          /**
           * Update the offset...
           */
          onPanResponderRelease: (evt, gestureState) => {
            let state = this.state;
            state.volumeOffset = state.volumePosition;
            this.setControlTimeout();
            this.setState(state);
          },
        });
      }
    
      /**
       | -------------------------------------------------------
       | Rendering
       | -------------------------------------------------------
       |
       | This section contains all of our render methods.
       | In addition to the typical React render func
       | we also have all the render methods for
       | the controls.
       |
       */
    
      /**
       * Standard render control function that handles
       * everything except the sliders. Adds a
       * consistent <TouchableHighlight>
       * wrapper and styling.
       */
      renderControl(children, callback, style = {}) {
        return (
            <TouchableHighlight
                underlayColor="transparent"
                // activeOpacity={0.3}
                onPress={() => {
                  this.resetControlTimeout();
                  callback();
                }}
                // style={[styles.controls.control, style]}
            >
              {children}
            </TouchableHighlight>
        );
      }
    
      /**
       * Renders an empty control, used to disable a control without breaking the view layout.
       */
      renderNullControl() {
        return <View style={[styles.controls.control]} />;
      }
    
      /**
       * Groups the top bar controls together in an animated
       * view and spaces them out.
       */
      renderTopControls() {
        const backControl = this.props.disableBack
            ? this.renderNullControl()
            : this.renderBack();
        const volumeControl = this.props.disableVolume
            ? this.renderNullControl()
            : this.renderVolume();
        const renderOpenMore = this.props.disableFullscreen
            ? this.renderNullControl()
            : this.renderOpenMore();
    
        return (
            <Animated.View
                style={[
                  styles.controls.top,
                  {
                    opacity: this.animations.topControl.opacity,
                    marginTop: this.animations.topControl.marginTop,
                  },
                ]}>
              <ImageBackground
                  source={require('./assets/img/top-vignette.png')}
                  style={[styles.controls.column,{}]}
                  imageStyle={[styles.controls.vignette]}>
                <SafeAreaView style={styles.controls.topControlGroup}>
                  {backControl}
                  <View style={styles.controls.pullRight}>
                    {/*{volumeControl}*/}
                    {/*{fullscreenControl}*/}
                    {renderOpenMore}
                  </View>
                </SafeAreaView>
              </ImageBackground>
            </Animated.View>
        );
      }
    
      /**
       * Back button control
       */
      renderBack() {
        return this.renderControl(
            <View style={{paddingHorizontal:7,paddingVertical:5}}>
              <Image
                  source={require('./assets/img/back.png')}
                  style={{}}
              />
            </View>
            ,
            this.events.onBack,
            styles.controls.back,
        );
      }
    
      /**
       * 这是我自己添加的事件
       */
    
      renderOpenMore() {
        return this.renderControl(
            <Image
                source={require('../../YGYM_app/images/class/sandian.png')}
                style={{height:32,width:32,borderRadius:9999}}
                pointerEvents={'none'}
            />
            ,
            this.events.onOpenMore,
            styles.controls.back,
        );
      }
    
      /**
       * Render the volume slider and attach the pan handlers
       */
      renderVolume() {
        return (
            <View style={styles.volume.container}>
              <View
                  style={[styles.volume.fill, {width: this.state.volumeFillWidth}]}
              />
              <View
                  style={[styles.volume.track, {width: this.state.volumeTrackWidth}]}
              />
              <View
                  style={[styles.volume.handle, {left: this.state.volumePosition}]}
                  {...this.player.volumePanResponder.panHandlers}>
                <Image
                    style={styles.volume.icon}
                    source={require('./assets/img/volume.png')}
                />
              </View>
            </View>
        );
      }
    
      /**
       * Render fullscreen toggle and set icon based on the fullscreen state.
       */
      renderFullscreen() {
        let source =
            this.state.isFullscreen === true
                ? require('./assets/img/shrink.png')
                : require('./assets/img/expand.png');
        return this.renderControl(
            <Image source={source}  style={{zIndex:1}}/>,
            this.methods.toggleFullscreen,
            styles.controls.fullscreen,
        );
      }
    
      /**
       * Render bottom control group and wrap it in a holder
       */
      renderBottomControls() {
        // const timerControl = this.props.disableTimer
        //   ? this.renderNullControl()
        //   : this.renderTimer();
        const seekbarControl = this.props.disableSeekbar
            ? this.renderNullControl()
            : this.renderSeekbar();
        // const playPauseControl = this.props.disablePlayPause
        //   ? this.renderNullControl()
        //   : this.renderPlayPause();
    
        return (
            <Animated.View
                style={[
                  styles.controls.bottom,
                  {
                    opacity: this.animations.bottomControl.opacity,
                    marginBottom: this.animations.bottomControl.marginBottom,
                  },
                ]}>
              <ImageBackground
                  source={require('./assets/img/bottom-vignette.png')}
                  style={[styles.controls.column,]}
                  imageStyle={[styles.controls.vignette]}>
    
                {seekbarControl}
    
                {/*<SafeAreaView*/}
                {/*  style={[styles.controls.row, styles.controls.bottomControlGroup]}>*/}
                {/*  {playPauseControl}*/}
                {/*  {this.renderTitle()}*/}
                {/*  {timerControl}*/}
                {/*</SafeAreaView>*/}
              </ImageBackground>
            </Animated.View>
        );
      }
    
      /**
       * Render the seekbar and attach its handlers
       */
      renderTimerFn(time){
        const timerControl = this.props.disableTimer
            ? this.renderNullControl()
            : this.renderTimer(time);
        return timerControl
      }
    
      renderSeekbar() {
        const playPauseControl = this.props.disablePlayPause
            ? this.renderNullControl()
            : this.renderPlayPause();
    
        const fullscreenControl = this.props.disableFullscreen
            ? this.renderNullControl()
            : this.renderFullscreen();
    
        return (
            <View style={{width:'100%',flexDirection:'row',alignItems:'center',paddingHorizontal:15}}>
              {playPauseControl}
              {/*{timerControl}*/}
              {this.renderTimerFn(this.state.currentTime)}
    
              <View
                  style={[styles.seekbar.container,{marginLeft:10}]}
                  collapsable={false}
                  {...this.player.seekPanResponder.panHandlers}>
                <View
                    style={styles.seekbar.track}
                    onLayout={event =>
                        (this.player.seekerWidth = event.nativeEvent.layout.width)
                    }
                    pointerEvents={'none'}>
                  <View
                      style={[
                        styles.seekbar.fill,
                        {
                          width: this.state.seekerFillWidth,
                          backgroundColor: this.props.seekColor || '#FFF',
                        },
                      ]}
                      pointerEvents={'none'}
                  />
                </View>
                <View
                    style={[styles.seekbar.handle, {left: this.state.seekerPosition}]}
                    pointerEvents={'none'}>
                  <Image
                      source={require('../../YGYM_app/images/play_icon.png')}
                      style={[
                        styles.seekbar.circle,
                        // {backgroundColor: this.props.seekColor || '#FFF'},
                      ]}
                      pointerEvents={'none'}
                  />
                </View>
              </View>
    
              <View style={{marginLeft:10}}>
                {this.renderTimerFn(this.state.duration)}
              </View>
    
              <View style={{marginLeft:10}}>
                {fullscreenControl}
              </View>
    
    
            </View>
        );
      }
    
      /**
       * Render the play/pause button and show the respective icon
       */
      renderPlayPause() {
        let source =
            this.state.paused === true
                ? require('./assets/img/play.png')
                : require('./assets/img/pause.png');
        return this.renderControl(
            <Image source={source} style={{marginRight:10}}/>,
            this.methods.togglePlayPause,
            styles.controls.playPause,
        );
      }
    
      /**
       * Render our title...if supplied.
       */
      renderTitle() {
        if (this.opts.title) {
          return (
              <View style={[styles.controls.control, styles.controls.title]}>
                <Text
                    style={[styles.controls.text, styles.controls.titleText]}
                    numberOfLines={1}>
                  {this.opts.title || ''}
                </Text>
              </View>
          );
        }
    
        return null;
      }
    
      /**
       * Show our timer.
       */
      renderTimer(time) {
        return this.renderControl(
            // <Text style={styles.controls.timerText}>{this.calculateTime()}</Text>,
            <Text style={styles.controls.timerText}>{this.formatSeconds(time)}</Text>,
            this.methods.toggleTimer,
            styles.controls.timer,
        );
      }
    
      /**
       * Show loading icon
       */
      renderLoader() {
        if (this.state.loading) {
          return (
              <View style={styles.loader.container}>
                <Animated.Image
                    source={require('./assets/img/loader-icon.png')}
                    style={[
                      styles.loader.icon,
                      {
                        transform: [
                          {
                            rotate: this.animations.loader.rotate.interpolate({
                              inputRange: [0, 360],
                              outputRange: ['0deg', '360deg'],
                            }),
                          },
                        ],
                      },
                    ]}
                />
              </View>
          );
        }
        return null;
      }
    
      renderError() {
        if (this.state.error) {
          return (
              <View style={styles.error.container}>
                <Image
                    source={require('./assets/img/error-icon.png')}
                    style={styles.error.icon}
                />
                <Text style={styles.error.text}>Video unavailable</Text>
              </View>
          );
        }
        return null;
      }
    
      /**
       * Provide all of our options and render the whole component.
       */
      render() {
        return (
            <TouchableWithoutFeedback
                onPress={this.events.onScreenTouch}
                style={[styles.player.container, this.styles.containerStyle]}>
              <View style={[styles.player.container, this.styles.containerStyle]}>
                <Video
                    {...this.props}
                    ref={videoPlayer => (this.player.ref = videoPlayer)}
                    resizeMode={this.state.resizeMode}
                    volume={this.state.volume}
                    paused={this.state.paused}
                    poster={this.state.poster}
                    muted={this.state.muted}
                    rate={this.state.rate}
                    onLoadStart={this.events.onLoadStart}
                    onProgress={this.events.onProgress}
                    onError={this.events.onError}
                    onLoad={this.events.onLoad}
                    onEnd={this.events.onEnd}
                    onSeek={this.events.onSeek}
                    style={[styles.player.video, this.styles.videoStyle]}
                    source={this.props.source}
                />
                {this.renderError()}
                {this.renderLoader()}
                {this.renderTopControls()}
    
    
                {this.renderBottomControls()}
    
    
              </View>
            </TouchableWithoutFeedback>
        );
      }
    }
    
    /**
     * This object houses our styles. There's player
     * specific styles and control specific ones.
     * And then there's volume/seeker styles.
     */
    const styles = {
      player: StyleSheet.create({
        container: {
          overflow: 'hidden',
          backgroundColor: '#000',
          flex: 1,
          alignSelf: 'stretch',
          justifyContent: 'space-between',
        },
        video: {
          overflow: 'hidden',
          position: 'absolute',
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
        },
      }),
      error: StyleSheet.create({
        container: {
          backgroundColor: 'rgba( 0, 0, 0, 0.5 )',
          position: 'absolute',
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
          justifyContent: 'center',
          alignItems: 'center',
        },
        icon: {
          marginBottom: 16,
        },
        text: {
          backgroundColor: 'transparent',
          color: '#f27474',
        },
      }),
      loader: StyleSheet.create({
        container: {
          position: 'absolute',
          top: 0,
          right: 0,
          bottom: 0,
          left: 0,
          alignItems: 'center',
          justifyContent: 'center',
        },
      }),
      controls: StyleSheet.create({
        row: {
          flexDirection: 'row',
          alignItems: 'center',
          justifyContent: 'space-between',
          height: null,
          width: null,
        },
        column: {
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'space-between',
          height: null,
          width: null,
        },
        vignette: {
          resizeMode: 'stretch',
        },
        control: {
          padding: 16,
        },
        text: {
          backgroundColor: 'transparent',
          color: '#FFF',
          fontSize: 14,
          textAlign: 'center',
        },
        pullRight: {
          flexDirection: 'row',
          alignItems: 'center',
          justifyContent: 'center',
        },
        top: {
          flex: 1,
          alignItems: 'stretch',
          justifyContent: 'flex-start',
        },
        bottom: {
          alignItems: 'stretch',
          flex: 2,
          justifyContent: 'flex-end',
        },
        topControlGroup: {
          alignSelf: 'stretch',
          alignItems: 'center',
          justifyContent: 'space-between',
          flexDirection: 'row',
          width: null,
          margin: 12,
          marginBottom: 18,
        },
        bottomControlGroup: {
          alignSelf: 'stretch',
          alignItems: 'center',
          justifyContent: 'space-between',
          marginLeft: 12,
          marginRight: 12,
          marginBottom: 0,
        },
        volume: {
          flexDirection: 'row',
        },
        fullscreen: {
          flexDirection: 'row',
        },
        playPause: {
          position: 'relative',
          // width: 80,
          zIndex: 0,
        },
        title: {
          alignItems: 'center',
          flex: 0.6,
          flexDirection: 'column',
          padding: 0,
        },
        titleText: {
          textAlign: 'center',
        },
        timer: {
          width: 80,
        },
        timerText: {
          backgroundColor: 'transparent',
          color: '#FFF',
          fontSize: 11,
          textAlign: 'right',
        },
      }),
      volume: StyleSheet.create({
        container: {
          alignItems: 'center',
          justifyContent: 'flex-start',
          flexDirection: 'row',
          height: 1,
          marginLeft: 20,
          marginRight: 20,
          width: 150,
        },
        track: {
          backgroundColor: '#333',
          height: 1,
          marginLeft: 7,
        },
        fill: {
          backgroundColor: '#FFF',
          height: 1,
        },
        handle: {
          position: 'absolute',
          marginTop: -24,
          marginLeft: -24,
          padding: 16,
        },
        icon: {
          marginLeft: 7,
        },
      }),
      seekbar: StyleSheet.create({
        container: {
          flex:1,
          // alignSelf: 'stretch',
          height: 28,
          // marginLeft: 20,
          // marginRight: 20,
        },
        track: {
          backgroundColor: '#333',
          height: 1,
          position: 'relative',
          top: 14,
          width: '100%',
        },
        fill: {
          backgroundColor: '#FFF',
          height: 1,
          width: '100%',
        },
        handle: {
          position: 'absolute',
          marginLeft: -7,
          height: 28,
          width: 28,
        },
        circle: {
          borderRadius: 12,
          position: 'relative',
          top: 8,
          left: 5,
          height: 12,
          width: 11,
        },
      }),
    };
    
    
    

    相关文章

      网友评论

          本文标题:react-native-video-controls 视频播放

          本文链接:https://www.haomeiwen.com/subject/udqmjltx.html