Day 11 - Custom Video Player

비디오 플레이어를 직접 구현해보는 과제이다. 구현이 엄청 복잡할 줄 알았는데, video 태그에 대한 메소드들만 적당히 다룰 줄 안다면 구현하기 어렵지는 않을 것 같다.

여느 사이트에서나 볼 수 있는 비디오 플레이어를 플레이 버튼, 볼륨 조절, 배속 조절, 스킵, 재생 진행바까지 구현하는 것이 목표이다.

재생

1
2
3
4
function togglePlay() {
const method = video.paused ? "play" : "pause";
video[method]();
}

처음에 보면 대체 무슨 말인가 할 수도 있다.

이 코드는 정확히 밑의 코드와 같다.

1
2
3
4
5
6
7
function togglePlay() {
if (video.paused) {
video.play();
} else {
video.pause();
}
}

사실 난 가독성은 밑의 코드가 더 나은 것 같다.

이벤트 걸기

1
2
video.addEventListener("click", togglePlay);
toggle.addEventListener("click", togglePlay);

비디오를 클릭하나 재생버튼을 클릭하나 실행하는 함수는 같아야 한다.

재생 버튼 변경

이제 재생 여부가 바뀔 때마다 삼각형 플레이 버튼과 일시정지 버튼을 서로 바꿔준다.

1
2
3
4
function updateButton() {
const icon = this.paused ? "►" : "❚ ❚";
toggle.textContent = icon;
}

이벤트 걸기

1
2
video.addEventListener("play", updateButton);
video.addEventListener("pause", updateButton);

video 객체에는 play, pause 이벤트가 있다.(있더라)

스킵

1
2
3
function skip() {
video.currentTime += parseFloat(this.dataset.skip);
}

video.currentTime으로 현재 재생 시간을 조절할 수 있다. dataset은 여전히 많이 사용되는 것 같으니 다시 상기하자.

이벤트 걸기

1
skipButtons.forEach((button) => button.addEventListener("click", skip));

forEach 구문은 이제 물구나무 서서도 작성할 줄 알아야 한다.

볼륨, 배속 조절

1
2
3
function handleRangeUpdate() {
video[this.name] = this.value;
}

볼륨, 배속 조절은 모두 input type="range"로 만들어졌기 때문에 DOM 객체의 name과 value를 가져와서 한 함수로 각각을 모두 조절할 수 있다.

이벤트 걸기

1
2
3
4
ranges.forEach((range) => range.addEventListener("change", handleRangeUpdate));
ranges.forEach((range) =>
range.addEventListener("mousemove", handleRangeUpdate)
);

ranges 객체는 볼륨, 배속 조절 2가지 태그를 모두 포함한다. mousemove에도 이벤트를 거는 이유는 드래그 상태로 움직일 때 실시간으로 바꿔야 하니까.

재생 진행바 (시간 조절)

우선 재생 진행바를 클릭할 때 해당 위치로 재생 시간을 조절하기 위해 scrub 함수를 작성한다.

1
2
3
4
function scrub(e) {
const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
video.currentTime = scrubTime;
}

이벤트를 받는 DOM 객체의 x 좌표와 가로 크기, 비디오 재생 시간으로 재생 시간을 설정한다. 이 함수는 마우스 드래그, 클릭 이벤트에 모두 적용되어야 한다.

이벤트 걸기

1
2
3
4
5
let mousedown = false;
progress.addEventListener("click", scrub);
progress.addEventListener("mousemove", (e) => mousedown && scrub(e));
progress.addEventListener("mousedown", () => (mousedown = true));
progress.addEventListener("mouseup", () => (mousedown = false));

여기서 mousedown && scrub(e)mousemove 이벤트일 때 mousedown이면 scrub 함수를 실행한다. 신박하다.

재생 진행바 (업데이트)

이제 재생이 진행될 때 진행바도 같이 올라가면 끝이다.

1
2
3
4
function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = `${percent}%`;
}

이벤트 걸기

1
video.addEventListener("timeupdate", handleProgress);

현재 재생 시간이 바뀔 때 마다 발생하는 timeupdate라는 이벤트도 있다.(있더라)

응용

전체화면 만들기

전체화면 만들기도 마지막에 해보라고 해서 만들어봤다.

1
2
3
4
5
6
7
8
9
10
11
12
13
const fullButton = player.querySelector("#fullscreen");

function toggleFullScreen() {
if (video.requestFullScreen) {
video.requestFullScreen();
} else if (video.webkitRequestFullScreen) {
video.webkitRequestFullScreen();
} else if (video.mozRequestFullScreen) {
video.mozRequestFullScreen();
}
}

fullButton.addEventListener("click", toggleFullScreen);

구글링하니까 requestFullScreen 이라는 메소드가 있었고, 이벤트 거는건 어렵지 않았다.