translation-waveform-audio
Version:
Waveform react audioplayer
306 lines (292 loc) • 10.4 kB
JSX
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
const HOCAudio = Audio => class HOCAudioComponent extends React.Component {
static propTypes = {
playlist: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
src: PropTypes.string,
img: PropTypes.string,
comments: PropTypes.arrayOf(PropTypes.shape({
time: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
content: PropTypes.string
}))
})).isRequired
};
constructor(props) {
super(props);
if (!this.props.playlist || this.props.playlist.length === 0) {
throw new Error('You should provide a playlist which contains at least 1 audio object');
}
if (this.props.fullPlayer) {
this.props.playlist.forEach((song) => {
if (!song.img) {
throw new Error('You should provide song.img when the fullPlayer is enabled.');
}
if (this.props.comment && !song.comments) {
throw new Error('You turned on commenting function and you need to provide the `comments` field for each song in the playlist');
}
});
}
// bind methods
// this.playNext = props.recordButton2; // A boolean to determine whether to play the next song or not
this.loadSrc = this.loadSrc.bind(this);
this.togglePlayPause = this.togglePlayPause.bind(this);
this.onCanPlay = this.onCanPlay.bind(this);
this.onEnded = this.onEnded.bind(this);
this.onPlay = this.onPlay.bind(this);
this.onPause = this.onPause.bind(this);
// this.onTimeUpdate = this.onTimeUpdate.bind(this);
this.setVolume = this.setVolume.bind(this);
this.setProgress = this.setProgress.bind(this);
this.skipToNext = this.skipToNext.bind(this);
this.skipToPrevious = this.skipToPrevious.bind(this);
this.togglePlayingState = this.togglePlayingState.bind(this);
this.playEventHandler = this.playEventHandler.bind(this);
this.pauseEventHandler = this.pauseEventHandler.bind(this);
this.skipToNextEventHandler = this.skipToNextEventHandler.bind(this);
this.skipToPreviousEventHandler = this.skipToPreviousEventHandler.bind(this);
const discardPileSize = Math.ceil(props.playlist.length / 2);
this.state = {
playing: false,
currentPlaylistPos: 0,
playingState: 0, // 0: cycle, 1: repeat, 2: shuffle
progress: 0,
duration: 0,
volume: 1,
shuffleState: {
size: discardPileSize,
drawPile: [...Array(props.playlist.length).keys()], // initialise full playlist indexes
discardPile: []
}
};
}
onCanPlay() {
// console.log('audio oncanplay');
// this.playNext = this.state.playing;
this.setState({
duration: this.audioElement.duration
});
}
onPlay() {
// console.log('audio onplay');
this.playNext = true;
this.setState({ playing: true });
this.intervalId = setInterval(() => {
this.setState({ progress: this.audioElement.currentTime });
}, 900);
}
onPause() {
// console.log('audio onpause');
this.setState({ playing: false });
this._clearInterval();
}
onEnded() {
// console.log('audio onended');
if (this.playNext) {
this.handleEndedProgress();
}
}
// onTimeUpdate(e) {
// this.setState({ progress: this.audioElement.currentTime });
// console.log(e.target.currentTime);
// }
handleEndedProgress() {
this.playNext = true;
switch (this.state.playingState) {
case 0: {
this.skipToNext();
break;
}
case 1: {
this.setState({ progress: 0 });
this.audioElement.currentTime = 0;
this.togglePlayPause();
break;
}
case 2: {
const ss = this.state.shuffleState;
const currentSongIndex = ss.drawPile.indexOf(this.state.currentPlaylistPos);
const newDrawPile = ss.drawPile;
const newDiscardPile = ss.discardPile;
if (currentSongIndex !== -1) {
newDrawPile.splice(currentSongIndex, 1);
newDiscardPile.push(this.state.currentPlaylistPos);
}
const index = Math.floor(Math.random() * ss.drawPile.length);
const sliced = newDrawPile.slice(index, index + 1);
this.setState({
currentPlaylistPos: sliced[0],
shuffleState: {
size: ss.size,
drawPile: newDrawPile,
discardPile: newDiscardPile
}
});
if (newDiscardPile.length === ss.size) {
const shiftedElement = newDiscardPile.shift();
newDrawPile.push(shiftedElement);
this.setState({
shuffleState: {
size: ss.size,
drawPile: newDrawPile,
discardPile: newDiscardPile
}
});
}
this.loadSrc();
break;
}
default: {
// console.log('onend BUG!!!');
break;
}
}
}
setVolume(volume) {
// console.log(`volume is set to ${volume}`);
this.audioElement.volume = volume;
this.setState({ volume });
}
setProgress(newProgress) {
let progress = newProgress;
const duration = this.audioElement.duration;
if (progress > duration) {
progress = duration;
}
this.audioElement.currentTime = progress;
this.setState({ progress });
// console.log(`progress is set to ${progress}`);
}
_clearInterval() {
// console.log('interval cleared');
if (this.intervalId !== null) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
loadSrc() {
// console.log('load src');
if (this.state.currentPlaylistPos < this.props.playlist.length) {
this.audioElement.src = this.props.playlist[this.state.currentPlaylistPos].src;
this.audioElement.load();
if (this.playNext) {
this.audioElement.play();
}
this.setState({ progress: 0 });
this._clearInterval();
}
}
togglePlayPause() {
// console.log('toggle playpause');
if (this.state.playing) {
// console.log('togglePlayPause(): playing');
this.audioElement.pause();
} else if (this.audioElement.currentTime === this.audioElement.duration) {
this.handleEndedProgress();
} else {
this.audioElement.play();
}
}
skipToNext() {
// console.log('skip to next');
this.state.currentPlaylistPos = this.setCycleNumPos(this.state.currentPlaylistPos, 1, this.props.playlist.length);
this.loadSrc();
}
skipToPrevious() {
// console.log('skip to next');
this.state.currentPlaylistPos = this.setCycleNumPos(this.state.currentPlaylistPos, -1, this.props.playlist.length);
this.loadSrc();
}
togglePlayingState() {
this.setState({
playingState: this.setCycleNumPos(this.state.playingState, 1, 3)
});
}
setCycleNumPos(currentVal, change, length) {
let newPos = currentVal + change;
if (newPos >= length) {
newPos -= length;
}
if (newPos < 0) {
newPos += length;
}
return newPos;
}
render() {
const newProps = Object.assign({}, {
name: this.props.playlist[this.state.currentPlaylistPos].name,
CommentsWrapperStates: {
songImageSrc: this.props.playlist[this.state.currentPlaylistPos].img,
comments: (this.props.fullPlayer && this.props.comment) ? this.props.playlist[this.state.currentPlaylistPos].comments : null,
pos: this.state.currentPlaylistPos
},
controlStates: {
playing: this.state.playing,
playingState: this.state.playingState,
volume: this.state.volume * 100
},
controlCallbacks: {
setVolume: this.setVolume,
togglePlayPause: this.togglePlayPause,
togglePlayingState: this.togglePlayingState,
skipToNext: this.skipToNext,
skipToPrevious: this.skipToPrevious
},
timelineStates: {
playing: this.state.playing,
progress: this.state.progress,
duration: this.state.duration
},
timelineCallbacks: {
setProgress: this.setProgress,
togglePlayPause: this.togglePlayPause
},
children: this.props.children
}, this.props);
return <Audio {...newProps} />;
}
playEventHandler() {
this.audioElement.play();
}
pauseEventHandler() {
this.audioElement.pause();
}
skipToNextEventHandler() {
this.skipToNext();
}
skipToPreviousEventHandler() {
this.skipToPrevious();
}
componentDidMount() {
// console.log('Audio mounted!');
// set audio element event listeners
console.log(this);
this.audioElement = document.createElement('audio');
this.audioElement.addEventListener('canplay', this.onCanPlay);
this.audioElement.addEventListener('ended', this.onEnded);
this.audioElement.addEventListener('play', this.onPlay);
this.audioElement.addEventListener('pause', this.onPause);
this.audioElement.addEventListener('volumechange', this.onVolumeChange);
this.loadSrc();
this.setState({ volume: this.audioElement.volume });
ReactDOM.findDOMNode(this).addEventListener('audio-play', this.playEventHandler);
ReactDOM.findDOMNode(this).addEventListener('audio-pause', this.pauseEventHandler);
ReactDOM.findDOMNode(this).addEventListener('audio-skip-to-next', this.skipToNextEventHandler);
ReactDOM.findDOMNode(this).addEventListener('audio-skip-to-previous', this.skipToPreviousEventHandler);
}
componentWillUnmount() {
this._clearInterval();
this.audioElement.removeEventListener('canplay', this.onCanPlay);
this.audioElement.removeEventListener('ended', this.onEnded);
this.audioElement.removeEventListener('play', this.onPlay);
this.audioElement.removeEventListener('pause', this.onPause);
this.audioElement.removeEventListener('volumechange', this.onVolumeChange);
this.audioElement = null;
ReactDOM.findDOMNode(this).removeEventListener('audio-play', this.playEventHandler);
ReactDOM.findDOMNode(this).removeEventListener('audio-pause', this.pauseEventHandler);
ReactDOM.findDOMNode(this).removeEventListener('audio-skip-to-next', this.skipToNextEventHandler);
ReactDOM.findDOMNode(this).removeEventListener('audio-skip-to-previous', this.skipToPreviousEventHandler);
}
};
export default HOCAudio;