成品連結:Custom Video Player、HTML 程式碼、CSS 程式碼、JS 程式碼
今天要做的是播放器!其實瀏覽器本身就有內建瀏覽器,但有個缺點是每個瀏覽器的預設樣式都不一樣;所以如果有樣式統一的需求,就需要自己刻一個了(像 YouTube 就是一致的樣式)
今天的規劃可以分成三個部分,分別是:
那就開始寫程式碼吧~
最簡單的部分就是這裡啦!分別將 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 playPause() {
// 當狀態為未播放時 -> 播放 & 更改圖示
if (video.paused) {
video.play();
playToggle.textContent = '❚ ❚';
} else { // 當狀態為播放時 -> 暫停 & 更改圖示
video.pause();
playToggle.textContent = '►';
}
}
同樣先設定監聽 click
事件
playSkip.forEach(cur => cur.addEventListener('click', skip));
由於我將兩個按鈕綁在同一個變數 playSkip
,所以要透過 forEach
分別設定監聽事件
在 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
來處理
從 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);
先說明一下顯示已播放的方法:這裡是透過調整 HTML tag div.progress__filled
的 flex-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
首先我們要先得出點選或拖曳進度條的位置,透過 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);
我找到的方法是使用 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()
即可
呼~終於完成了!