跳至主要内容

[JS30] Day11: Custom HTML Video Player

HTML 部分

Video and Audio APIs @ MDN - Learn web development

Video Element

因為我對 HTML5 的 Video Element 不太熟,所以就自己在打了一次。

先來看一下 HTML5 中 Video element,預設有一些可用的屬性:

<video
autoplay
controls
muted
height="300px"
src="http://www.html5videoplayer.net/videos/toystory.mp4"
></video>

<!--
- autoplay: 載入自動播放
- controls: 出現瀏覽器預設的播放器
- loop: 是否重複播放
- muted: 播放時預設靜音
- src: 影片連結
- height: 設定高度(不能設定百分比)
- width: 設定寬度(不能設定百分比)
- 等等...
-->

HTML5 Video Element

由此可知,其實如果沒有要客制化播放器的話,直接在 <video> tag 中加上 controls 就可以使用瀏覽器內建的播放器了。

以下是 JS30 的 HTML Code

原本的影片連結我不知道為什麼後來沒辦法正常播放,所以我換了一個連結:

//- pug .player
video.player__video.viewer(src="http://www.html5videoplayer.net/videos/toystory.mp4")
.player__controls .progress .progress_filled button.player__button.toggle(title="Toggle Play") ►
input.player__slider(type="range" name="volume" min="0" max="1" step="0.05" value="1")
input.player__slider(type="range" name="playbackRate" min="0.5" max="2" step="0.1" value="1")
button.player__button(data-skip="-10") « 10s button.player__button(data-skip="25") 25s »

再不添加任何 CSS 的情況下,畫面會長這樣:

img

CSS 部分

簡單記錄一下幾個重點

重置 button

/**
* 利用 background, border, outline, padding 的設定重置 button
*/
.player__button {
background: none;
border: 0;
line-height: 1;
color: white;
text-align: center;
outline: 0;
padding: 0;
cursor: pointer;
max-width: 50px;
}

隱藏播放清單

/**
* 透過 transform: translateY 來隱藏播放清單
*/
.player__controls {
display: flex;
position: absolute;
bottom: 0;
width: 100%;
transform: translateY(100%) translateY(-5px);
transition: all 0.3s;
flex-wrap: wrap;
background: rgba(0, 0, 0, 0.1);
}
/* hover 時顯示播放清單 */
.player:hover .player__controls {
transform: translateY(0);
}

添加完 CSS 後畫面會長這樣子:

img

JS 部分

HTML5 Video Element 主要繼承自 HTMLMediaElement,因此我們也可以從中找到可用的屬性:

const video = document.querySelector('video');

// 屬性(property)
video.paused; // 影片是否為暫停狀態,ReadOnly
video.muted;
video.volume; // from 0 to 1
video.playbackRate; // 播放速度,預設是 1.0
video.currentTime; // 可以取得和設定影片播放到的時間
video.duration; // 取得影片的時間

// 方法(method)
video.play();
video.pause();

// 事件(event),其他事件可直接用 console.dir(video) 查看
play;
pause;
timeupdate; // 當 video.currentTime 改變時會觸發

選擇所有相關元素

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');

播放或停止

點擊 video 視窗時可以播放或停止。使用 video.play()video.pause() 來控制播放或暫停:

// 播放或暫停影片
function togglePlay() {
let playOrPause = video.paused ? 'play' : 'pause';
video[playOrPause](); // video.play() or video.pause()
}

// 切換播放按鈕 icon
function switchPlayButtonIcon() {
let icon = this.paused ? '►' : '❚ ❚';
toggle.textContent = icon;
}

video.addEventListener('click', togglePlay);
video.addEventListener('play', switchPlayButtonIcon);
video.addEventListener('pause', switchPlayButtonIcon);
toggle.addEventListener('click', togglePlay);

快轉與倒退

使用 video.currentTime 來取得並設定當前影片播放到的時間點。

function skip(e) {
video.currentTime += Number(e.target.dataset.skip);
}
skipButtons.forEach((skipButton) => {
skipButton.addEventListener('click', skip);
});

調整音量和播放速度

使用 video.volumevideo.playbackRate 來控制播放的音量(0~1)和播放速度(預設是 1.0):

function handleRangeUpdate(e) {
video[e.target.name] = e.target.value;
}
ranges.forEach((rangeInput) => {
rangeInput.addEventListener('input', handleRangeUpdate);
});

進度條相關函式

使用 video.duration 可以取得影片的時間長度,因此我們可以用 (video.currentTime/video.duration) 知道目前影片播放了多少百分比,接著利用 progressBar.style.flexBasis 來改變進度條的長度:

// 顯示當前播放進度
function showCurrentProgress() {
let progressPercent = (video.currentTime / video.duration) * 100;
// 利用 flex-basis 改變 progress bar 長度
progressBar.style.flexBasis = progressPercent + '%';
}
// 當 video.currentTime 改變時會觸發 timeupdate
video.addEventListener('timeupdate', showCurrentProgress);

當我們要透過點擊進度條來改變播放時間時,要計算使用者點擊進度條位置的長度(e.offsetX)和總長度(progress.offsetWidth)的百分比,得到百分比後,再給回 video.currentTime

// 點擊進度條改變當前播放進度
function changeCurrentProgress(e) {
// console.log('offsetParent', e.target.offsetParent)
let timeAtProgressBar = (e.offsetX / progress.offsetWidth) * video.duration;
video.currentTime = Number(timeAtProgressBar);
}
progress.addEventListener('click', changeCurrentProgress);

Day13: Slide In on Scroll 也會用到 element.offset 的屬性,因為offset是以外層容器左上角為原點,因此建議在使用offset前還是先用 offsetParent 看一下相對的外層容器指的是誰。

完成作品

Day11: Custom HTML Video Player @ PJCHENder CodePen

參考資料