天天看點

浏覽器中的畫中畫(Picture-in-Picture)模式及其 API

想邊刷微網誌邊追劇?想邊聊微信邊看球賽?從浏覽器支援擴充功能開始,人們就一直有這樣的需求: Chrome看視訊,有沒有可以彈出視訊視窗的插件? 各個國産殼子浏覽器也争相内置“視訊彈窗播放”的功能,比如當年的 火狐中國版

浏覽器中的畫中畫(Picture-in-Picture)模式及其 API
這麼多年過去了,正統浏覽器們終于要内置這個功能了,而且還有配套的 API 給開發者使用。 WICG ,全名為 Web Incubator Community Group(Web 孵化器社群小組),主要成員為 Chrome 的工程師們,他們從一年前開始标準化這個能讓視訊彈窗播放的特性,起名為  Picture-in-Picture

(畫中畫),縮寫為 PiP。

Chrome 從今年年初開始着手實作,到本文撰稿時,已經實作的差不多了。這篇文章将主要講兩方面:Chrome 目前實作的 PiP 互動是怎麼樣的,以及有哪些 API 讓我們開發者使用。

Chrome 中的 PiP 互動

Chrome 官方提供了一個 demo 頁面 

Picture-in-Picture Sample

,我用這個 demo 錄了一個簡單的示範視訊:

從視訊中可以看到,Chrome 在原生的 video 控件中提供了開啟畫中畫模式的菜單項,一旦開啟畫中畫模式,原本頁面中内聯(inline)展現的那個 video 還占據着原來的位置,各種控件也都在,但最重要的視訊畫面已經被無縫的轉移到了新的彈出視窗中了,隻剩下一張 poster 圖檔和一層灰色的遮罩。

我們把彈出的那個視訊視窗叫做 PiP 視窗,PiP 視窗隻有視訊畫面,沒有标題欄和位址欄(chrome-less),這一點和用 

window.open()

打開的彈窗不一樣。同時,PiP 視窗也沒有完整的視訊控件,隻有開始/暫停/關閉按鈕三個,時間、進度條、音量這些控件都沒有。PiP 視窗還支援拖拽改變大小(不能大于一個象限),以及支援拖拽到任意位置(錄屏裡沒有展現,因為在本文撰寫時還不支援)。還有最重要的一點是,PiP 視窗在所有視窗中是置頂的(always on top),正如我在錄屏裡示範的,當焦點切換到 Safari 後,Chrome 中打開的 PiP 視窗仍然是在最上層的。

另外一個沒有在錄屏裡展現出來的互動是,“同一時刻隻能有一個 PiP 視窗”。同一個頁面裡如果為多個視訊開啟畫中畫模式,前面開啟的那個會自動退出畫中畫模式,同一個浏覽器視窗下的多個 Tab 裡的視訊亦如此,同一個浏覽器打開的多個浏覽器視窗亦如此(在本文撰稿時,Chrome 還沒有實作最後一種情況,即多個浏覽器視窗可以同時開啟多個 PiP 視窗,已确認是 bug 不是 feature)。

PiP 相關 API

1. 

video

 元素新增的方法 

requestPictureInPicture()

請求讓該 

video

 元素進入畫中畫模式,傳回一個 promise,如果沒有異常,這個 promise 包的值會是一個 

PictureInPictureWindow

對象,這個對象就代表彈出的那個 PiP 視窗,後面會單獨講它的 API。

async function openPiP(video) {
  try {
    const pipWindow = await video.requestPictureInPicture() // 進入畫中畫模式
    ...
  } catch (e) {
    console.error(e) // 處理異常
  }
})           

