We had an inch of snow today. I took a brisk walk around the block on the sunny, but slippery sidewalks before getting back to work programming.
Yesterday our exercise was to remove the default html5 video player controls, then substitute a handbuilt controller. As an added challenge, they asked if we could program in a clickable playhead. They gave us a couple clues, but thinking it through was a brain squeezer. There was a 190 pixel wide area where the progress bar walked across as the video played.
Somehow that area needed to become clickable. When clicked, the video should play at that percentage. Meaning, if you click half way across the progress bar area, the video should jump to the halfway point.
I can’t post the video here in WordPress without a lot of trouble from width properties. So instead I’m linking to it here as an external page. But the code is shown below. The magic in the clickable progress bar is on line 157: function jumpTo(e){…}
My solution isn’t a true draggable playhead, but it does at least navigate, and displays a nice red bar when clicked.
var media = document.querySelector('video');
var controls = document.querySelector('.controls');
var play = document.querySelector('.play');
var stop = document.querySelector('.stop');
var rwd = document.querySelector('.rwd');
var fwd = document.querySelector('.fwd');
var timerWrapper = document.querySelector('.timer');
var timer = document.querySelector('.timer span');
var timerBar = document.querySelector('.timer div');
var intervalRwd;
var intervalFwd;
//removes the default html5 controls
media.removeAttribute('controls');
controls.style.visibility = 'visible';
play.addEventListener('click', playPauseMedia);
function playPauseMedia(){
/*below fixes a problem that occurs if they try to stop
while fast forward or fast reverse is running*/
rwd.classList.remove('active');
fwd.classList.remove('active');
clearInterval(intervalRwd);
clearInterval(intervalFwd);
if(media.paused) {
play.setAttribute('data-icon', 'u');
media.play();
} else {
play.setAttribute('data-icon','P');
media.pause();
}
}//end function playPauseMedia
stop.addEventListener('click', stopMedia);
media.addEventListener('ended', stopMedia);
function stopMedia() {
media.pause();
media.currentTime = 0;
play.setAttribute('data-icon','P');
/*below fixes a problem that occurs if they try to stop
while fast forward or fast reverse is running*/
rwd.classList.remove('active');
fwd.classList.remove('active');
clearInterval(intervalRwd);
clearInterval(intervalFwd);
}
rwd.addEventListener('click', mediaBackward);
fwd.addEventListener('click', mediaForward);
function mediaBackward(){
clearInterval(intervalFwd);
fwd.classList.remove('active');
if(rwd.classList.contains('active')){
rwd.classList.remove('active');
clearInterval(intervalRwd);
media.play();
} else {
rwd.classList.add('active');
media.pause();
/*
If it hasn't yet been set, we add the active
class to the rwd button using classList.add(),
pause the video using HTMLMediaElement.pause(),
then set the intervalRwd variable to equal
a setInterval() call. When invoked, setInterval()
creates an active interval, meaning that
it runs the function given as the first
parameter every x milliseconds,
where x is the value of the 2nd parameter.
So here we are running the windBackward()
function every 200 milliseconds — we'll
use this function to wind the video backwards
constantly. To stop a setInterval() running,
you have to call clearInterval(),
giving it the identifying name of the
interval to clear, which in this case
is the variable name intervalRwd
*/
intervalRwd = setInterval(windBackward, 200);
}
}//end function mediaBackward
function mediaForward(){
clearInterval(intervalRwd);
rwd.classList.remove('active');
if(fwd.classList.contains('active')){
fwd.classList.remove('active');
clearInterval(intervalFwd);
media.play();
} else {
fwd.classList.add('active');
media.pause();
intervalFwd = setInterval(windForward, 200);//setInterval() takes two parameters: a function & milliseconds
}
}//end function mediaForward
function windBackward(){
if(media.currentTime <= 3){
//rwd.classList.remove('active');
//clearInterval(intervalRwd);
stopMedia();
} else {
media.currentTime -= 3;
}
}//end function windBackward
function windForward(){
if(media.currentTime >= media.duration - 3){
//fwd.classList.remove('active');
//clearInterval(intervalFwd);
stopMedia();
} else {
media.currentTime += 3;
}
}//end function windBackward
media.addEventListener('timeupdate', setTime);
function setTime(){
var minutes = Math.floor(media.currentTime / 60);//floor rounds down to nearest integer, if 90 seconds, returns: 1
var seconds = Math.floor(media.currentTime - minutes * 60);//if 90 seconds, returns: 30
var minuteValue;
var secondValue;
if(minutes < 10){
minuteValue = '0' + minutes;
} else {
minuteValue = minutes;
}
if (seconds < 10){
secondValue = '0' + seconds;
} else {
secondValue = seconds;
}
var mediaTime = minuteValue + ':' + secondValue;
timer.textContent = mediaTime;
//any elements width is found via: element.clientWidth
var barLength = timerWrapper.clientWidth * (media.currentTime/media.duration);
timerBar.style.width = barLength + 'px';
}//end function setTime()
timerWrapper.addEventListener('click', jumpTo)
var durationPercent;
function jumpTo(e){
durationPercent = Math.floor((e.x - 273) / 2);
media.currentTime = durationPercent * (media.duration / 100);
timerBar.classList.add('playHead');
console.log('xMouse: ' + e.x) + ', ' + console.log('yMouse: ' + e.y);
}
// timerWrapper.onclick = function(e){
// console.log('xMouse: ' + e.x) + ', ' + console.log('yMouse: ' + e.y);
// }