今天要來自定義一個影片控制器,因為主要還是學習 javascript,樣式的部分就不多加琢磨,直接使用作者給的那些元素進行功能串接吧。
需將這些 html 元素串接到影片播放器對應的常見功能,如:切換播放暫停、控制聲音、播放速度、播放時間跳轉等,那我們就會用到各式各樣的HTMLMediaElement 媒體元素的 properties 及 method,推薦大家還是去 MDN 快速閱覽一次,或用 Javascript 取得節點用console.dir()
印出來觀察看看。
實作開始
forEach
都加上針對點擊事件的監聽器,觸發時便會執行togglePlay()
函式。// <video>影片容器本體,我們很多地方都需要改變它的參數
const video = document.querySelector(".player__video.viewer");
// 左下方播放鈕
const playButton = document.querySelector(".player__button.toggle");
// 放在陣列中,並依序添加點擊事件監聽器
[video, playButton].forEach((node) => {
node.addEventListener("click", togglePlay);
});
// 函式切換播放,利用媒體元素的paused暫停與否狀態,去判斷這次點擊要播放還是暫停
// ,並記得對按鈕圖示進行變更,這邊作者使用文字符號代替icon所以使用textContent
function togglePlay() {
if (video.paused) {
video.play();
playButton.textContent = "▍▍";
} else {
video.pause();
playButton.textContent = "►";
}
}
<input type="range">
且name
即代表媒體元素的該項特性且可供操作,所以我們先用querySelectorAll
取得NodeList
後將它們依序添加 input 值改變值的事件監聽器,觸發時便會執行handlePlayerSliderInput()
函式。//volume控制滑塊 & playbackRate控制滑塊
const skipButtonList = document.querySelectorAll(".player__button[data-skip]");
// 依序添加點擊事件監聽器
playerSliderList.forEach((input) => {
input.addEventListener("input", handlePlayerSliderInput);
});
// 將video媒體元素的這個特性 變更為這個滑塊的當前值
function handlePlayerSliderInput() {
video[this.name] = this.value;
}
currentTime
已播放時間,所以我們先用querySelectorAll
取得NodeList
後將它們依序添加 click 的事件監聽器,handleSkipButtonClick()
函式。// 2個播放時間跳轉按鈕
const playerSliderList = document.querySelectorAll(".player__slider");
// 依序添加點擊事件監聽器
skipButtonList.forEach((button) => {
button.addEventListener("click", handleSkipButtonClick);
});
//調整影片當前進度,因為data-skip是字串記得轉成數字
function handleSkipButtonClick() {
video.currentTime += Number(this.dataset.skip);
}
//進度條容器
const progressContainer = document.querySelector(".progress");
//裡面黃色那條實際反應播放進度的
const progressFilled = document.querySelector(".progress__filled");
.progress__filled
的flex-basis
CSS 屬性實現進度條的,所以我們可以利用變更這個屬性的百分比來反應播放時間,這邊利用到timeupdate
這個事件,只要currentTime
變更都會觸發,既然都有currentTime
播放時間,那當然有另一個屬性代表影片的總長duration
,動一下腦就可以知道如何計算現在的播放進度囉!// 影片播放、快進倒退、還有等下寫的點擊、拖曳進度條都會觸發這個監聽器
video.addEventListener("timeupdate", updateProgress);
// 調整進度條的CSS flex-basis %數 = 算出來的播放進度
function updateProgress() {
progressFilled.style.flexBasis = `${
(video.currentTime / video.duration) * 100
}%`;
}
progressContainer
X 軸位置 ÷ progressContainer
的容器總寬度 × video.duration
影片總長度,賦值成影片的currentTIme
,然後因為又觸發了 timeupdate 事件,所以進度條的 flex-basis 會跟著變更。progressContainer.addEventListener("click", handleProgressClick);
function handleProgressEvents(e) {
video.currentTime =
(e.offsetX / progressContainer.clientWidth) * video.duration;
}
// 滑鼠按下時才新增 滑鼠移動的事件監聽器
progressContainer.addEventListener("mousedown", handleProgressMouseDown);
function handleProgressMouseDown() {
progressContainer.addEventListener("mousemove", handleProgressEvents);
}
// 滑鼠彈起 及 滑鼠離開progressContainer容器則會將滑鼠移動的監聽器移除
progressContainer.addEventListener("mouseup", handleProgressMouseUp);
function handleProgressMouseUp() {
progressContainer.removeEventListener("mousemove", handleProgressEvents);
}
progressContainer.addEventListener("mouseleave", handleProgressMouseLeave);
function handleProgressMouseLeave() {
progressContainer.removeEventListener("mousemove", handleProgressEvents);
}
這邊我透過觸發mousedown
時才會新增mousemove
的事件監聽器,滑鼠彈起或離開容器時則會再將這個mousemove
的事件監聽器移除,這樣會相對節省一些效能,而不會只要滑鼠在progressContainer
容器中滑動時都會一直觸發並判斷要不要更改影片時間。