哪些情況下進入畫中畫模式會失敗?一共有 5 種情況:

  1. 作業系統不支援、或者使用者通過浏覽器選項禁用了此功能,此時 

    document.pictureInPictureEnabled

     屬性會傳回 

    false

  2. 視訊檔案錯誤、或者沒有視訊流隻有音頻流
  3. 此次請求不是由使用者操作觸發的,比如使用者沒有點選任何按鈕,頁面自動執行該方法,會被當做惡意行為攔截掉
  4. 目前頁面 通過 feature-policy 禁用了畫中畫特性 ,此時 

    document.pictureInPictureEnabled

    屬性也會傳回 

    false

  5. 目前 video 元素通過 

    disablePictureInPicture

     屬性(HTML 屬性和 DOM 屬性均可)禁用了畫中畫特性

2. 

video

 元素新增的屬性 

disablePictureInPicture

通過該屬性可以禁用 

video

 元素的畫中畫特性,右鍵菜單中的“畫中畫”選項會被禁用。

通過 HTML 屬性:

<video src="..." disablePictureInPicture>

通過 DOM 屬性:

video.disablePictureInPicture = true

3. 

video

 元素新增的事件 

enterpictureinpicture

 和 

leavepictureinpicture

video.addEventListener('enterpictureinpicture', function(pipWindow) {
  // 進入了畫中畫模式,可以拿到 pipWindow 對象
})

video.addEventListener('leavepictureinpicture', function() {
  // 退出了畫中畫模式
})           

4. 

document

 上新增的方法 

exitPictureInPicture()

因為一個頁面隻能打開一個 PiP 視窗,是以讓 

video

 元素退出畫中畫模式的方法不在 video 元素自己身上,而在 

document

 上。

這個方法也傳回一個 promise,不過 promise 包的值是個 

undefined

5. 

document

 上新增的屬性 

pictureInPictureElement

和 

pictureInPictureEnabled

類似于

document.pointerLockElement

document.fullscreenElement

, 

document.pictureInPictureElement

 會傳回目前頁面中處于畫中畫模式的 

video

 元素,如果沒有的話,傳回 

null

document.pictureInPictureEnabled

上面已經提到過了,在目前頁面不支援或被禁用畫中畫模式的情況下會傳回 

false

,否則傳回 

true

這兩個屬性都是隻讀的。

6. 

PictureInPictureWindow

對象的 API

requestPictureInPicture()

方法的傳回值和 

enterpictureinpicture

事件的回調參數中可以拿到 

pipWindow

 對象,該對象有兩個屬性 

width

height

,還支援一個

resize

事件,在使用者改變 PiP 視窗大小時會觸發。

async function openPiP(video) {
  const pipWindow = await video.requestPictureInPicture()
  console.log(pipWindow.width, pipWindow.height) // 列印了預設的視窗大小

  pipWindow.addEventListener('resize', function() {
    console.log(pipWindow.width, pipWindow.height) // 使用者改變 PiP 視窗大小時觸發
  })
}           

注意這裡的 

width

height

是隻讀的,你不能通過給他們指派來改變視窗大小。

雜項

1. Safari 中已有的畫中畫 API

Safari 在兩年前就支援了

畫中畫模式

,還有一套帶字首的配套 API,叫 

Presentation Mode

。好消息是這個新規範也有 Safari 的人參與了制定,事實上目前為止四大浏覽器廠商隻剩 Edge 還沒表示支援。

2. 畫中畫這個名字的由來

“畫中畫”這東西是十幾年前電視機上的一個功能,我自己去年看到這個規範的時候也覺的它這個名字比較誤導人,現在規範 issue 裡也

有個人在問

,希望你沒把它了解成是嵌套的 

<img>

3. Chrome 哪個版本支援

現在的 Chrome Canary(69)還沒有預設打開這個特性,需要你手動開啟

chrome://flags/#enable-experimental-web-platform-features

chrome://flags/#disable-background-video-track

chrome://flags/#enable-picture-in-picture

這三個開關,不用 Canary 的同學老實等兩個月。

4. 可不可能彈出 

video

 以外的元素

規範制定者表示未來有可能

原文釋出時間為:2018年06月21日

原文作者:掘金

本文來源: 

掘金 https://juejin.im/entry/5b3a29f95188256228041f46

如需轉載請聯系原作者

繼續閱讀