분류 Nodejs

JavaScript로 사용자 정의 HTML5 비디오 플레이어를 빌드하는 방법

컨텐츠 정보

  • 조회 695 (작성일 )

본문

비디오 컨텐츠를 보고 공유하는 것은 웹의 가장 일반적인 용도 중 하나이며 비디오가 수년에 걸쳐 진화함에 따라 웹 페이지에 포함되는 방식입니다. 

요즘에는 모든 최신 브라우저에서 작동하고 다양한 비디오 형식을 지원하는 <video> 요소를 사용하는 것만큼 웹 페이지에 비디오 파일을 추가하는 것이 쉽습니다.

HTML5 video support 


주요 경고는 렌더링 되는 비디오 플레이어의 인터페이스가 브라우저마다 다르기 때문에 일관된 사용자 경험을 제공하려는 경우에는 적합하지 않다는 것입니다. 그렇기 때문에 브라우저 기본값을 사용하지 않고 사용자 정의 컨트롤을 갖춘 고유한 인터페이스를 작성하는 것이 유용합니다.


https://freshman.tech/custom-html5-video/ 


이 자습서에서는 JavaScript를 사용하여 사용자 지정 비디오 플레이어를 만드는 과정을 안내합니다. 목적은 브라우저에서 HTML5 미디어 API를 활용하여 기본값을 개선하는 비디오 경험을 구축하는 방법을 보여주는 것입니다.


이 자습서에서 제작할 플레이어는 YouTube에서 찾은 플레이어와 매우 비슷합니다. 대부분의 사람들이 이미 알고 있는 일부 기능을 복제하는 것이 좋겠다고 결정했기 때문입니다.


YouTube 플레이어에서 발견되는 모든 기능을 구현하지는 않을 것입니다. 더 길고 복잡한 자습서를 만들 수 있기 때문입니다. 그러나 이 자습서를 마치면 새로운 기능을 쉽게 연결할 수 있습니다.


라이브 데모 


우리의 맞춤형 비디오 플레이어는 블렌더 단편 영화 Big Buck Bunny의 예고편을 표시합니다.


여기에서 라이브 데모를 보거나 Github에서 소스 코드를 확인할 수 있습니다.


전제 조건 


이 학습서를 진행하려면 JavaScript 및 DOM에 대한 기본 지식이 있어야 합니다.

또한 Google에서 추가 할 일부 기능 (예 : Picture-in-Picture)은 작성 당시 Chrome에서만 작동하므로 최신 버전의 Chrome을 사용하는 것이 좋습니다.


시작 


Github에서 이 튜토리얼을 위한 스타터 파일을 준비했습니다. 이를 컴퓨터에 복제하고 텍스트 편집기에서 프로젝트 디렉토리를 열어야 합니다.

포함 된 HTML 및 CSS 파일과 플레이어를 테스트하는 비디오 파일에서 플레이어의 마크업 및 스타일을 찾을 수 있습니다.

포함 된 index.js 파일은 플레이어가 작동하는 데 필요한 모든 기능을 연결하는 곳입니다.

npm install을 실행하여 웹 서버를 시작하고 파일이 변경 될 때 브라우저를 자동으로 새로 고치기 위한 개발 종속성으로 browser-sync를 설치한 다음 터미널에서 npm start를 수행하여 브라우저에서 프로젝트를 엽니다.


지금까지 수행 한 작업 


동영상 플레이어는 현재 기본 브라우저 컨트롤을 사용하며 예상대로 작동합니다. Google 맞춤 컨트롤의 마크 업은 이미 #video-controls 요소에 정의되어 있지만 기본적으로 숨겨져 있습니다.


<div class="video-controls hidden" id="video-controls">
<!-- Custom controls are defined here -->
</div>


컨트롤에 대한 사용자 정의 인터페이스를 구현할지라도 <video> 요소에 controls 속성을 포함 시켜 어떤 이유로 JavaScript를 끈 사용자가 브라우저의 네이티브에 계속 액세스 할 수 있도록 하는 것이 좋습니다 통제 수단.

JavaScript를 사용하도록 설정한 사용자는 기본 컨트롤을 쉽게 숨기고 표시되는 대로 자체 맞춤 컨트롤을 제공할 수 있습니다.


또한 포스터 이미지가 비디오에 대해 정의되고 preload 속성이 메타 데이터로 설정되어 브라우저가 비디오 메타 데이터 (예 : 지속 시간) 만 처음으로 가져 오도록 지시합니다.

