Skip to content

Instantly share code, notes, and snippets.

@dangdennis
Last active November 14, 2017 20:04
Show Gist options
  • Save dangdennis/5c175bccfd2c7e98260f5f6a57e29182 to your computer and use it in GitHub Desktop.
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
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">
[ &ndash; ]
</button>
</div>
</div>
);
}
}
export default VideoPlayer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment