diff --git a/CHANGELOG.md b/CHANGELOG.md index 28957be..05cefe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0 + - The seekBar is now seek-able. + - Added `seekBarKnob` to `customStyles` to style the seek bar knob. + ## 0.2.0 - `resizeMode` prop added, now defaults to `contain` diff --git a/README.md b/README.md index 5d8a35a..48497d0 100644 --- a/README.md +++ b/README.md @@ -46,14 +46,16 @@ All other props are passed to the react-native-video component. - playIcon - seekBar - seekBarProgress + - seekBarKnob - thumbnail - playButton - playArrow ## Future features -- [ ] Make seek bar seekable. +- [X] Make seek bar seekable. - [x] Make player customizable. - [ ] Add volume control -- [ ] Add fullscreen button +- [X] Add fullscreen button + - [ ] Add fullscreen button for android - [ ] Add loader diff --git a/index.js b/index.js index 1d97d96..ff150e4 100644 --- a/index.js +++ b/index.js @@ -52,6 +52,15 @@ const styles = StyleSheet.create({ seekBarProgress: { backgroundColor: '#F00', }, + seekBarKnob: { + width: 20, + height: 20, + borderRadius: 10, + marginHorizontal: -10, + marginVertical: -8.5, + backgroundColor: '#F00', + transform: [{ scale: 0.8 }], + }, overlayButton: { flex: 1, }, @@ -69,8 +78,14 @@ export default class VideoPlayer extends Component { isMuted: props.defaultMuted, isControlsVisible: !props.hideControlsOnStart, duration: 0, + isSeeking: false, }; + this.seekBarWidth = 200; + this.wasPlayingBeforeSeek = props.autoplay; + this.seekTouchStart = 0; + this.seekProgressStart = 0; + this.onLayout = this.onLayout.bind(this); this.onStartPress = this.onStartPress.bind(this); this.onProgress = this.onProgress.bind(this); @@ -80,6 +95,10 @@ export default class VideoPlayer extends Component { this.onMutePress = this.onMutePress.bind(this); this.showControls = this.showControls.bind(this); this.onToggleFullScreen = this.onToggleFullScreen.bind(this); + this.onSeekBarLayout = this.onSeekBarLayout.bind(this); + this.onSeekGrant = this.onSeekGrant.bind(this); + this.onSeekRelease = this.onSeekRelease.bind(this); + this.onSeek = this.onSeek.bind(this); } componentDidMount() { @@ -105,6 +124,9 @@ export default class VideoPlayer extends Component { } onProgress(event) { + if (this.state.isSeeking) { + return; + } if (this.props.onProgress) { this.props.onProgress(event); } @@ -157,6 +179,49 @@ export default class VideoPlayer extends Component { this.player.presentFullscreenPlayer(); } + onSeekBarLayout({ nativeEvent }) { + this.seekBarWidth = nativeEvent.layout.width; + } + + onSeekStartResponder() { + return true; + } + + onSeekMoveResponder() { + return true; + } + + onSeekGrant(e) { + this.seekTouchStart = e.nativeEvent.pageX; + this.seekProgressStart = this.state.progress; + this.wasPlayingBeforeSeek = this.state.isPlaying; + this.setState({ + isSeeking: true, + isPlaying: false, + }); + } + + onSeekRelease() { + this.setState({ + isSeeking: false, + isPlaying: this.wasPlayingBeforeSeek, + }); + this.showControls(); + } + + onSeek(e) { + const diff = e.nativeEvent.pageX - this.seekTouchStart; + const ratio = 100 / this.seekBarWidth; + const progress = this.seekProgressStart + ((ratio * diff) / 100); + + + this.setState({ + progress, + }); + + this.player.seek(progress * this.state.duration); + } + getSizeStyles() { const { videoWidth, videoHeight } = this.props; const { width } = this.state; @@ -225,6 +290,7 @@ export default class VideoPlayer extends Component { }, customStyles.seekBar, ]} + onLayout={this.onSeekBarLayout} > + { !fullWidth ? ( + + ) : null } ); @@ -366,6 +448,8 @@ VideoPlayer.propTypes = { playIcon: Icon.propTypes.style, seekBar: View.propTypes.style, seekBarProgress: View.propTypes.style, + seekBarKnob: View.propTypes.style, + seekBarKnobSeeking: View.propTypes.style, thumbnail: Image.propTypes.style, playButton: TouchableOpacity.propTypes.style, playArrow: Icon.propTypes.style,