Javascript_30_11

Derek·2020년 12월 28일
0

javascript_30

목록 보기
12/31
post-thumbnail

안녕하세요!

Derek 입니다. :)

영국 변종 코로나 바이러스가 한국에도.. 시작된것 같아요! 무서워 죽겠네요 😥
다들 집콕하셔서 안전하게 연말 보내자구요!

오늘은 Javascript 30 의 11번째 주제를 가지고 포스팅 해보려고 해요!

Day11 project는 동영상 재생 플레이어를 편집해보는 프로젝트였습니다! 나름 무거운 주제라, 양도 많지만 잘 정리해볼게요.




11. Custom Video Player

목표

재생, 일시정지 기능, 동영상이 재생됨에 따라 영상이 얼마나 재생됬는지 보여주는 바, skip 기능, 볼륨 조절 기능 및 재생속도 기능을 구현한다.

기본적인 틀은 제공받았고, 해당 기능들만 구현하는 과제였습니다.




Derek 과 wes Bos 구현코드

/* Get Our Elements */
const player = document.querySelector('.player');
const video = player.querySelector('.viewer');
const progress = player.querySelector('.progress');
const progressBar = player.querySelector('.progress__filled');
const toggle = player.querySelector('.toggle');
const skipButtons = player.querySelectorAll('[data-skip]');
const ranges = player.querySelectorAll('.player__slider');

function togglePlay(){
    video.paused ? video.play() : video.pause();
}

// The fact that this function is not included in "togglePlay()"
// is meaningful. We listened to the "play" and "pause" event.
function updateButton(){
    const icon = this.paused ? '►' : '❚ ❚';
    toggle.innerHTML = icon;
}

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

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

// this function have to be ran everytime.
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;
}

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

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

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

ranges.forEach(range => range.addEventListener("change", handleRangeUpdate));
ranges.forEach(range => range.addEventListener("mousemove", handleRangeUpdate));

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)

꽤나 길고 복잡해보입니다.

기능별로 정리해볼게요.


1. 동영상 재생 및 일시정지 기능

영상 자체를 누르거나 재생/일시정지 버튼을 누르는 기능 구현을 먼저 다뤄보겠습니다.

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

function togglePlay(){
    video.paused ? video.play() : video.pause();
}

영상 자체를 누르거나 재생버튼을 누르면 togglePlay 함수를 실행시킵니다.

삼항 연산자로 video.paused 가 참이면 video.play() 를 실행시켜 재생을 하고, 아니라면 video.pause() 를 실행시켜 멈춥니다. 그야말로 동영상 재생에 대해서toggle 을 하는 함수죠.


다음은 togglePlay() 를 수행하며 영상이 재생되거나 일시정지를 하는데, 그때마다 아이콘을 업데이트 해주는 기능입니다.

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

function updateButton(){
    const icon = this.paused ? '►' : '❚ ❚';
    toggle.innerHTML = icon;
}

video element에 대해서 event를 기다립니다. video 가 재생되거나 일시정지한다면, eventListener 가 작동하여, updateButton을 수행합니다.

this 객체는 video 로, video 가 paused 했는지 판단하여 맞다면 재생버튼을, 아니면 일시정지 버튼을 출력하게 합니다. 이를 삼항 연산자로 표현했네요.

2. skip 버튼 기능

영상 우측 하단에 10초 뒤로가거나 25초 앞으로 이동하는 기능입니다.

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

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

skip 버튼이 있는 곳에 대한 HTML 파일의 내용은,

<button data-skip="-10" class="player__button">« 10s</button>
<button data-skip="25" class="player__button">25s »</button>

즉, data 속성을 사용함을 알 수 있어요. data 속성은 1번째 게시물에 가면 확인할 수 있습니다! 사용자가 마음대로 만든 일종의 attribute 라고 생각하시면 될 것 같아요.

skipButtons

const skipButtons = player.querySelectorAll('[data-skip]');

querySelectorAll 을 사용했으며, 따라서 skipButtonsevent 등록은 forEach 구문으로 등록했습니다.

skipButtons 이 눌리면 skip() 함수를 수행하는데, 이는 this.dataset.skip 인자를 불러들여서 처리합니다!