일을 단순하게 하기 위해 모든 주요 브라우저에서 지원되는 MP4 형식의 비디오용 소스 파일 하나만 포함했으며 꽤 안전한 기본값 입니다.

비디오 형식 및 브라우저 호환성에 대한 자세한 내용은 이 문서를 참조하십시오.


<video controls class="video" id="video" preload="metadata" poster="poster.jpg">
  <source src="video.mp4" type="video/mp4"></source>
</video>


기본 컨트롤을 사용자 정의 인터페이스로 교체 


가장 먼저 할 일은 브라우저가 HTML5 비디오를 지원한다고 판단되면 브라우저의 컨트롤을 숨기고 자체 인터페이스를 제공하는 것입니다. 이를 위해 index.js 파일에 아래 코드를 입력하십시오.


// Select elements here
const video = document.getElementById('video');
const videoControls = document.getElementById('video-controls');

const videoWorks = !!document.createElement('video').canPlayType;
if (videoWorks) {
  video.controls = false;
  videoControls.classList.remove('hidden');
}

canPlayType 메소드는 브라우저에서 비디오 형식에 대한 지원을 감지하는 방법입니다. 이를 사용하려면 <video> 요소 인스턴스를 만들고 canPlayType 메서드를 지원하는지 확인해야 합니다.

그렇다면 HTML5 비디오가 지원된다고 가정하는 것이 안전하므로 기본 컨트롤은 사용자 정의 컨트롤을 위해 즉시 비활성화 됩니다.


Custom controls replace default controls 


비디오 재생 또는 일시 정지 


기본부터 시작하겠습니다. 재생 버튼을 클릭하여 비디오를 재생 및 일시 정지 할 수 있어야 하며 아이콘이 비디오 상태에 맞게 변경되어야 합니다.


다음과 같이 index.js의 상단에 있는 비디오와 재생 버튼을 선택하여 시작하겠습니다.


const playButton = document.getElementById('play');


그런 다음 비디오의 재생 상태를 전환하는 기능을 만듭니다.


// Add functions here

// togglePlay toggles the playback state of the video.
// If the video playback is paused or ended, the video is played
// otherwise, the video is paused
function togglePlay() {
  if (video.paused || video.ended) {
    video.play();
  } else {
    video.pause();
  }
}


마지막으로 playButton을 클릭하면 togglePlay 함수를 실행하는 이벤트 리스너를 만듭니다.


// Add eventlisteners here
playButton.addEventListener('click', togglePlay);


충분히 쉬워요? 브라우저에서 재생 버튼을 클릭하여 테스트하십시오. 비디오를 적절히 재생하고 일시 정지해야 합니다.


https://res.cloudinary.com/freshman/video/upload/v1571831751/ezgif-4-d59b305ed6ca.webm


실제로 나머지 튜토리얼의 톤을 설정합니다. 일반적으로 비디오 컨트롤 중 하나를 선택하고 특정 기능을 구현하는 기능을 만든 다음 이벤트 리스너를 통해 컨트롤에 연결합니다.


비디오 상태에 따라 재생 아이콘을 계속 업데이트하겠습니다. 이것은 playButton의 HTML입니다.


<button data-title="Play (k)" id="play">
  <svg class="playback-icons">
    <use href="#play-icon"></use>
    <use class="hidden" href="#pause"></use>
  </svg>
</button>

우리는 이미 SVG에 재생 및 일시 중지 아이콘을 모두 가지고 있지만 다른 아이콘을 숨겨서 한 번에 하나씩 만 표시합니다 (숨겨진 클래스에 주목). 이제 해야 할 일은 비디오 상태에 따라 적절한 아이콘이 표시되도록 각 아이콘에서 숨겨진 클래스를 켜거나 끄는 것입니다.


먼저 상단의 아이콘을 선택하십시오.


const playbackIcons = document.querySelectorAll('.playback-icons use');


그런 다음 togglePlay에서 재생 버튼을 업데이트하는 기능을 만듭니다.


// updatePlayButton updates the playback icon and tooltip
// depending on the playback state
function updatePlayButton() {
  playbackIcons.forEach(icon => icon.classList.toggle('hidden'));
}


마지막으로 하단에 이벤트 리스너를 추가하십시오.


video.addEventListener('play', updatePlayButton);
video.addEventListener('pause', updatePlayButton);

비디오가 재생되거나 일시 정지되면 updatePlayButton 함수가 실행되어 각 버튼에서 숨겨진 클래스를 토글합니다. 기본적으로 일시 중지 아이콘에 숨겨진 클래스가 있으므로 비디오가 재생되면 이 아이콘이 표시되고 재생 아이콘이 숨겨집니다. 비디오가 다시 일시 정지되면 반대의 현상이 발생합니다. 브라우저에서 이를 테스트 할 수 있습니다.


