做爲HTML5中的一大亮點,video
標籤的出現取代了以往Flash
的地位,成爲了標準的視頻播放方案。css
接下來咱們將一步一步挖掘video
的全部功能。html
<video> <source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" type="video/mp4"> <source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.webm" type="video/webm"> <p>您的瀏覽器不支持HTML5播放器</p> </video> 複製代碼
video
標籤下面使用多個source
標籤連接視頻源,瀏覽器選取第一個支持的視頻格式進行播放,當瀏覽器不支持video
播放功能時,會顯示標籤裏的其餘內容,提示用戶採用其餘方法觀看。web
固然,仍是更簡單是使用方法:瀏覽器
<video src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"> <p>您的瀏覽器不支持HTML5播放器</p> </video> 複製代碼
咱們獲得下圖,不知道的覺得咱們只加載了一張圖片,由於它不會動:緩存
咱們在video
標籤上添加autoplay
屬性讓其加載後自動播放。bash
<video autoplay>...</video> 複製代碼
好了,如今視頻動起來了,它開始播放了!服務器
可是問題又來了,把鼠標懸上去以後,發現啥也沒發生,咱們控制不了視頻的播放!markdown
咱們爲video
標籤再加上controls
屬性,再來看看。數據結構
如今當你鼠標移上視頻的時候,底部會出現一些控制功能,每一個瀏覽器都不同,可是通常會提供:播放/暫停、導航、全屏、音量功能。dom
布爾屬性,循環播放
布爾屬性,控制視頻是否靜音
視頻預加載設置。
設置合適的值能夠減小服務器的訪問量。
視頻海報,須要是圖片地址。 當視頻在第一幀可用時,啥都不會展現,可是若是設置了poster
,那麼在用戶播放或者跳幀前展現該圖片做爲封面。
<video poster="/example/image.jpg">...</video> 複製代碼
可是咱們發現,poster
的尺寸和視頻並不匹配,會出現空白的地方,那咱們怎麼去調整呢?使用css
屬性object-fit
能夠幫助咱們,經過設置合適的值能讓咱們的poster
撐滿元素。
元素的寬高,通常不在這裏設置。
瞭解了video
標籤支持的屬性以後,咱們能夠將一個視頻展示在用戶面前,它能夠自動播放,循環播放,靜音播放,有首幀海報。可是,這些功能確定是不能知足咱們的不少需求的,而且瀏覽器提供的controls
各有所異,咱們又怎麼去實現一個跨瀏覽器的播放器呢?
還好咱們可使用JS去控制DOM來實現更多的媒體操做功能。
點擊查看HTML Audio/Video
具體支持的屬性方法列表
接下來,咱們就要實現一個本身的視頻播放器!
咱們來實現的第一個功能,就是最基礎的播放暫停。
須要使用到的API:
play()
、parse()
paused
咱們按照最開始的基礎使用
小節,先寫一個video
標籤。一開始這個視頻是沒有控制器,而且不會自動播放的。
而後咱們開始使用JS配合DOM來定義一些視頻播放控制器,先來定義一個Video
類
class Video { $video; constructor(options) { this.$video = document.querySelector(options.videoElement); } } 複製代碼
再定義播放/暫停方法:
play() { this.$video.play(); } pause() { this.$video.pause(); } 複製代碼
而後咱們將事件綁定在對應的元素上:
$playCtr.addEventListener("click", e => { if (this.$video.paused) { this.play(); e.target.innerText = "暫停"; } else { this.pause(); e.target.innerText = "播放"; } }); 複製代碼
這裏注意的是咱們判斷視頻的播放狀態paused
來切換播放狀態。 這樣咱們就有了一個能夠切換的播放/暫停的功能。
接下來咱們實現一個實時反映視頻播放進度的控制器,而且可以利用該控制器來跳轉視頻的播放時間,這裏咱們使用最基礎的控件input[type=range]
。
須要使用到的API:
duration
、currentTime
在確保視頻元數據加載完畢後,咱們來初始化進度條的一些基礎信息:
initProgress($progressCtr) { $progressCtr.value = 0; $progressCtr.min = 0; $progressCtr.max = this.$video.duration; } 複製代碼
而後監聽播放時間變化來更新控件的值:
this.$video.addEventListener("timeupdate", e => { $progressCtr.value = e.target.currentTime; }); 複製代碼
最後監聽控件的值來切換視頻播放點:
$progressCtr.addEventListener("change", e => { this.$video.currentTime = +e.target.value; }); 複製代碼
這樣咱們就實現了一個簡單的進度條:
其實音量控制和進度控制的思路是同樣的,也是採用input[type=range]
控件,這裏咱們還須要控制靜音狀態。
須要使用到的API:
muted
、volume
咱們先初始化音量控件的最大最小值:
initVolume($volumeCtr) { $volumeCtr.value = this.$video.volume; $volumeCtr.step = 0.01; $volumeCtr.min = 0; $volumeCtr.max = 1; } 複製代碼
這裏有些小細節,音量的最大值爲1.0(100%),最小值爲0。 因爲默認step爲1,因此咱們須要調節該值,使咱們能更精細地控制音量。
而後咱們再監聽控件的值變化,去改變視頻的音量:
$volumeCtr.addEventListener("change", e => { const volume = e.target.value; if (volume > 0) { this.$video.muted = false; } this.$video.volume = volume; }); 複製代碼
調節播放速率也是一個很常見的功能,慢速看細節,快速跳劇情,咱們來實現一個。也是使用input[type=range]
控件。
須要使用到的API:
playbackRate
inityPlaybackRate($speedCtr) { $speedCtr.value = 1; $speedCtr.step = 0.25; $speedCtr.min = 1; $speedCtr.max = 2; $speedCtr.addEventListener("change", e => { this.$video.playbackRate = +e.target.value; }); } 複製代碼
咱們來實現一個讓視頻全屏播放的功能。
須要使用到的API:
Element.requestFullscreen()
這個功能和video
標籤沒有什麼太大的聯繫,任何元素均可以全屏展現。該方法會返回一個Promise
,並在完成全屏時resolve
。
initFullscreen($fullscreenCtr) { $fullscreenCtr.addEventListener("click", e => { this.$video.requestFullscreen(); }); } 複製代碼
這裏咱們發現,在Chrome和Safari中,若是直接將video
標籤全屏,瀏覽器會自動將元素撐滿屏幕,而且會加上controls
。因此若是咱們要把本身的控制器也帶上,就得在最外層再包裝一層,而後全屏最外層元素。
另外全屏接口是有瀏覽器差別的,因此這裏有個兼容方法:
function toFullVideo(videoDom) { if (videoDom.requestFullscreen) { return videoDom.requestFullscreen(); } else if (videoDom.webkitRequestFullScreen) { return videoDom.webkitRequestFullScreen(); } else if (videoDom.mozRequestFullScreen) { return videoDom.mozRequestFullScreen(); } else { return videoDom.msRequestFullscreen(); } } 複製代碼
儘管咱們可以實現基礎的視頻控制,不過如今咱們的用戶體驗仍是不好的,好比視頻緩存量顯示、視頻加載提示、錯誤提示、字幕等等,這些咱們都沒有,不過好在咱們有一些想法去實現這些功能。
所須要使用到的API:
buffered
waiting
,error
,canplay
,playing
...首先咱們來測試一些播放事件:
function log(e) { console.log(`${e.type}`); } this.$video.addEventListener("canplay", log); this.$video.addEventListener("canplaythrough", log); this.$video.addEventListener("play", log); this.$video.addEventListener("playing", log); this.$video.addEventListener("waiting", log); this.$video.addEventListener("ended", log); 複製代碼
在咱們播放一個正常視頻時它是這樣的:
play
playing
ended
複製代碼
當視頻有一些卡頓時是這樣的:
play
playing
waiting
canplay
playing
waiting
...
複製代碼
在這裏咱們能夠針對視頻緩衝增長加載提示,也就是在waiting
的時候加載提示(轉圈圈),在canplay
時去掉提示。
另外還有一個事件叫作progress
,用它搭配上buffered
,我們就能夠清楚的知道視頻的緩存加載了多少。
buffered
返回的是一個TimeRanges
對象,他有length
屬性標識有幾個buffer
區間,用戶可能跳轉視頻,因此可能出現多個。還有倆方法獲取buffer
區間的起始位置,start
、end
:
// 第一個區間的起始位置 start(0) // 第一個區間的結束位置 end(0) 複製代碼
那麼咱們能夠獲取到全部的buffer
記錄:
logBuffered() { this.$video.addEventListener("progress", e => { const { length } = this.$video.buffered; const buffers = []; for (let i = 0; i < length; i++) { const s = this.$video.buffered.start(i); const e = this.$video.buffered.end(i); buffers.push(`${s} - ${e}`); } }); } 複製代碼
最後buffers
的結果多是這樣的:
["0 - 10", "20 - 40"] 複製代碼
咱們這裏只是展現其數據結構
考慮到progress
的觸發頻率也能夠作節流,而後渲染UI。