Last active
November 14, 2017 20:04
-
-
Save dangdennis/5c175bccfd2c7e98260f5f6a57e29182 to your computer and use it in GitHub Desktop.
A first draft of a React component wrapped around the html5 video API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
class VideoPlayer extends Component { | |
constructor(props) { | |
super(props); | |
// Still determing the optimal method to set the initial state of the range inputs | |
// to match the video's states on initial load. Thus, the range will start at 0, but not | |
// match the volume and playback rate of the video until used. | |
// May just default playbackrate and volume at 0.5 (50%). | |
this.state = { | |
playing: false, | |
playbackRate: 0, | |
volume: 0, | |
progressBar: 0, | |
mouseDown: false | |
}; | |
this.togglePlay = this.togglePlay.bind(this); | |
this.toggleFullScreen = this.toggleFullScreen.bind(this); | |
this.skipBack = this.skipBack.bind(this); | |
this.skipForward = this.skipForward.bind(this); | |
this.handlePlayBackRate = this.handlePlayBackRate.bind(this); | |
this.handleVolume = this.handleVolume.bind(this); | |
this.handleProgress = this.handleProgress.bind(this); | |
this.mouseDownOn = this.mouseDownOn.bind(this); | |
this.mouseDownOff = this.mouseDownOff.bind(this); | |
this.scrub = this.scrub.bind(this); | |
} | |
componentDidMount() { | |
this.videoPlayer.addEventListener('timeupdate', this.handleProgress); | |
this.progressSetter = document.querySelector('.progress'); | |
this.progressSetter.addEventListener('click', this.scrub); | |
this.progressSetter.addEventListener( | |
'mousemove', | |
e => this.state.mouseDown && this.scrub(e) | |
); | |
this.progressSetter.addEventListener('mousedown', this.mouseDownOn); | |
this.progressSetter.addEventListener('mouseup', this.mouseDownOff); | |
} | |
componentDidUpdate(prevProps, prevState) { | |
const { volume, playbackRate } = this.state; | |
if (prevState.volume !== volume) { | |
this.videoPlayer.volume = volume; | |
} | |
if (prevState.playbackRate !== playbackRate) { | |
this.videoPlayer.playbackRate = playbackRate; | |
} | |
} | |
componentWillUnmount() { | |
this.videoPlayer.removeEventListener('timeupdate', this.handleProgress); | |
this.progressSetter.removeEventListener('click', this.scrub); | |
this.progressSetter.removeEventListener('mousemove', this.scrub); | |
this.progressSetter.removeEventListener('mousedown', this.mouseDownOn); | |
this.progressSetter.removeEventListener('mouseup', this.mouseDownOff); | |
} | |
/* Video API Methods | |
Methods in order: Play/Pause, FullScreen Toggle, Skip -10/+25, | |
Play Back Rate, Volume, Scrub | |
*/ | |
togglePlay() { | |
const { playing } = this.state; | |
if (playing) { | |
this.videoPlayer.play(); | |
} else { | |
this.videoPlayer.pause(); | |
} | |
this.setState({ playing: !playing }); | |
} | |
// Pulled from https://www.npmjs.com/package/screenfull | |
toggleFullScreen() { | |
if (this.videoPlayer.requestFullscreen) { | |
this.videoPlayer.requestFullscreen(); | |
} else if (this.videoPlayer.mozRequestFullScreen) { | |
this.videoPlayer.mozRequestFullScreen(); | |
} else if (this.videoPlayer.webkitRequestFullScreen) { | |
this.videoPlayer.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); | |
} | |
} | |
skipBack(sec) { | |
this.videoPlayer.currentTime -= parseFloat(sec); | |
} | |
skipForward(sec) { | |
this.videoPlayer.currentTime += parseFloat(sec); | |
} | |
handlePlayBackRate(e) { | |
this.setState({ playbackRate: e.target.value }); | |
} | |
handleVolume(e) { | |
this.setState({ volume: e.target.value }); | |
} | |
mouseDownOn() { | |
this.setState({ mouseDown: true }); | |
} | |
mouseDownOff() { | |
this.setState({ mouseDown: false }); | |
} | |
handleProgress() { | |
const percentTime = | |
this.videoPlayer.currentTime / this.videoPlayer.duration; | |
const percent = percentTime * 100; | |
this.setState({ progressBar: percent }); | |
} | |
scrub(e) { | |
const progress = document.querySelector('.progress'); | |
// e.offsetX = offset of the mouse position relative to the parent, aka the progress bar | |
// progress.offsetWidth = padding + width of the progress bar | |
const percentWidth = e.offsetX / progress.offsetWidth; | |
const scrubTime = percentWidth * this.videoPlayer.duration; | |
this.videoPlayer.currentTime = scrubTime; | |
} | |
render() { | |
const { progressBar } = this.state; | |
return ( | |
<div className="player"> | |
<video | |
ref={(video) => { | |
this.videoPlayer = video; | |
}} | |
className="player__video viewer" | |
src="https://player.vimeo.com/external/194837908.sd.mp4?s=c350076905b78c67f74d7ee39fdb4fef01d12420&profile_id=164" | |
> | |
<track kind="captions" srcLang="en" /> | |
</video> | |
<div className="player__controls"> | |
<div className="progress"> | |
<div | |
className="progress__filled" | |
style={{ flexBasis: `${progressBar}%` }} | |
/> | |
</div> | |
<button | |
className="player__button toggle" | |
title="Toggle Play" | |
onClick={this.togglePlay} | |
> | |
{this.state.playing ? '►' : '❚ ❚'} | |
</button> | |
<input | |
type="range" | |
name="volume" | |
className="player__slider" | |
min="0" | |
max="1" | |
step="0.05" | |
onChange={this.handleVolume} | |
/> | |
<input | |
type="range" | |
name="playbackRate" | |
className="player__slider" | |
min="0.5" | |
max="2" | |
step="0.1" | |
onChange={this.handlePlayBackRate} | |
/> | |
<button className="player__button" onClick={() => this.skipBack(10)}> | |
« 10s | |
</button> | |
<button | |
className="player__button" | |
onClick={() => this.skipForward(25)} | |
> | |
25s » | |
</button> | |
<button onClick={this.toggleFullScreen} className="player__button"> | |
[ – ] | |
</button> | |
</div> | |
</div> | |
); | |
} | |
} | |
export default VideoPlayer; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment