iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 11
0

成品連結:Custom Video PlayerHTML 程式碼CSS 程式碼JS 程式碼

今天要做的是播放器!其實瀏覽器本身就有內建瀏覽器,但有個缺點是每個瀏覽器的預設樣式都不一樣;所以如果有樣式統一的需求,就需要自己刻一個了(像 YouTube 就是一致的樣式)

事前規劃

今天的規劃可以分成三個部分,分別是:

  • 設定 DOM 元素變數
  • 對應的 function
  • 設定監聽事件

那就開始寫程式碼吧~

設定 DOM 元素變數

最簡單的部分就是這裡啦!分別將 HTML 元素存入變數供 JS 操作

// 播放器本身
const video = document.querySelector('video');
// 播放 / 暫停
const playToggle = document.querySelector('button.toggle');
// 時間增加 / 減少
const playSkip = document.querySelectorAll('.player__button[data-skip]');
// 調節音量 / 調節速度
const slider = document.querySelectorAll('.player__slider');
// 時間進度條底色
const progress = document.querySelector('.progress');
// 時間進度條(已過時間),與上面的時間進度條顏色不一樣
const progressFilled = document.querySelector('.progress__filled');
// 全螢幕
const fullScreen = document.querySelector('.full__screen');

接下來要寫對應的 function 以及設定監聽事件,但我們依照功能分別設定吧!

播放 & 暫停

設定監聽事件

一般來說,按下播放 / 暫停按鍵或是播放器本身都應該能夠達到此功能,因此先設定監聽事件

playToggle.addEventListener('click', playPause);
video.addEventListener('click', playPause);
// playPause 函式用來控制播放及暫停

設定 function

接著完善 function 內容,也就是改變狀態及更改播放/ 暫停圖示

function playPause() {
    // 當狀態為未播放時 -> 播放 & 更改圖示
    if (video.paused) {
        video.play();
        playToggle.textContent = '❚ ❚';
    } else { // 當狀態為播放時 -> 暫停 & 更改圖示
        video.pause();
        playToggle.textContent = '►';
    }
}

時間後退 / 前進(-10/+25)

設定監聽事件

同樣先設定監聽 click 事件

playSkip.forEach(cur => cur.addEventListener('click', skip));

由於我將兩個按鈕綁在同一個變數 playSkip,所以要透過 forEach 分別設定監聽事件

設定 function

在 HTML tag 中可以看到有寫入 dataset 來存放後退或前進的值,我們需要利用這點來更改播放時間

function skip() {
    video.currentTime += parseFloat(this.dataset.skip);
}

沒有錯,就只需要一行程式碼就好!先取得影片目前時間並加上存在 dataset 中的數字,但注意 dataset 中的數字是 string,所以要先轉成數字(使用 Number(..) 也可以)

調整音量 & 播放速度

設定監聽事件

slider.forEach(cur => cur.addEventListener('input', changeSlider));

如同上面的 playSkip,由於 slider 內有兩個元素 input[type="range"],所以要用 forEach 分別設定監聽事件,使用 input 事件可以監測隨時變動的值,或是也可以用兩個監聽事件 change 以及 mousemove 來處理

設定 function

從 HTML 中可以看到 tag 中有 name 這個屬性,我們要透過這項屬性以及 value 來更改值

如有興趣知道如何設定 input[type="range"] 內容,請參考 MDN 介紹

function changeSlider() {
    if (this.name === 'volume') {
        video.volume = this.value;  // 設定音量
    } else {
        video.playbackRate = this.value;  // 設定播放速度
    }
}

這裡的 this 指的就是被選取到的 HTML tag,而我們可以透過 video.volume 以及 video.playbackRate 來存取影片音量及播放速度

但上面的寫法可以寫得更簡潔:

function changeSlider() {
    video[this.name] = this.value;
}

第一時間可能會看不懂,但其實就是與 video.name 不同寫法而已。例如 video.name 又可以寫成 video["name"]

顯示播放進度

現在影片已經可以正常播放了,但要如何顯示出進度呢?

設定監聽事件

其實一開始我想到的方法是用 setInterval(),每 100 毫秒更新畫面,但後來發現這樣可能會影響到效能;即便影片未在播放狀態,網頁仍舊會不停更新

