PJCHENder 未整理筆記

[JS30] Day11: Custom HTML Video Player

2017-09-26

[JS30] Day11: Custom HTML Video Player

@([WesBos] JS30)[JavaScript]

HTML 部分

Video and Audio APIs @ MDN - Learn web development

Video Element

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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

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

1
2
3
4
5
6
7
8
9
10
11
//- 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 利用 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;
}

隱藏播放清單

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 透過 transform: translateY 來隱藏播放清單
*/
.player__controls {
display: flex;
position: absolute;
bottom: 0;
width: 100%;
transform: translateY(100%) translateY(-5px);
transition: all .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,因此我們也可以從中找到可用的屬性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 改變時會觸發

選擇所有相關元素

1
2
3
4
5
6
7
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() 來控制播放或暫停:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 播放或暫停影片
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 來取得並設定當前影片播放到的時間點。

1
2
3
4
5
6
function skip (e) {
video.currentTime += Number(e.target.dataset.skip)
}
skipButtons.forEach(skipButton => {
skipButton.addEventListener('click', skip)
})

調整音量和播放速度

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

1
2
3
4
5
6
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 來改變進度條的長度:

1
2
3
4
5
6
7
8
// 顯示當前播放進度
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

1
2
3
4
5
6
7
// 點擊進度條改變當前播放進度
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

參考資料

掃描二維條碼,分享此文章