[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: 設定寬度(不能設定百分比)
- 等等...
-->
由此可知,其實如果沒有要客制化播放器的話,直接在 <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 的情況下,畫面會長這樣:
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 後畫面會長這樣子:
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.volume
和 video.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
參考資料
- HTMLMediaElement @ WebAPIs MDN
- HTMLVideoElement @ WebAPIs MDN
- HTML5 Elements @ MDN
- TimeRanges @ MDN