또한 그 인자는 string 으로 읽히기 때문에 parseFloat 함수를 적용시킵니다.

CurrentTime 이라는 객체도 처음 보네요!

3. 볼륨 조절 및 재생속도 조절 기능

볼륨 조절과 재생속도는 range input 으로 조정이 됩니다.

HTML 의 구조는,

<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">

이렇게 name 속성을 가진 2개의 input 이 존재합니다. 이는 아래의 코드로 이벤트가 관리됩니다.

ranges.forEach(range => range.addEventListener("change", handleRangeUpdate));
ranges.forEach(range => range.addEventListener("mousemove", handleRangeUpdate));

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

changemousemove 두 개가 있는 이유는, 볼륨 버튼과 재생속도 버튼을 클릭하여 변동도 하지만 잡고 끌며 조절하기 때문에, 두 개의 eventListener 가 존재하게 구성했습니다.

그리고 그 이벤트가 발생하면 handleRangeUpdate 함수를 수행합니다.

this 는 이벤트를 호출한 range 이므로, this.value 를 video 속성에 집어 넣어서 구현하였습니다.

4. 현재 재생현황 표시 기능

영상이 재생됨에 따라 하단에 얼마나 재생되었는지 표시해주는 기능입니다.

video.addEventListener("timeupdate", handleProgress)

// this function have to be ran everytime.
function handleProgress(){
    const percent = (video.currentTime / video.duration) * 100;
    progressBar.style.flexBasis = `${percent}%`;
}

해당 timeUpdate 이벤트의 정의는 MDN 에 따르면 다음과 같습니다.

currentTime 속성으로 나타나는 시간이 업데이트되었을 때.

미디어에 국한된 이벤트인 것 같습니다..! 미디어의 currentTime 속성이 바뀔때마다 등록한 함수인 handleProgress 함수가 실행됩니다.

현재 재생시간을 전체 재생시간으로 나누고 비율을 계산한 다음, style 속성으로 바로 바꿔주는 모습입니다.

5. 재생 구간 변동 기능

재생 구간 bar를 클릭하여 현재 재생 위치를 바꾸거나, 클릭 후 드래그로 변동하는 기능을 구현합니다.

let mouseDown = false;

progress.addEventListener("click", scrub);
progress.addEventListener("mousemove", (e) => mouseDown && scrub(e));

progress.addEventListener("mousedown", () => mouseDown = true);
progress.addEventListener("mouseup", () => mouseDown = false);

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

먼저 scrub 함수는, 이벤트가 발생한 위치 offsetX 값을 구해서 progress bar의 가로 길이로 나눕니다. 나눈 값을 전체 재생시간을 곱해서 옮겨갈 재생구간을 찾는 함수에요.
click 이벤트가 발생하면, 단순히 scrub 함수만 실행시키면 되고, mousemove 이벤트가 발생하면 flag 하나를 참조하며 실행됩니다.

mouseDown 는 flag으로, mousedown 이나 mouseup 이벤트에 따라 각각 true, false 값을 가지게 됩니다.

progress.addEventListener("mousemove", (e) => mouseDown && scrub(e));

만약 mouseDown flag가 false 라면, 익명 함수 내에서 바로 거짓이 되어 아무런 기능을 수행하지 않고, 만약 mouseDowntrue 라면, 뒤에 scrub 함수를 수행하는, 방식입니다.

개인적으로 이 방법이 꽤나 괜찮다고 느꼈어요. if 문 없이 조건문을 완성한 것 처럼 구현되었습니다.

() => (조건 1) && (조건 2)

이런식으로 생겼다면, 조건 1 이 거짓이라면 바로 익명함수를 탈출한다! 꼭 기억했으면 좋겠어요. 🤗




이렇게 비디오 재생 툴 관련한 간단한 프로젝트가 마무리되었습니다!눈에 보이는 툴을 만드는 것이 생각보다 재밌었어요.

틀린내용이나 수정할 내용이 있다면 언제든지 피드백 부탁드립니다!
감사합니다!🤗

profile
Whereof one cannot speak, thereof one must be silent.

1개의 댓글

comment-user-thumbnail
2022년 3월 18일

도움 많이 되었습니다 감사해요!

답글 달기