재생 버튼 위로 마우스를 가져 가면 표시되는 툴팁의 텍스트를 업데이트 해야 합니다. 기본적으로 재생 (k)을 읽지 만 비디오가 재생 중일 때 일시 중지 (k)를 읽도록 업데이트 해야 합니다. k는 나중에 자습서에서 동영상을 재생하거나 일시 중지하기 위해 추가 할 키보드 단축키입니다.


아래와 같이 updatePlayButton을 업데이트하십시오.

function updatePlayButton() {
  playbackIcons.forEach(icon => icon.classList.toggle('hidden'));

  if (video.paused) {
    playButton.setAttribute('data-title', 'Play (k)')
  } else {
    playButton.setAttribute('data-title', 'Pause (k)')
  }
}


비디오가 재생 중이거나 일시 정지되었을 때 버튼 위에 마우스를 올리면 툴팁에 적절한 텍스트가 설정됩니다.


툴팁이 어떻게 표시되는지 궁금하다면 버튼에서 :: before psuedo-element를 사용하고 그 내용을 data-title 속성의 값으로 설정합니다. 이것은 관련 CSS입니다.


button::before {
  content: attr(data-title);
  position: absolute;
  display: none;
  right: 0;
  top: -50px;
  background-color: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-weight: bold;
  padding: 4px 6px;
  word-break: keep-all;
  white-space: pre;
}

button:hover::before {
  display: inline-block;
}


https://res.cloudinary.com/freshman/video/upload/v1571832292/ezgif-4-fa36e82a34b9.webm


비디오 지속 시간 및 경과 시간 표시 


사용자가 가장 먼저 보고 싶은 것 중 하나인 동영상의 길이를 표시해야 하므로 다음에 할 것입니다.


경과 시간 및 시간에 대한 마크업입니다.


<div class="time">
  <time id="time-elapsed">00:00</time>
  <span> / </span>
  <time id="duration">00:00</time>
</div>


다음과 같이 index.js 파일에서 두 컨트롤을 모두 선택하십시오.


const timeElapsed = document.getElementById('time-elapsed');
const duration = document.getElementById('duration');

동영상의 기간 속성을 사용하여 페이지가 로드 되면 동영상의 총 기간이 표시됩니다. 이 속성은 동영상의 초 수를 나타내므로 표시하려면 먼저 이 숫자를 분과 초로 변환해야 합니다. 이를 위해 시간을 초 단위로 취하여 분과 초로 변환하는 formatTime 함수를 작성하십시오.


// formatTime takes a time length in seconds and returns the time in
// minutes and seconds
function formatTime(timeInSeconds) {
  const result = new Date(timeInSeconds * 1000).toISOString().substr(11, 8);

  return {
    minutes: result.substr(3, 2),
    seconds: result.substr(6, 2),
  };
};

다음으로 formatTime 아래에 initializeVideo 함수를 작성하십시오.


// initializeVideo sets the video duration, and maximum value of the
// progressBar

function initializeVideo() {
  const videoDuration = Math.round(video.duration);
  const time = formatTime(videoDuration);
  duration.innerText = `${time.minutes}:${time.seconds}`;
  duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`)
}


위에 표시된 것처럼 동영상의 지속 시간 (초)은 가장 가까운 정수로 반올림 되고 분과 초 형식으로 화면에 업데이트 됩니다. 또한 비디오 시간을 나타내는 시간 문자열로 datetime 속성을 업데이트했습니다.


다음과 같이 initializeVideo 함수를 비디오의 loadedmetadata 이벤트에 연결하십시오. 동영상의 메타 데이터가 로드 되면 동영상 재생 시간이 업데이트 됩니다.


video.addEventListener('loadedmetadata', initializeVideo);


Video duration 


다음으로 비디오를 재생할 때 경과 한 시간을 업데이트 해야 합니다. 원하는 것을 달성하는 데 도움이 되는 기능은 다음과 같습니다.


// updateTimeElapsed indicates how far through the video
// the current playback is
function updateTimeElapsed() {
  const time = formatTime(Math.round(video.currentTime));
  timeElapsed.innerText = `${time.minutes}:${time.seconds}`;
  timeElapsed.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`)
}


비디오에서 청취해야 하는 이벤트는 timeupdate 이벤트입니다. 이 이벤트는 동영상의 currentTime 속성으로 표시된 시간이 업데이트 될 때마다 시작됩니다.


