Custom HTML5 video player controls

Posted by on February 22nd, 2018  •  0 Comments

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);
// }

 

Leave a Reply

Your email address will not be published. Required fields are marked *