Custom HTML5 Video Player

이번 시간에는 html video tag를 커스터마이징 해 볼 것이다.

초기 소스코드는 위에 나와 있고, html과 css, js파일이 각각 하나씩, 그리고 예제 동영상으로 사용할 mp4파일이 있다. 초기 코드를 가지고 실행하면 다음과 같은 화면으로 나타난다.

동영상 화면 아래에 있는 컨트롤러는 현재 동작하지 않는 퍼블리싱만 된 요소들이다. 이제부터는 이 각각의 버튼과 슬라이더들이 동작하도록 js로직을 짜볼 것이다.

먼저 동영상 화면이나 play 버튼을 누르면 영상이 재생되도록 해볼 것이다. js파일은 크게 세 구간으로 나누어서 요소들을 변수로 정의해주고, 함수 로직을 구성하고, 각 요소에 함수들을 등록해주는 부분으로 관리한다.

동영상 재생 toggle 기능은 video와 toggle button에 togglePlay() function을 등록해준다.

또한 updateButton() function을 통해 toggle button의 모양도 변경해준다.

흥미운 점은 비디오의 상태가 paly / pause인 지에 따라 분기되는 로직을 삼항 연산자와 video[method]같은 방식으로 표현하여 코드를 깔끔하게 정리했다는 점이다. 비디오 상태에 따라 toggle시 비디오를 재생할지, 멈출지를 정해주고, 그에 따라 아이콘 모양도 갱신해주었다.

다음은 skip버튼을 활성화 시켜 볼 것이다.

//index.js/* Get Our Elements */const skipButtons = player.querySelectorAll('[data-skip]');/* Build out functions */
function skip() {
video.currentTime += parseFloat(this.dataset.skip);
}
/* Hook up the event listeners */
skipButtons.forEach(button => {
button.addEventListener('click', skip);
});

skipButton은 -10초와 +25초 두 가지 종류가 있으며, 모두 data-skip이라는 속성을 가지고 있다. querySelectorAll을 통해서 접근하고 forEach를 돌면서 각각에 skip() function을 등록해주었다.

skip() function은 클릭된 버튼의 data-skip 값만큼 비디오의 현재 시간에 더해주었다.

range들을 조정하기 위해서 hanleRangeUpdate() function을 만들고 비디오의 해당 속성을 range 값으로 업데이트 해주었다.

//index.js/* Get Our Elements */const ranges = player.querySelectorAll('.player__slider');/* Build out functions */
function handleRangeUpdate() {
video[this.name] = this.value;
}
/* Hook up the event listeners */
ranges.forEach(range => {
range.addEventListener('change', handleRangeUpdate);
range.addEventListener('mousemove', handleRangeUpdate);
});

다음은 동영상의 현재 재생 시간을 표시해주는 바를 만들어볼 것이다.

전체 동영상의 길이를 나타내는 progress와 현재 진행 상황을 나타내는 progressBar 두 요소를 사용한다.

//index.js/* Get Our Elements */const progress = player.querySelector('.progress');
const progressBar = player.querySelector('.progress__filled');
/* Build out functions */function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = `${percent}%`;
}
function scrub(e) {
const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
video.currentTime = scrubTime;
}
/* Hook up the event listeners */
video.addEventListener('timeupdate', handleProgress);
let mousedown = false;
progress.addEventListener('click', scrub);
progress.addEventListener('mousemove', e => {
mousedown && scrub(e);
});
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);

progressBar의 길이는 flex-basis의 %값으로 표현되어 있다. 따라서 동영상의 시간이 변경될 때마다 현재의 시간을 전체시간의 백분위로 구하여 flex-basis값을 변경해주었다.

scrub() function은 전체 progress의 길이 중에서 마우스의 위치가 어디에 위치해있는지를 파악하고 여기에 동영상 전체의 시간을 곱해 수정할 시간을 구해준다. 그리고 마우스를 클릭하거나 드래그 할 때 scrub() function을 발생시켜 동영상 시간을 갱신해준다.

드래그가 아니라 클릭하지 않고 마우스가 progress위에서 움직이기만 할 때는 해당 이벤트를 발생시키지 않기 위해 mousedown이라는 변수를 주고 마우스 down&up때마다 변수값을 바꿔주었다.

그리고

mousedown && scrub(e)

와 같이 표현하여 mousedown이 true일 때만 scrub() function이 실행되도록 깔끔하게 작성해주었다.

이렇게 동영상 재생 토글과 스킵 버튼, 볼륨 조절과 시간 조절 기능을 완성하였다.

Github Source Code

woohyun