video.addEventListener('timeupdate', updateTimeElapsed);


위 코드는 비디오 재생으로 비디오의 currentTime이 업데이트 되면 경과 시간도 적절하게 업데이트되도록합니다.


https://res.cloudinary.com/freshman/video/upload/v1571834573/ezgif-4-d5a3863ebc5e.webm


진행률 표시 줄 업데이트 


다음으로 동영상을 재생하면서 진행률 표시 줄을 업데이트합니다. 진행률 표시 줄에 대한 마크 업은 다음과 같습니다.


<div class="video-progress">
  <progress id="progress-bar" value="0" min="0"></progress>
  <input class="seek" id="seek" value="0" min="0" type="range" step="1">
  <div class="seek-tooltip" id="seek-tooltip">00:00</div>
</div>

여기에는 모든 작업의 ​​진행률을 표시하는 데 적합한 progress 요소가 있으며 범위 입력을 통해 비디오를 빠르고 완벽하게 스크러빙할 수 있습니다. 두 요소의 너비와 높이가 동일하도록 스타일을 지정했으며 범위 입력이 투명하게 표시됩니다 (진행률 표시 줄의 값과 동일한 색상의 엄지 손가락 제외).


궁금한 점이 있으면 CSS를 조사하여 내가 어떻게 했는지 알아볼 수 있습니다. 이것은 진행률 표시 줄을 단일 요소처럼 보이게 만드는 일종의 해킹이지만 사용 사례에 적합하다고 생각합니다.


둘 다의 min 속성은 0으로 설정되며 value 속성은 두 요소의 현재 값을 나타냅니다. 또한 위에서 설명한 대로 video.duration에서 가져온 비디오 지속 시간 (초)으로 설정 될 max 속성이 필요합니다. initializeVideo 함수에서 이 작업을 수행 할 수 있지만 먼저 요소를 선택해야 합니다.


const progressBar = document.getElementById('progress-bar');
const seek = document.getElementById('seek');


그런 다음 아래와 같이 initalizeVideo를 업데이트하십시오.


