[JS30筆記]-Day11 Custom Video Player

Ivy Ho
IvyCodeFive
Published in
9 min readMay 21, 2022

今天要學習如何客製化一個影片播放器 !

不小心被範例的奇怪兔子影片「Big Buck Bunny」吸引,還先看完一遍 (必需的吧),才開始做練習~

https://peach.blender.org/wp-content/uploads/bbb-splash.png

蝴蝶好可憐…

奇怪兔子會幫你報仇ㄉ (劇透?)

學習重點

<video> 屬性 :

  • paused
  • volume
  • playbackRate
  • currentTime
  • duration

<video> 事件 :

  • play
  • pause
  • timeupdateprogress

其他事件

  • change
  • mousedownmousemovemouseup

((最後完成的完整 JS 程式碼,以及檔案下載、課程連結,也會放在最下方)

功能需求

一、播放、暫停

二、skip 功能

三、音量與速度的進度條拖曳控制功能

四、影片進度條功能

先取得所有會用到的 DOM element

一、播放、暫停

  1. 對 video 及 toggle 按鈕監聽 click 事件,控制播放、停止

2. 播放、暫停時,切換按鈕 icon

監聽 <video> 的 play、pause 事件,觸發 updateButton 事件,判斷現在 video.paused 的狀態,再切換 icon。

二、skip 功能

  • 取得預先設定好的 data-skip 值 (為字串,可使用 parseFloat() 轉為以十進位表示的浮點數)
  • 設定 video.currentTime ,加上剛剛取得的 skipTime 來調整播放進度

三、音量與速度的 progress bar 拖曳控制功能

  • 對 range(也就是 .player__slider) 監聽 change 以及 mousemove 事件。(有監聽 mousemove 才能在拖曳的同時觸發改變,而不是滑鼠放開後才有變化)
  • volume 及 playbackRate 為 <video> 的屬性,我們要分別調整他們的 value。
  • 在 html 結構中,有將這兩個屬性名稱設定在 name ,因此在handleRangeUpdate() 裡,我們可以用 this.name 取得觸發當下要調整的屬性名稱,以及用 this.value 取得數值。

四、影片進度條功能

  1. 進度條中黃色進度要與影片進度一致
  • 對 video 監聽 timeupdate 事件,也可以改成 progress 事件,會有一樣的效果。
  • 利用 css flex-basis 屬性來控制進度條內黃色部分所佔的寬度比例。

2. 影片的進度條拖曳控制功能

  • 對影片進度條 progress 監聽 click 事件,可以利用 e.offsetX / progress.offsetWidth 算出目前拖曳到的位置,是占總寬度的多少比例。再乘以影片總長度 video.duration,以算出要指定影片從什麼時間開始播放。
  • 設定 video.currentTime ,指定影片從剛剛算出來的指定時間 scrubTime 開始播放。
  • 加入 flag 開關機制,新增 mousedown 變數,並對 progress 監聽 mousedown、mouseup 事件來切換開關。
  • 監聽 mousemove 事件,並善用 && 寫法。 判斷開關變數 mousedown 如果為 true,再執行 scrub(e)

到這邊,就完成了播放、暫停 、skip 、音量與速度的進度條拖曳控制,以及影片進度條功能囉~🤓

完整的 JS 程式碼

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() {
const method = video.paused ? 'play' : 'pause';
video[method]();
}
function updateButton() {
const icon = this.paused ? '►' : '❚ ❚';
console.log(icon);
toggle.textContent = icon;
}
function skip() {
video.currentTime += parseFloat(this.dataset.skip);
}
function handleRangeUpdate() {
video[this.name] = this.value;
}
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); video.addEventListener('play', updateButton); video.addEventListener('pause', updateButton); video.addEventListener('timeupdate', handleProgress);
toggle.addEventListener('click', togglePlay); skipButtons.forEach(button => button.addEventListener('click', skip)); ranges.forEach(range => range.addEventListener('change', handleRangeUpdate)); ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate));let mousedown = false;
progress.addEventListener('click', scrub); progress.addEventListener('mousemove', (e) => mousedown && scrub(e));
progress.addEventListener('mousedown', () => mousedown = true); progress.addEventListener('mouseup', () => mousedown = false);

--

--

Ivy Ho
IvyCodeFive

"You don't have to be great to start, but you have to start to be great."