接著發現其實有事件可以監聽這種播放或暫停的事件,也就是 timeupdate

video.addEventListener('timeupdate', autoChangeStatus);

設定 function

先說明一下顯示已播放的方法:這裡是透過調整 HTML tag div.progress__filledflex-basis 屬性來看出播放進度,flex-basis 的值從 0% ~ 100%

也就是說我們只需要取得播放的進度百分比即可,也就是(目前時間 / 影片長度)* 100,並透過設定 CSS 來更改 flex-basis

function autoChangeStatus() {
    progressFilled.style.flexBasis = `${(video.currentTime / video.duration) * 100}%`;
}

接著當影片結束時,要把 playToggle 的圖示從 ❚ ❚ 變成 ►

function autoChangeStatus() {
    progressFilled.style.flexBasis = `${(video.currentTime / video.duration) * 100}%`;
    
    // 改變 playToggle 的圖標
    if (this.ended) playToggle.textContent = '►';
}

點選 & 拖曳進度條以更改影片時間

這個作品最困難的地方就是這裡了... 當初可是想了很久才想出來啊!

設定監聽事件

我們需要點擊、拖曳時都能更改影片進度

progress.addEventListener('click', changeProgress);
progress.addEventListener('mousemove', changeProgress);    // 要是 clicked 的狀態才會執行 changeProgress

設定 function

首先我們要先得出點選或拖曳進度條的位置,透過 console.dir(progress) 可以看到 offsetWidth 這項屬性就是進度條的寬度;接著透過監聽事件 click 中寫入 console.dir(progress) ,我們可以看到 offsetX 這個屬性的值會隨著點擊或拖曳變動值。
所以我們能夠透過上面兩個值找出點擊 / 拖曳後在進度條 progress 的位置,並更改影片進度

function changeProgress(e) {
    progressFilled.style.flexBasis = `${(e.offsetX / progress.offsetWidth) * 100}%`;
    video.currentTime = video.duration * (e.offsetX / progress.offsetWidth);
}

雖然這樣設定可以運作了,但是拖曳時(滑鼠左鍵未按下的狀態下)進度條也會變動

為了要修改這點,我們需要偵測當點擊 + 拖曳時才會執行 changeProgress;因此需要設定一個 flag 來標示是否有按下滑鼠左鍵

設定新的 keyup & keydown 監聽事件
let clicked = false; // 預設為 false,按下左鍵時才會變成 true

progress.addEventListener('mousedown', () => clicked = true);
progress.addEventListener('mouseup', () => clicked = false);

並且要更改一下 mousemove 的監聽事件

// 原寫法
progress.addEventListener('mousemove', function(e) {
    if (clicked) {
        changeProgress(e);
    }
});

// 簡潔寫法
progress.addEventListener('mousemove', (e) => clicked && changeProgress(e));

上、下兩個寫法功能是一樣的,這裡解釋一下簡潔寫法。clicked && changeProgress(e)) 的意思是當按下左鍵時(clicked === true)才會接著執行 changeProgress

補充一下,如果變成 clicked || changeProgress(e)),則意思變成當 clicked === false 時,才會執行 changeProgress

進入全螢幕

終於來到最後一個部份啦~

設定監聽事件

當點擊按鍵時進入全螢幕模式

fullScreen.addEventListener('click', goFullScreen);

設定 function

我找到的方法是使用 requestFullscreen(),但比較麻煩的是只有 IE 11 以上才支援,且使用時需要加入前綴(webkit, moz & ms);若要對 IE 有較好支援需要另寫語法,這裡就不多說明了,可以看一下下方參考連結

function goFullScreen() {
    var requestMethod = video.requestFullScreen || video.webkitRequestFullScreen || video.mozRequestFullScreen || video.msRequestFullScreen;
    requestMethod.call(video);
}

如果只需要簡單支援單一瀏覽器(以 chrome 為例),可以寫 video.webkitRequestFullScreen() 即可

呼~終於完成了!

Reference


上一篇
JS30 Day 10 - Hold Shift and Check Checkboxes
下一篇
JS30 Day 12 - Key Sequence Detection
系列文
一起挑戰 JavaScript 30 吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言