iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0

11 - Custom Video Player

tags: JavaScript30

專案簡介

第十一天的目標是自己建立一個影片撥放器,需含有撥放,快轉,調整音量大小,調整撥放速度,影片撥放進度條等功能

課程影片:JS30 11
導讀影片:Alex

初始文件

Github 檔案位置:11 - Custom Video Player

網頁一開始的樣子如下,就是影片撥放器的樣子,但目前沒有任何的功能

可以先去看看 最後的成品

正式製作

流程

我們今天要做的事情就是逐步的把功能建構完成
預計會以以下的順序來做

  1. 撥放
  2. 快轉
  3. 調整聲音、撥放速度
  4. 更新影片進度條
  5. 以進度條變換撥放時間

撥放影片

為整個畫面以及撥放的按鈕加上 click 事件監聽

這裡比較特別的是用了先前所提的三元運算子,以及 call 函式時所用到的 video[method]()[method] 的意義就是 video.play() 這樣寫可以簡化程式碼

function togglePlay(){
    const method = video.paused ? 'play' : 'pause';
    video[method]();
}

video.addEventListener('click', togglePlay); 
toggle.addEventListener('click', togglePlay);

在可以撥放影片後,我們需要監聽影片的 playpause 來改變撥放鍵的樣式

function updateButton(){
    const icon = video.paused ? '>' : '|| ||';
    toggle.textContent = icon;
}

video.addEventListener('play', updateButton); // 更新撥放按鈕為撥放
video.addEventListener('pause', updateButton); // 更新撥放按鈕為暫停

快轉

在監聽 click 事件時,要記得由於 skipButton 不只一個,所以要下 forEach

由於 HTML 的 dataset 已經有寫好 +25 及 -10 了,所以這裡可以直接將影片的時間加上設定好的數值

備註:由於 dataset.skip 值的型態是 string 所以需要轉型成 int

function skip(){
    // console.log(this.dataset);
    // console.log(typeof this.dataset.skip);
    // dataset.skip 的 typeof 結果為 string
    video.currentTime += (this.dataset.skip) * 1.0; // 強制轉型為數值
}

skipButtons.forEach(skipButton => skipButton.addEventListener('click', skip)); // 有兩個 要做 forEach

調整聲音、撥放速度

一樣是監聽 play 和 pause 兩個事件,再直接採用 HTML 裡面寫好的 name 去做屬性賦值

function handleRangeUpdate(){
    // video 的 volume 可以控制音量
    // video 的 playbackRate 可以控制速度
    video[this.name] = this.value; 
}


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

更新影片進度條

在這裡就是以目前影片的時間 / 整段影片的時間來算出進度條的百分比,再賦值到進度條的 CSS 屬性上

備註:timeupdate 事件在影片撥放時會持續觸發

function handleProgress(){
    const percent = (video.currentTime / video.duration) * 100;
    console.log(progressBar);
    progressBar.style.flexBasis = `${percent}%`;
}

video.addEventListener('timeupdate', handleProgress); // 更新影片進度條

以進度條變換撥放時間

這裡一樣加上學過的事件監聽後,利用偵測鼠標點擊位置座標 / 總長度得到調整影片時間的百分比,再賦值給 video

function scrub(e){
    // console.log(e.offsetX, progress.offsetWidth);
    // e.offsetX 可以得到以進度條為範圍的 X 座標
    const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration; 
    video.currentTime = scrubTime;
}

let mousedown = false;
progress.addEventListener('click', scrub); // 點擊影片進度條
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);
progress.addEventListener('mousemove', (e) => mousedown && scrub(e)); // 滑鼠按下才反應

最後程式碼

/* Get Our Elements */
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 = video.paused ? '>' : '|| ||';
    toggle.textContent = icon;
}

function skip(){
    // console.log(this.dataset);
    // console.log(typeof this.dataset.skip);
    // dataset.skip 的 typeof 結果為 string
    video.currentTime += (this.dataset.skip) * 1.0; // 強制轉型為數值
}

function handleRangeUpdate(){
    // video 的 volume 可以控制音量
    // video 的 playbackRate 可以控制速度
    video[this.name] = this.value; 
}

function handleProgress(){
    const percent = (video.currentTime / video.duration) * 100;
    console.log(progressBar);
    progressBar.style.flexBasis = `${percent}%`;
}

function scrub(e){
    // console.log(e.offsetX, progress.offsetWidth);
    // e.offsetX 可以得到以進度條為範圍的 X 座標
    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(skipButton => skipButton.addEventListener('click', skip)); // 有兩個 要做 forEach
ranges.forEach(range => range.addEventListener('change', handleRangeUpdate)); 
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate));

let mousedown = false;
progress.addEventListener('click', scrub); // 點擊影片進度條
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);
progress.addEventListener('mousemove', (e) => mousedown && scrub(e)); // 滑鼠按下才反應

完成結果圖

最後的成品

結語

以上是第十天的製作紀錄,如有錯誤或不足的地方還請多多指教 >.<

Custom HTML5 Video Player - #JavaScript30 11/30
[ Alex 宅幹嘛 ] 深入淺出 Javascript30 快速導覽 | Day 11:Custom Video Player


上一篇
JS30 -> 10 - Hold Shift and Check Checkboxes
下一篇
JS30 -> 12 - Key Sequence Detection
系列文
剛接觸前端一個月的小白 - JavaScript30 挑戰筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言