Custom Video Player
On day 11 of JS-30 we made a custom video player in HTML5 and using JavaScript and CSS (for styling the control buttons) added a lot of features and functionalities to the video player.
In this lesson I gained a better understanding of how video, and by extension audio, elements can be manipulated both in style and functionality.
So let's get right into it.
Here is the html we had by default so you don't get confused as to which element has what class.
<div class="player">
<video class="player__video viewer" src="652333414.mp4"></video>
<div class="player__controls">
<div class="progress">
<div class="progress__filled"></div>
</div>
<button class="player__button toggle" title="Toggle Play">▶</button>
<input
type="range"
name="volume"
class="player__slider"
min="0"
max="1"
step="0.05"
value="1"
/>
<input
type="range"
name="playbackRate"
class="player__slider"
min="0.5"
max="2"
step="0.1"
value="1"
/>
<button data-skip="-10" class="player__button">« 10s</button>
<button data-skip="25" class="player__button">25s »</button>
</div>
</div>
The buttons and sliders were styled by default now we'll add functionality to them using JavaScript.
Before we start one advice, Always turnoff autoplay
on video and audio unless the user is expecting it.
Toggle Play
First we'll add a togglePlay
function and when this is called it will either going to play or pause the video. We want to play/pause when user either clicks the play/pause button or clicks on the screen so we'll add click
event listeners to both the button and the screen.
const video = player.querySelector(".viewer");
const toggle = player.querySelector(".toggle");
video.addEventListener("click", togglePlay);
toggle.addEventListener("click", togglePlay);
function togglePlay() {
if (video.paused) {
video.play();
} else {
video.pause();
}
}
We can shorten the code in if-else
by substituting it with
const method = video.paused ? "play" : "pause";
video[method]();
Updating play/pause button
Now that we have added play/pause functionality we should also update the play/pause button along with it.
Now we would now do that inside the togglePlay
function because videos can be paused by other ways as well like a plugin or if a pop up opens so what we will do is we'll listen for the video whenever it is paused. So then whatever may be the reason of the video stopping we'll update our buttons.
video.addEventListener("play", updateButton);
video.addEventListener("pause", updateButton);
function updateButton() {
const icon = this.paused ? "▶" : "⏸";
toggle.textContent = icon;
}
Skip Buttons
We have added 2 skip buttons, one takes the video backward by 10sec and the other takes the video forward by 25sec, and we added that in the elements html using data-
property.
<button data-skip="-10" class="player__button">« 10s</button>
<button data-skip="25" class="player__button">25s »</button>
Now we'll make use of the value stored in data attribute.
const video = player.querySelector(".viewer");
const skipButtons = player.querySelectorAll("[data-skip]");
skipButtons.forEach((button) => button.addEventListener("click", skip));
function skip() {
// console.log(this.dataset);
// console.log(this.dataset.skip);
video.currentTime += parseFloat(this.dataset.skip);
}
Here you console and see that this.dataset
contains an object that has the skip value in it, so we'll use that value and update our video's currentime.
Range Sliders
We have added 2 input elements of type
range, one for volume and the other for playback speed.
We intentionally added a name attribute with the same name as the property a video has that is volume
and playbackRate
so that later we can make use of that name.
<input
type="range"
name="volume"
class="player__slider"
min="0"
max="1"
step="0.05"
value="1"
/>
<input
type="range"
name="playbackRate"
class="player__slider"
min="0.5"
max="2"
step="0.1"
value="1"
/>
Now we'll select the sliders and update the playback speed and volume.
const ranges = player.querySelectorAll(".player__slider");
ranges.forEach((range) => range.addEventListener("change", handleRangeUpdate));
ranges.forEach((range) =>
range.addEventListener("mousemove", handleRangeUpdate)
);
function handleRangeUpdate() {
// console.log(this.name);
// console.log(this.value);
video[this.name] = this.value;
}
Here this.name
contains the name of the property and this.value
contains the value to which we want to update the property to.
Progress Bar
We want our progress bar to update in real time and also if a user clicks on it or drags it the video playback should update accordingly.
Also we do not want the handleProgress()
fucntion to run every second, rather we'll listen for an event called timeupdate
which is fired everytime the timestamp changes.
Another challenge is how are we going to make the progress bar increase/decrease according to video current time. We are going to make a percentage because that is how we implemented it in CSS using flex-basis
, we initially start with a 0% and we will be updating that flex basis value (sort of like updating the width of the progress bar) and it will correspond with the video progress
.progress__filled {
width: 50%;
background: #ffc600;
flex: 0;
flex-basis: 0%;
}
Now inside our handleProgress()
function we will calculate the pecentage.
const video = player.querySelector(".viewer");
video.addEventListener("timeupdate", handleProgress);
function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = `${percent}%`;
}
The currentTime
and duration
are properties on video.
Scrub
We want to add the functionality where someone clicks/holds & drags on the progress bar and video is adjusted accordingly.
To make sure user has clicked the mouse while dragging we maintain flag and update it accordingly to the mouse click by using mousedown
and mouseup
event listeners.
Inside the function we can console log and see the mouse event has properties of which we are going to use offsetX
which tells us exactly the user clicked and the values are relative to the progress bar, also offsetWidth
tells the exact width of the progress bar, so by dividing them we get the percentage our video playback should be so after multiplying it with the video.duration
we can obtain the time where our video playback should be and hence we update the video.currentTime
.
const progress = player.querySelector(".progress");
let mousedown = false;
progress.addEventListener("click", scrub);
progress.addEventListener("mousemove", (e) => {
if (mousedown) {
scrub(e);
}
});
progress.addEventListener("mousedown", () => (mousedown = true));
progress.addEventListener("mouseup", () => (mousedown = false));
function scrub(e) {
// console.log(e);
const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
video.currentTime = scrubTime;
}
and with this our project for the day was completed.
GitHub repo:
Blog on Day-10 of javascript30
Blog on Day-9 of javascript30
Blog on Day-8 of javascript30
Follow me on Twitter
Follow me on Linkedin
DEV Profile
You can also do the challenge at javascript30
Thanks @wesbos , WesBos to share this with us! 😊💖
Please comment and let me know your views
Top comments (0)