Skip to content

Instantly share code, notes, and snippets.

@bartimaeus
Last active August 16, 2018 07:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bartimaeus/8dae7765eb3489e5591da4ebb7fbedce to your computer and use it in GitHub Desktop.
Save bartimaeus/8dae7765eb3489e5591da4ebb7fbedce to your computer and use it in GitHub Desktop.
Lesson 03
/*
- Make the Play button work
- Make the Pause button work
- Disable the play button if it's playing
- Disable the pause button if it's not playing
- Make the PlayPause button work
- Make the JumpForward button work
- Make the JumpBack button work
- Make the progress bar work
- change the width of the inner element to the percentage of the played track
- add a click handler on the progress bar to jump to the clicked spot
Here is the audio API you'll need to use, `audio` is the <audio/> dom nod
instance, you can access it as `this.audio` in `AudioPlayer`
```js
// play/pause
audio.play()
audio.pause()
// change the current time
audio.currentTime = audio.currentTime + 10
audio.currentTime = audio.currentTime - 30
// know the duration
audio.duration
// values to calculate relative mouse click position
// on the progress bar
event.clientX // left position *from window* of mouse click
const rect = node.getBoundingClientRect()
rect.left // left position *of node from window*
rect.width // width of node
```
Other notes about the `<audio/>` tag:
- You can't know the duration until `onLoadedData`
- `onTimeUpdate` is fired when the currentTime changes
- `onEnded` is called when the track plays through to the end and is no
longer playing
Good luck!
*/
import "./index.css";
import React from "react";
// import PropTypes from "prop-types";
import podcast from "./podcast.mp4";
import mario from "./mariobros.mp3";
import FaPause from "react-icons/lib/fa/pause";
import FaPlay from "react-icons/lib/fa/play";
import FaRepeat from "react-icons/lib/fa/repeat";
import FaRotateLeft from "react-icons/lib/fa/rotate-left";
// Polyfill for React 15 => react-create-context
const AudioContext = React.createContext();
class AudioPlayer extends React.Component {
state = {
isPlaying: false,
isLoaded: false,
currentTime: 0,
duration: 0,
play: () => {
this.setState(
{
isPlaying: true
},
() => {
this.audio.play();
}
);
},
pause: () => {
this.setState(
{
isPlaying: false
},
() => {
this.audio.pause();
}
);
},
jumpForward: () => {
this.audio.currentTime = this.audio.currentTime + 30;
},
jumpBack: () => {
this.audio.currentTime = this.audio.currentTime - 30;
},
setPlayHead: event => {
let { left, width } = event.currentTarget.getBoundingClientRect();
let percentage = (event.clientX - left) / width;
this.audio.currentTime = this.audio.duration * percentage;
}
};
render() {
return (
<AudioContext.Provider value={this.state}>
<div className="audio-player">
<audio
src={this.props.source}
onTimeUpdate={() =>
this.setState({
currentTime: this.audio.currentTime
})
}
onLoadedData={() =>
this.setState({ isLoaded: true, duration: this.audio.duration })
}
onEnded={() => this.setState({ isPlaying: false, currentTime: 0 })}
ref={n => (this.audio = n)}
/>
{this.props.children}
</div>
</AudioContext.Provider>
);
}
}
class Play extends React.Component {
render() {
return (
<AudioContext.Consumer>
{context => {
return (
<button
className="icon-button"
onClick={context.play}
disabled={context.isPlaying}
title="play"
>
<FaPlay />
</button>
);
}}
</AudioContext.Consumer>
);
}
}
class Pause extends React.Component {
render() {
return (
<AudioContext.Consumer>
{context => (
<button
className="icon-button"
onClick={context.pause}
disabled={!context.isPlaying}
title="pause"
>
<FaPause />
</button>
)}
</AudioContext.Consumer>
);
}
}
class PlayPause extends React.Component {
render() {
return (
<AudioContext.Consumer>
{context => {
return !context.isPlaying ? <Play /> : <Pause />;
}}
</AudioContext.Consumer>
);
}
}
class JumpForward extends React.Component {
render() {
return (
<AudioContext.Consumer>
{context => (
<button
className="icon-button"
onClick={context.jumpForward}
disabled={!context.isPlaying}
title="Forward 10 Seconds"
>
<FaRepeat />
</button>
)}
</AudioContext.Consumer>
);
}
}
class JumpBack extends React.Component {
render() {
return (
<AudioContext.Consumer>
{context => (
<button
className="icon-button"
onClick={context.jumpBack}
disabled={!context.isPlaying}
title="Back 10 Seconds"
>
<FaRotateLeft />
</button>
)}
</AudioContext.Consumer>
);
}
}
class Progress extends React.Component {
render() {
return (
<AudioContext.Consumer>
{context => (
<div className="progress" onClick={context.setPlayHead}>
<div
className="progress-bar"
style={{
width: `${
context.isLoaded
? context.currentTime / context.duration * 100
: 0
}%`
}}
/>
</div>
)}
</AudioContext.Consumer>
);
}
}
class ProgressCounter extends React.Component {
formatTime = timeInSeconds => {
let seconds = Math.floor(timeInSeconds % 60);
let minutes = Math.floor(timeInSeconds / 60.0);
return `${Math.round(minutes)
.toString()
.padStart(2, "0")}:${Math.round(seconds)
.toString()
.padStart(2, "0")}`;
};
render() {
return (
<AudioContext.Consumer>
{context => (
<div className="counter" style={{ float: "right" }}>
{context.isLoaded && (
<React.Fragment>
<span className="current-time">
{this.formatTime(context.currentTime)}
</span>{" "}
/
<span className="total-time">
{this.formatTime(context.duration)}
</span>
</React.Fragment>
)}
</div>
)}
</AudioContext.Consumer>
);
}
}
const Exercise = () => (
<div className="exercise">
<AudioPlayer source={mario}>
<PlayPause /> <span className="player-text">Mario Bros. Remix</span>
<Progress />
<ProgressCounter />
</AudioPlayer>
<AudioPlayer source={podcast}>
<PlayPause /> <JumpBack /> <JumpForward />{" "}
<span className="player-text">Workshop.me Podcast Episode 02</span>
<Progress />
<ProgressCounter />
</AudioPlayer>
</div>
);
export default Exercise;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment