iT邦幫忙

2023 iThome 鐵人賽

DAY 11
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 11

[Day11] - Custom Video Player(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

來自定義一個影片播放器吧!

今天要來自定義一個影片控制器,因為主要還是學習 javascript,樣式的部分就不多加琢磨,直接使用作者給的那些元素進行功能串接吧。

需將這些 html 元素串接到影片播放器對應的常見功能,如:切換播放暫停、控制聲音、播放速度、播放時間跳轉等,那我們就會用到各式各樣的HTMLMediaElement 媒體元素的 properties 及 method,推薦大家還是去 MDN 快速閱覽一次,或用 Javascript 取得節點用console.dir()印出來觀察看看。

實作開始

  1. 影片播放及暫停:平常在觀看 YouTube 時各位應該可以發現除了影片播放鈕,當你點擊影片視窗任何一處時,都會觸發,所以我們要取得兩這個節點,並把他們放在陣列中用forEach都加上針對點擊事件的監聽器,觸發時便會執行togglePlay()函式。
//  <video>影片容器本體,我們很多地方都需要改變它的參數
const video = document.querySelector(".player__video.viewer");
// 左下方播放鈕
const playButton = document.querySelector(".player__button.toggle");

//  放在陣列中,並依序添加點擊事件監聽器
[video, playButton].forEach((node) => {
  node.addEventListener("click", togglePlay);
});

//  函式切換播放,利用媒體元素的paused暫停與否狀態,去判斷這次點擊要播放還是暫停
//  ,並記得對按鈕圖示進行變更,這邊作者使用文字符號代替icon所以使用textContent
function togglePlay() {
  if (video.paused) {
    video.play();
    playButton.textContent = "▍▍";
  } else {
    video.pause();
    playButton.textContent = "►";
  }
}
  1. 音量及播放速率控制:因為這兩個控制閥都是使用<input type="range">name即代表媒體元素的該項特性且可供操作,所以我們先用querySelectorAll取得NodeList後將它們依序添加 input 值改變值的事件監聽器,觸發時便會執行handlePlayerSliderInput()函式。
//volume控制滑塊 & playbackRate控制滑塊
const skipButtonList = document.querySelectorAll(".player__button[data-skip]");

// 依序添加點擊事件監聽器
playerSliderList.forEach((input) => {
  input.addEventListener("input", handlePlayerSliderInput);
});
// 將video媒體元素的這個特性 變更為這個滑塊的當前值
function handlePlayerSliderInput() {
  video[this.name] = this.value;
}
  1. 播放時間跳轉(快進倒退):一樣因為兩個元素都是 button 也都是控媒體元素的currentTime已播放時間,所以我們先用querySelectorAll取得NodeList後將它們依序添加 click 的事件監聽器,handleSkipButtonClick()函式。
// 2個播放時間跳轉按鈕
const playerSliderList = document.querySelectorAll(".player__slider");
// 依序添加點擊事件監聽器
skipButtonList.forEach((button) => {
  button.addEventListener("click", handleSkipButtonClick);
});
//調整影片當前進度,因為data-skip是字串記得轉成數字
function handleSkipButtonClick() {
  video.currentTime += Number(this.dataset.skip);
}
  1. 影片進度條控制:終於進到這題的大魔王(所以擺到最後),有點多再拆細一點講解,我們一樣先取得節點
//進度條容器
const progressContainer = document.querySelector(".progress");
//裡面黃色那條實際反應播放進度的
const progressFilled = document.querySelector(".progress__filled");
  • 動態進度條:觀察作者黃色進度條可以發現是使用.progress__filledflex-basisCSS 屬性實現進度條的,所以我們可以利用變更這個屬性的百分比來反應播放時間,這邊利用到timeupdate這個事件,只要currentTime變更都會觸發,既然都有currentTime播放時間,那當然有另一個屬性代表影片的總長duration,動一下腦就可以知道如何計算現在的播放進度囉!
//  影片播放、快進倒退、還有等下寫的點擊、拖曳進度條都會觸發這個監聽器
video.addEventListener("timeupdate", updateProgress);

//  調整進度條的CSS flex-basis %數 = 算出來的播放進度
function updateProgress() {
  progressFilled.style.flexBasis = `${
    (video.currentTime / video.duration) * 100
  }%`;
}
  • 進度條調整播放時間(點擊):依據點擊事件觸發的在progressContainerX 軸位置 ÷ progressContainer的容器總寬度 × video.duration影片總長度,賦值成影片的currentTIme,然後因為又觸發了 timeupdate 事件,所以進度條的 flex-basis 會跟著變更。
progressContainer.addEventListener("click", handleProgressClick);

function handleProgressEvents(e) {
  video.currentTime =
    (e.offsetX / progressContainer.clientWidth) * video.duration;
}
  • 進度條調整播放時間(拖曳):因為是拖曳必須是在滑鼠按下狀態移動滑鼠才會觸發,所以這裡需要用到相當多的事件監聽器。
//  滑鼠按下時才新增 滑鼠移動的事件監聽器
progressContainer.addEventListener("mousedown", handleProgressMouseDown);

function handleProgressMouseDown() {
  progressContainer.addEventListener("mousemove", handleProgressEvents);
}

//  滑鼠彈起 及 滑鼠離開progressContainer容器則會將滑鼠移動的監聽器移除
progressContainer.addEventListener("mouseup", handleProgressMouseUp);
function handleProgressMouseUp() {
  progressContainer.removeEventListener("mousemove", handleProgressEvents);
}
progressContainer.addEventListener("mouseleave", handleProgressMouseLeave);

function handleProgressMouseLeave() {
  progressContainer.removeEventListener("mousemove", handleProgressEvents);
}

這邊我透過觸發mousedown時才會新增mousemove的事件監聽器,滑鼠彈起或離開容器時則會再將這個mousemove的事件監聽器移除,這樣會相對節省一些效能,而不會只要滑鼠在progressContainer容器中滑動時都會一直觸發並判斷要不要更改影片時間。

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/

上一篇
[Day10] - Hold Shift and Check Checkboxes(JS30 x 鐵人 30 x MDN)
下一篇
[Day12] - Key Sequence Detection(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言