JS30-11-Custom-Video-Player

利用Video屬性及Method來變更播放器的功能,
播放/暫停、快進/快退、音量控制、倍數控制。

目標

  • 實現播放器功能
    • 播放/暫停
    • 快進/快退
    • 音量控制
    • 倍數控制

實踐步驟

  1. 取得所有video DOM元素

  2. 撰寫Function 並 監聽DOM元素

    • 監聽 video 播放或暫停 →延伸→ 切換播放的icon
    • 進度條更新目前播放時間
    • 進度條監聽是否有切換video播放時間
    • 調整 聲音、播放倍數
    • 進、快退(+25 or -10)

成品

[DEMO] | [GitHub]


JS學習紀錄

video 播放或暫停

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function togglePlay(e){
const method = video.paused ? 'play' : 'pause';
video[method](); // 觸發影片API,當有更新時,連動會觸發本身的Event
}

// 切換icon
function updateButton() {
const icon = this.paused ? '►' : '❚ ❚';
toggleBtn.textContent = icon;
}

video.addEventListener('click',togglePlay); //點擊 影片的任何位置
toggleBtn.addEventListener('click',togglePlay); //點擊 icon

// 監聽影片本身的Event,達到切換播放的icon
video.addEventListener('play', updateButton);
video.addEventListener('pause', updateButton);

上例比較特別的地方是在 togglePlay() 裡的 video[method]() 這個寫法,

原本會寫成 video.play()video.pause() 來呼叫影片method,

所以也可使用 video[method]() 呼叫method。

進度條更新目前播放時間

因為影片會一直持續播放,所以我們也要持續更新進度條UI畫面。

JS
1
2
3
4
5
6
7
8
9
function handleProgress(e){
// video.currentTime 目前播放時間
// video.duration 屬性返回當前音頻/視頻的長度,以秒計。
const percent = (video.currentTime / video.duration) * 100;
// console.log(video.currentTime, video.duration, percent);
progressBar.style.flexBasis = `${percent}%`;
}

video.addEventListener('progress', handleProgress);

作者有提到關於 video 有兩個監聽參數 timeupdateprogress
都可以做為影片時間變動時的觸發條件,
二者差異如下:

  • progress:會在載入時,就開始觸發
  • timeupdate:會在啟動播放後,才開始觸發

所以建議使用 progress 提早觸發。

進度條監聽是否有切換video播放時間

判斷User是否有在 進度條變更video時間 的動作,
當符合操作條件(滑鼠按住不放且拖移進度條)時,就變更目前影片的播放時間。

JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const progress = document.querySelector('.progress'); // 進度條DOM元素

function scrub(e) {
// e.offsetX 取得滑鼠點擊的位置
// progress.offsetWidth 進度條的總寬度
// video.duration 屬性返回當前音頻/視頻的長度,以秒計。
// console.log(e.offsetX, progress.offsetWidth, video.duration);

// 根據 滑鼠點擊位置 位於進度條的多少比例,進而計算轉換成 影片的時間
const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
// 更改影片的播放時間
video.currentTime = scrubTime;
}

//判斷 滑鼠是否有點擊
let mousedown = false;
//監聽 進度條DOM元素
progress.addEventListener('click', scrub);
// 監聽 滑鼠滑動 時,若 滑鼠為down 的狀態時,就呼叫 scrub 的method。
progress.addEventListener('mousemove', (e) => mousedown && scrub(e));
// 監聽 滑鼠是否是 down 或 up 的狀態
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);

補充說明

上列程式碼其中一段,關於監聽 mousemove 的寫法,原始寫法如下

簡化寫法
1
2
3
4
5
6
7
8
9
10
progress.addEventListener('mousemove', (e) => {
if (mousedown) {
scrub(e);
}
});

/* 上面程式 可簡化成 下面程式碼 */

// 監聽 滑鼠滑動 時,若 滑鼠為down 的狀態時,就呼叫 scrub 的method。
progress.addEventListener('mousemove', (e) => mousedown && scrub(e));

變更 聲音、播放倍數

利用 JS 取得DOM元素的name、value的值,進而更新video的屬性。

HTML
1
2
3
4
<!-- 聲音 -->
<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">
JS
1
2
3
4
5
6
function handleRangeUpdate() {
video[this.name] = this.value;
}

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

快進、快退(+25 or -10)

利用 JS 取得DOM元素dataset的值,進而更新video的屬性。

HTML
1
2
3
4
<!-- 快退 -->
<button data-skip="-10" class="player__button">« 10s</button>
<!-- 快進 -->
<button data-skip="25" class="player__button">25s »</button>
JS
1
2
3
4
5
6
function skip() {
// dataset值是字串,利用parseFloat需轉換成數字型態
video.currentTime += parseFloat(this.dataset.skip);
}

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