想邊刷微網誌邊追劇?想邊聊微信邊看球賽?從浏覽器支援擴充功能開始,人們就一直有這樣的需求: Chrome看視訊,有沒有可以彈出視訊視窗的插件? 各個國産殼子浏覽器也争相内置“視訊彈窗播放”的功能,比如當年的 火狐中國版 :
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicWZwpmLjRWYlFWZykzY3YTOkBzNzQmNjJTM4YzYiRDMmVTZmJ2YxYDMwAzNy8CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.jpeg)
(畫中畫),縮寫為 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
requestPictureInPicture()
請求讓該
video
元素進入畫中畫模式,傳回一個 promise,如果沒有異常,這個 promise 包的值會是一個
PictureInPictureWindow
對象,這個對象就代表彈出的那個 PiP 視窗,後面會單獨講它的 API。
async function openPiP(video) {
try {
const pipWindow = await video.requestPictureInPicture() // 進入畫中畫模式
...
} catch (e) {
console.error(e) // 處理異常
}
})
哪些情況下進入畫中畫模式會失敗?一共有 5 種情況:
- 作業系統不支援、或者使用者通過浏覽器選項禁用了此功能,此時
屬性會傳回document.pictureInPictureEnabled
false
- 視訊檔案錯誤、或者沒有視訊流隻有音頻流
- 此次請求不是由使用者操作觸發的,比如使用者沒有點選任何按鈕,頁面自動執行該方法,會被當做惡意行為攔截掉
- 目前頁面 通過 feature-policy 禁用了畫中畫特性 ,此時
屬性也會傳回document.pictureInPictureEnabled
false
- 目前 video 元素通過
屬性(HTML 屬性和 DOM 屬性均可)禁用了畫中畫特性disablePictureInPicture
2. video
元素新增的屬性 disablePictureInPicture
video
disablePictureInPicture
通過該屬性可以禁用
video
元素的畫中畫特性,右鍵菜單中的“畫中畫”選項會被禁用。
通過 HTML 屬性:
<video src="..." disablePictureInPicture>
通過 DOM 屬性:
video.disablePictureInPicture = true
3. video
元素新增的事件 enterpictureinpicture
和 leavepictureinpicture
video
enterpictureinpicture
leavepictureinpicture
video.addEventListener('enterpictureinpicture', function(pipWindow) {
// 進入了畫中畫模式,可以拿到 pipWindow 對象
})
video.addEventListener('leavepictureinpicture', function() {
// 退出了畫中畫模式
})
4. document
上新增的方法 exitPictureInPicture()
document
exitPictureInPicture()
因為一個頁面隻能打開一個 PiP 視窗,是以讓
video
元素退出畫中畫模式的方法不在 video 元素自己身上,而在
document
上。
這個方法也傳回一個 promise,不過 promise 包的值是個
undefined
。
5. document
上新增的屬性 pictureInPictureElement
和 pictureInPictureEnabled
document
pictureInPictureElement
pictureInPictureEnabled
類似于
document.pointerLockElement
document.fullscreenElement
,
document.pictureInPictureElement
會傳回目前頁面中處于畫中畫模式的
video
元素,如果沒有的話,傳回
null
document.pictureInPictureEnabled
上面已經提到過了,在目前頁面不支援或被禁用畫中畫模式的情況下會傳回
false
,否則傳回
true
這兩個屬性都是隻讀的。
6. PictureInPictureWindow
對象的 API
PictureInPictureWindow
從
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
以外的元素
video
規範制定者表示未來有可能
原文釋出時間為:2018年06月21日
原文作者:掘金
本文來源:
掘金 https://juejin.im/entry/5b3a29f95188256228041f46如需轉載請聯系原作者