function initializeVideo() {
  const videoDuration = Math.round(video.duration);
  seek.setAttribute('max', videoDuration);
  progressBar.setAttribute('max', videoDuration);
  const time = formatTime(videoDuration);
  duration.innerText = `${time.minutes}:${time.seconds}`;
  duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`)
}


이제 progress 요소와 range 입력의 범위는 두 요소의 min 및 max 속성으로 표시되는 0과 비디오 지속 시간 (초)입니다. 이렇게 하면 언제든지 볼 수 있는 바와 같이 진행률 표시 줄을 범위 입력과 쉽게 동기화 할 수 있습니다.


진행률 표시 줄이 작동하도록 비디오가 재생 될 때 위에서 언급 한 요소의 값을 업데이트하겠습니다. 아래에 새 updateProgress 함수를 작성하십시오.


// updateProgress indicates how far through the video
// the current playback is by updating the progress bar
function updateProgress() {
  seek.value = Math.floor(video.currentTime);
  progressBar.value = Math.floor(video.currentTime);
}

그런 다음 첫 번째 비디오 요소 아래의 비디오 요소에 새로운 timeupdate 이벤트 리스너를 추가하십시오.


video.addEventListener('timeupdate', updateProgress);


브라우저를 새로 고침하고 사용해보십시오. 비디오가 재생되는 동안 진행률 표시 줄이 업데이트 되는 것을 볼 수 있습니다.


https://res.cloudinary.com/freshman/video/upload/v1571834931/ezgif-4-48f0ac94e3d4.webm


건너 뛰기 


대부분의 비디오 플레이어에서는 진행률 표시 줄을 클릭하여 비디오의 특정 지점으로 이동할 수 있으며, 다른 점은 없습니다. 먼저 툴팁 요소를 선택해야 합니다.


const seekTooltip = document.getElementById('seek-tooltip')

그런 다음 커서가 진행률 막대 위에 있을 때 seekTooltip에 타임 스탬프를 표시하는 함수를 추가하십시오.


// updateSeekTooltip uses the position of the mouse on the progress bar to
// roughly work out what point in the video the user will skip to if
// the progress bar is clicked at that point
function updateSeekTooltip(event) {
  const skipTo = Math.round((event.offsetX / event.target.clientWidth) * parseInt(event.target.getAttribute('max'), 10));
  seek.setAttribute('data-seek', skipTo)
  const t = formatTime(skipTo);
  seekTooltip.textContent = `${t.minutes}:${t.seconds}`;
  const rect = video.getBoundingClientRect();
  seekTooltip.style.left = `${event.pageX - rect.left}px`;
}


이 함수는 탐색 요소에서 커서의 위치를 ​​사용하여 사용자가 마우스 커서를 올리는 범위 입력의 위치를 ​​대략적으로 파악하고 해당 위치의 타임 스탬프를 반영하도록 툴팁을 업데이트하는 동안 데이터 검색 속성에 위치를 저장합니다.


탐색 제어에서 updateSeekTooltip 함수를 mousemove 이벤트에 연결하여 작동 효과를 확인하십시오.


seek.addEventListener('mousemove', updateSeekTooltip);


https://res.cloudinary.com/freshman/video/upload/v1571833292/ezgif-4-bba8f666a098.webm


이제 엄지 손가락을 클릭하거나 드래그 하여 탐색 요소의 값을 변경하면 비디오가 데이터 탐색 속성에 설정된 시간으로 이동하기를 원합니다. updateSeekTooltip 아래에 새로운 skipAhead 함수를 작성하십시오.


// skipAhead jumps to a different point in the video when
// the progress bar is clicked
function skipAhead(event) {
  const skipTo = event.target.dataset.seek;
  video.currentTime = skipTo;
  progressBar.value = skipTo;
  seek.value = skipTo;
}

이 함수는 seek 이벤트의 값이 입력 이벤트를 사용하여 모니터 할 수 있을 때 실행될 것입니다. 그런 다음 data-seek 속성 값을 가져 와서 비디오 경과 시간과 진행률 표시 줄을 해당 값으로 업데이트합니다. 비디오에서 다른 위치로 건너 뛰는 효과를 만듭니다.


seek.addEventListener('input', skipAhead);


https://res.cloudinary.com/freshman/video/upload/v1571835038/ezgif-4-bbf3741e4bb9.webm


음량 조절 


<div class="volume-controls">
  <button data-title="Mute (m)" class="volume-button" id="volume-button">
    <svg>
      <use class="hidden" href="#volume-mute"></use>
      <use class="hidden" href="#volume-low"></use>
      <use href="#volume-high"></use>
    </svg>
  </button>

  <input class="volume" id="volume" value="1" type="range" max="1" min="0" step="0.01">
</div>

위의 스니펫에서 모든 볼륨 관련 컨트롤에 대한 마크 업을 찾을 수 있습니다. 비디오 볼륨 상태에 따라 볼륨 아이콘을 나타내는 버튼과 비디오 볼륨을 제어하는 ​​범위 입력이 있습니다.


가장 먼저 해야 할 일은 #volume 입력 값이 변경 될 때 비디오의 볼륨을 업데이트하는 것입니다. 또한 비디오의 현재 볼륨을 반영하도록 아이콘을 업데이트해야 합니다.


음량의 범위는 0에서 1까지이며 입력의 각 단계에서 음량이 0.1 씩 증가합니다. 이 방법은 0에서 1 사이의 0에서 1까지의 범위 인 비디오의 볼륨 속성과 일치하도록 설정되었습니다. 1은 가장 높은 볼륨입니다.


index.js 파일에서 버튼, 아이콘 및 입력을 선택하십시오.


const volumeButton = document.getElementById('volume-button');
const volumeIcons = document.querySelectorAll('.volume-button use');
const volumeMute = document.querySelector('use[href="#volume-mute"]');
const volumeLow = document.querySelector('use[href="#volume-low"]');
const volumeHigh = document.querySelector('use[href="#volume-high"]');
const volume = document.getElementById('volume');


다음으로, 볼륨 입력이 변경되는 즉시 볼륨을 업데이트하는 새로운 updateVolume 함수를 작성하십시오.


// updateVolume updates the video's volume
// and disables the muted state if active
function updateVolume() {
  if (video.muted) {
    video.muted = false;
  }

  video.volume = volume.value;
}


다음과 같이 볼륨 요소에 연결하십시오.


volume.addEventListener('input', updateVolume);


이 시점에서 범위를 왼쪽으로 밀면 볼륨이 감소하고 그 반대도 마찬가지입니다. 볼륨이 변경 될 때마다 아이콘을 업데이트하려면 다른 기능을 추가해야 합니다.


// updateVolumeIcon updates the volume icon so that it correctly reflects
// the volume of the video
function updateVolumeIcon() {
  volumeIcons.forEach(icon => {
    icon.classList.add('hidden');
  });

  volumeButton.setAttribute('data-title', 'Mute (m)')

  if (video.muted || video.volume === 0) {
    volumeMute.classList.remove('hidden');
    volumeButton.setAttribute('data-title', 'Unmute (m)')
  } else if (video.volume > 0 && video.volume <= 0.5) {
    volumeLow.classList.remove('hidden');
  } else {
    volumeHigh.classList.remove('hidden');
  }
}

이 기능이 실행되면 모든 아이콘이 숨겨지고 어떤 조건이 true로 평가 되는지에 따라 그 중 하나가 표시됩니다.


다음과 같이 volumechange 이벤트를 수신하여 볼륨이 변경 될 때마다 updateVolumeIcon을 실행할 수 있습니다.


video.addEventListener('volumechange', updateVolumeIcon);


다음은 이 변경을 수행 한 후 브라우저에서 가져 오는 것입니다.


https://res.cloudinary.com/freshman/video/upload/v1571833058/ezgif-4-18943112516f.webm


추가해야 할 한 가지 사항은 볼륨 아이콘을 클릭하여 비디오를 음소거 및 음소거 해제하는 기능입니다. 이를 위해 새로운 toggleMute 함수를 만들겠습니다.


// toggleMute mutes or unmutes the video when executed
// When the video is unmuted, the volume is returned to the value
// it was set to before the video was muted
function toggleMute() {
  video.muted = !video.muted;

  if (video.muted) {
    volume.setAttribute('data-volume', volume.value);
    volume.value = 0;
  } else {
    volume.value = volume.dataset.volume;
  }
}

그런 다음 volumentButton을 클릭하면 함수를 실행하십시오.


volumeButton.addEventListener('click', toggleMute);


이 기능은 비디오의 음소거 속성 상태를 true 또는 false로 전환합니다. 비디오가 음소거 되면 볼륨이 볼륨 요소의 데이터 볼륨 속성에 저장되므로 비디오가 음소거 해제 된 경우 볼륨 상태를 이전 값으로 복원 할 수 있습니다.


실제 모습은 다음과 같습니다.


https://res.cloudinary.com/freshman/video/upload/v1571835561/ezgif-4-f0037efb597b.webm


동영상을 클릭하여 재생 또는 일시 중지 


많은 비디오 플레이어 응용 프로그램에서 비디오 자체를 클릭하면 비디오를 더 빠르게 재생하거나 일시 중지 할 수 있습니다. 우리의 응용 프로그램에서 가능하게 합시다.


비디오에서 click 이벤트를 수신하고 이벤트가 발생할 때 togglePlay 기능을 실행하기 만 하면 됩니다.


video.addEventListener('click', togglePlay);


이 방법이 작동하는 동안 YouTube 나 Netflix에서와 마찬가지로 비디오를 재생하거나 일시 정지 할 때 약간의 피드백을 추가하여 더 재미있게 만들어 봅시다.


애니메이션 용 HTML은 다음과 같습니다.


<div class="playback-animation" id="playback-animation">
  <svg class="playback-icons">
    <use class="hidden" href="#play-icon"></use>
    <use href="#pause"></use>
  </svg>
</div>


관련 CSS는 다음과 같습니다.


.playback-animation {
  pointer-events: none;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-left: -40px;
  margin-top: -40px;
  width: 80px;
  height: 80px;
  border-radius: 80px;
  background-color: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
  opacity: 0;
}


기본적으로 .playback-animation 요소는 opacity 속성을 사용하여 완전히 투명하게 만들어집니다. YouTube에서 애니메이션을 복제하기 위해 Web Animation API를 사용하여 이 요소의 불투명도와 스케일에 애니메이션을 적용합니다.


index.js 맨 위에서 먼저 선택하십시오.


const playbackAnimation = document.getElementById('playback-animation');


그런 다음 파일의 다른 함수 아래에 다음 함수를 작성하십시오.


// animatePlayback displays an animation when
// the video is played or paused
function animatePlayback() {
  playbackAnimation.animate([
    {
      opacity: 1,
      transform: "scale(1)",
    },
    {
      opacity: 0,
      transform: "scale(1.3)",
    }], {
    duration: 500,
  });
}

animate 메서드는 키 프레임 객체와 옵션 객체를 가져 와서 애니메이션 지속 시간을 제어 할 수 있습니다.


이제 비디오에 클릭 이벤트 리스너를 추가하십시오.


video.addEventListener('click', animatePlayback);


결과적으로 비디오를 클릭하여 재생하거나 일시 정지 할 때 짧은 애니메이션이 나타납니다.


https://res.cloudinary.com/freshman/video/upload/v1571835780/ezgif-4-4d8ff968c5f3.webm


전체 화면 비디오 


다음으로 전체 화면 버튼이 작동하도록하겠습니다. 비디오를 전체 화면 (컨트롤 포함)으로 만들려면 .video-container 요소를 선택하고 브라우저에 비디오 및 그 하위 항목을 전체 화면에 배치하도록 요청해야 합니다.


index.js 파일에서 버튼과 비디오 컨테이너를 모두 선택하십시오.


const fullscreenButton = document.getElementById('fullscreen-button');
const videoContainer = document.getElementById('video-container');


그런 다음 새로운 toggleFullScreen 함수를 작성하십시오.


// toggleFullScreen toggles the full screen state of the video
// If the browser is currently in fullscreen mode,
// then it must be exited and vice versa.
function toggleFullScreen() {
  if (document.fullscreenElement) {
    document.exitFullscreen();
  } else {
    videoContainer.requestFullscreen();
  }
}


그리고 아래와 같이 fullScreenButton에 클릭 이벤트 리스너를 추가하십시오.


fullscreenButton.onclick = toggleFullScreen;


toggleFullScreen 기능은 문서가 전체 화면 모드인지 먼저 확인한 후 다시 창 모드로 돌아갑니다. 그렇지 않으면 videoContainer를 전체 화면에 배치합니다.


이 섹션에서 수행해야 할 또 하나의 작업은 전체 화면 아이콘과 버튼 위에 마우스를 놓을 때 나타나는 툴팁의 텍스트를 업데이트하는 것입니다. 먼저 아이콘을 선택하십시오.


const fullscreenIcons = fullscreenButton.querySelectorAll('use');

그런 다음 videoContainer가 전체 화면 모드로 들어가거나 나올 때 버튼을 업데이트하는 기능을 만듭니다.


// updateFullscreenButton changes the icon of the full screen button
// and tooltip to reflect the current full screen state of the video
function updateFullscreenButton() {
  fullscreenIcons.forEach(icon => icon.classList.toggle('hidden'));

  if (document.fullscreenElement) {
    fullscreenButton.setAttribute('data-title', 'Exit full screen (f)')
  } else {
    fullscreenButton.setAttribute('data-title', 'Full screen (f)')
  }
}

마지막으로 videoContainer 요소의 onfullscreenchange 이벤트 핸들러에 updateFullscreenButton을 할당합니다.


videoContainer.addEventListener('fullscreenchange', updateFullscreenButton);


그리고 예상대로 작동합니다! 브라우저에서 테스트하거나 아래 GIF를 참조하십시오.


https://res.cloudinary.com/freshman/video/upload/v1571835872/Screen_record_from_2019-10-23_11.08.18_2x.webm


Picture-In-Picture 지원 추가 


PiP (Picture-in-Picture) API를 사용하면 사용자는 항상 다른 창 위에 있는 부동 창에서 비디오를 볼 수 있으므로 다른 사이트나 응용 프로그램과 상호 작용하면서 보고있는 내용을 계속 확인할 수 있습니다.


현재 이 API는 소수의 브라우저에서만 지원되므로 기능 감지를 사용하여 PiP 버튼을 지원하지 않는 브라우저에서 PiP 버튼을 숨기면 사용자가 사용할 수 없는 버튼을 볼 수 없습니다.

Browser support for Picture-in-Picture 


이를 달성하는 데 도움이 되는 코드는 다음과 같습니다. 다른 이벤트 리스너 아래에 추가하십시오.


document.addEventListener('DOMContentLoaded', () => {
  if (!('pictureInPictureEnabled' in document)) {
    pipButton.classList.add('hidden');
  }
});


이 자습서 전체에서 수행 한 것처럼 먼저 관련 컨트롤을 선택해야 합니다.


const pipButton = document.getElementById('pip-button')

그런 다음 Picture-in-Picture 모드를 전환하는 기능을 만듭니다.


// togglePip toggles Picture-in-Picture mode on the video
async function togglePip() {
  try {
    if (video !== document.pictureInPictureElement) {
      pipButton.disabled = true;
      await video.requestPictureInPicture();
    } else {
      await document.exitPictureInPicture();
    }
  } catch (error) {
    console.error(error)
  } finally {
    pipButton.disabled = false;
  }
}


여러 가지 이유로 발생할 수 있는 requestPictureInPicture() 메소드가 거부되는 경우 오류를 포착 할 수 있도록 togglePip 함수를 비동기식으로 만들었습니다. 실제 단어 앱에서는 오류 메시지를 콘솔에 기록하는 대신 사용자에게 표시하려고 합니다.


다음으로 pipButton에서 click 이벤트를 수신하고 togglePip 함수를 이벤트 처리기로 추가하십시오.


pipButton.addEventListener('click', togglePip);


이제 pipButton을 클릭하면 Picture-in-Picture 모드가 시작되거나 종료됩니다. 오른쪽 상단의 닫기 버튼을 클릭하여 PiP 창을 닫을 수도 있습니다.


https://res.cloudinary.com/freshman/video/upload/v1571836108/Screen_record_from_2019-10-23_11.23.57_2x.webm


적절하게 컨트롤 표시 및 숨기기 


동영상 하단의 컨트롤은 약간의 공간을 차지하고 일부 콘텐츠에 대한 사용자의 시야를 차단합니다. 사용하지 않을 때는 숨기고 비디오 위에 마우스를 올려 놓으면 다시 표시하는 것이 좋습니다.


이를 위해 아래 두 가지 기능을 작성하십시오.


// hideControls hides the video controls when not in use
// if the video is paused, the controls must remain visible
function hideControls() {
  if (video.paused) {
    return;
  }

  videoControls.classList.add('hide');
}

// showControls displays the video controls
function showControls() {
  videoControls.classList.remove('hide');
}


여기서 하고 싶은 것은 커서가 비디오 인터페이스를 떠날 때 컨트롤을 숨기는 것입니다. 그러나 비디오가 일시 중지 될 때 컨트롤이 항상 표시 되도록하려면 hideControls()의 조건부입니다.


이를 달성하기 위해 아래에 표시된 대로 비디오 요소와 videoControls 모두에서 onmouseenter 및 onmouseleave 이벤트 핸들러를 사용합니다.


video.addEventListener('mouseenter', showControls);
video.addEventListener('mouseleave', hideControls);
videoControls.addEventListener('mouseenter', showControls);
videoControls.addEventListener('mouseleave', hideControls);


https://res.cloudinary.com/freshman/video/upload/v1571836204/Screen_record_from_2019-10-23_11.29.02_2x.webm


키보드 단축키 지원 추가 


이 자습서에서 추가 할 마지막 기능은 키보드를 사용하여 비디오 재생을 제어하는 ​​기능입니다. 실제로 특정 키를 누를 때 이미 작성한 기능을 실행하는 것입니다. 여기서 구현할 바로 가기는 다음과 같습니다.

  • k: 비디오 재생 또는 일시 정지
  • m: 비디오 음소거 또는 음소거 해제
  • f: 전체 화면 전환
  • p: PIP 모드 전환

여기서 수행 할 작업은 문서에서 키업 이벤트를 수신하고 누른 키를 감지 한 후 키 관련 기능을 실행하는 것입니다.


// keyboardShortcuts executes the relevant functions for
// each supported shortcut key
function keyboardShortcuts(event) {
  const { key } = event;
  switch(key) {
    case 'k':
      togglePlay();
      animatePlayback();
      if (video.paused) {
        showControls();
      } else {
        setTimeout(() => {
          hideControls();
        }, 2000);
      }
      break;
    case 'm':
      toggleMute();
      break;
    case 'f':
      toggleFullScreen();
      break;
    case 'p':
      togglePip();
      break;
  }
}


위의 switch 문을 사용하여 어떤 키를 눌렀는지 감지 한 다음 관련 코드를 실행합니다. hideControls가 2 초 후에 호출되는 이유는 바로 가기 키를 사용하여 비디오를 재생할 때 비디오 재생이 시작되면 컨트롤이 즉시 숨겨지지 않지만 짧은 지연 후에 컨트롤이 숨겨지는 YouTube에서의 동작을 모방하기 위한 것입니다.


document.addEventListener('keyup', keyboardShortcuts);


마무리 


비디오 플레이어를 개선하는 데는 여러 가지 방법이 있지만 튜토리얼이 너무 길어 여기서 중단해야 했습니다. 플레이어의 기능을 확장하려는 경우 다음과 같은 아이디어가 있습니다.

  • 자막 및 자막 지원 추가
  • 속도 지원 추가
  • 비디오를 빨리 감거나 되 감는 기능 추가
  • 비디오 해상도 선택 기능 추가 (720p, 480p, 360p, 240p)

이 튜토리얼이 도움이 되었기를 바랍니다. 궁금한 점이 있으면 아래에 의견을 남겨 주시면 다시 연락 드리겠습니다. Github에서 전체 소스 코드를 확인하는 것을 잊지 마십시오.