紮實基礎系列 - H5播放器

做爲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_demo.jpg

經常使用屬性

autoplay

咱們在video標籤上添加autoplay屬性讓其加載後自動播放。bash

<video autoplay>...</video>
複製代碼

好了,如今視頻動起來了,它開始播放了!服務器

controls

可是問題又來了,把鼠標懸上去以後,發現啥也沒發生,咱們控制不了視頻的播放!markdown

咱們爲video標籤再加上controls屬性,再來看看。數據結構

截屏20200324上午11.19.15.jpg

如今當你鼠標移上視頻的時候,底部會出現一些控制功能,每一個瀏覽器都不同,可是通常會提供:播放/暫停、導航、全屏、音量功能。dom

loop

布爾屬性,循環播放

muted

布爾屬性,控制視頻是否靜音

preload

視頻預加載設置。

  • none 不會請求視頻任何信息
  • metadata 只請求視頻的一些元信息
  • auto 視頻須要下載

設置合適的值能夠減小服務器的訪問量。

poster

視頻海報,須要是圖片地址。 當視頻在第一幀可用時,啥都不會展現,可是若是設置了poster,那麼在用戶播放或者跳幀前展現該圖片做爲封面。

<video poster="/example/image.jpg">...</video>
複製代碼

可是咱們發現,poster的尺寸和視頻並不匹配,會出現空白的地方,那咱們怎麼去調整呢?使用css屬性object-fit能夠幫助咱們,經過設置合適的值能讓咱們的poster撐滿元素。

width,height

元素的寬高,通常不在這裏設置。

DOM API

瞭解了video標籤支持的屬性以後,咱們能夠將一個視頻展示在用戶面前,它能夠自動播放,循環播放,靜音播放,有首幀海報。可是,這些功能確定是不能知足咱們的不少需求的,而且瀏覽器提供的controls各有所異,咱們又怎麼去實現一個跨瀏覽器的播放器呢?

還好咱們可使用JS去控制DOM來實現更多的媒體操做功能。

點擊查看HTML Audio/Video具體支持的屬性方法列表

接下來,咱們就要實現一個本身的視頻播放器!

DIY

播放/暫停

咱們來實現的第一個功能,就是最基礎的播放暫停。

須要使用到的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:

  • 屬性:durationcurrentTime

在確保視頻元數據加載完畢後,咱們來初始化進度條的一些基礎信息:

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:

  • 屬性:mutedvolume

咱們先初始化音量控件的最大最小值:

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:

  • DOM: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
  • 事件:waitingerrorcanplayplaying...

緩衝提示

首先咱們來測試一些播放事件:

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區間的起始位置,startend

// 第一個區間的起始位置
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。

相關文章
相關標籤/搜索