[JS30筆記]-Day11 Custom Video Player
Published in
9 min readMay 21, 2022
今天要學習如何客製化一個影片播放器 !
不小心被範例的奇怪兔子影片「Big Buck Bunny」吸引,還先看完一遍 (必需的吧),才開始做練習~
蝴蝶好可憐…
奇怪兔子會幫你報仇ㄉ (劇透?)
學習重點
<video> 屬性 :
paused
volume
playbackRate
currentTime
duration
<video> 事件 :
play
pause
timeupdate
、progress
其他事件
change
mousedown
、mousemove
、mouseup
((最後完成的完整 JS 程式碼,以及檔案下載、課程連結,也會放在最下方)
功能需求
一、播放、暫停
二、skip 功能
三、音量與速度的進度條拖曳控制功能
四、影片進度條功能
先取得所有會用到的 DOM element
一、播放、暫停
- 對 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
取得數值。
四、影片進度條功能
- 進度條中黃色進度要與影片進度一致
- 對 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);