天天看點

如何在浏覽器實作畫中畫觀看視訊

原文作者:Arnaud Lewis

譯者:UC 國際研發 橋川

畫中畫(PiP)允許使用者在浮動視窗中觀看視訊(總是在其他視窗的頂部),這樣他們就可以在與其他站點或應用程式互動時密切關注他們正在觀看的内容。

使用新的畫中畫Web API,您可以在網站上啟動和控制視訊元素的畫中畫。 如果你還不知道什麼是畫中畫,可以看看上面的視訊效果。

背景

2016年9月,macOS Sierra中的Safari支援了Picture-in-Picture API。 六個月後,在Android O上的Chrome通過使用原生Android API,實作在移動裝置上播放畫中畫視訊。 六個月後,我們宣布了建構和标準化Web API的意圖,該功能與Safari相容,允許Web開發者建立和控制圍繞畫中畫的完整體驗。我們來了!

一起看看代碼實作

進入畫中畫

讓我們從video元素及使用者與其互動的方式(例如按鈕元素)開始。

<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>           

僅請求畫中畫以響應使用者手勢,并且永遠不要videoElement.play()傳回的promise中響應。 這是因為promises尚未傳播使用者手勢。而是在

pipButtonElement上的單擊處理程式中調用requestPictureInPicture(),如下所示。 如果使用者點選兩次,您有責任處理會發生的情況。

pipButtonElement.addEventListener('click', async function() {
  pipButtonElement.disabled = true;

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});           

當promise resolves,Chrome會将視訊縮小為一個小視窗,使用者可以在其中移動并定位在其他視窗上。

你完成了。 很好! 你可以停止閱讀,享受你當之無愧的假期。可悲的是,情況并非總是如此。 promise可能因以下任何原因而拒絕:

  • 系統不支援畫中畫。
  • 由于限制性功能政策,不允許文檔使用畫中畫。
  • 視訊中繼資料尚未加載(videoElement.readyState === 0)。
  • 視訊檔案僅有音頻。
  • 新的disablePictureInPicture屬性出現在視訊元素上。

該調用不是在使用者手勢事件處理程式中進行的(例如:點選按鈕)。

本文後面“特性支援部分”将告訴你如何根據這些限制啟用/禁用按鈕。

讓我們添加一個try...catch塊來捕獲這些潛在的錯誤,讓使用者知道發生了什麼。

pipButtonElement.addEventListener('click', async function() {
  pipButtonElement.disabled = true;

  try {
    await videoElement.requestPictureInPicture();
  }
  catch(error) {
    // TODO: Show error message to user.
  }
  finally {
    pipButtonElement.disabled = false;
  }
})           

無論是否在畫中畫中,視訊元素的行為都相同:觸發事件并調用方法。 它反映了畫中畫視窗中的狀态變化(例如播放,暫停,搜尋等),并且還可以在JavaScript中以程式設計方式更改狀态。

退出畫中畫

現在,讓我們讓按鈕可以切換進入和退出畫中畫。 我們首先要檢查隻讀對象document.pictureInPictureElement是否是我們的video元素。如果不是,我們發送請求以如上所述輸入畫中畫。否則,我們要求通過調用document.exitPictureInPicture()離開,這意味着視訊将顯示在原始頁籤中。 請注意,此方法也會傳回一個promise。

...

try {

if (videoElement !== document.pictureInPictureElement) {

await videoElement.requestPictureInPicture();           

} else {

await document.exitPictureInPicture();           

}

監聽Picture-in-Picture事件

作業系統通常将Picture-in-Picture限制在一個視窗,是以Chrome的實作遵循這種模式。這意味着使用者一次隻能播放一個畫中畫視訊。您應該期望使用者即使您沒有要求也退出Picture-in-Picture。

新的enterpictureinpicture和leavepictureinpicture事件處理程式讓我們為使用者量身定制體驗。它可以是浏覽視訊目錄,也可以是直播聊天。

videoElement.addEventListener('enterpictureinpicture', function(event) {
  // Video entered Picture-in-Picture.
});

videoElement.addEventListener('leavepictureinpicture', function(event) {
  // Video left Picture-in-Picture.
  // User may have played a Picture-in-Picture video from a different page.
});           
擷取畫中畫視窗大小

如果要在視訊進入和離開畫中畫時調整視訊品質,則需要知道畫中畫視窗大小,并在使用者手動調整視窗大小時收到通知。

下面的示例顯示了如何在建立或調整畫闆大小時擷取畫中畫視窗的寬度和高度。

let pipWindow;

videoElement.addEventListener('enterpictureinpicture', function(event) {
  pipWindow = event.pictureInPictureWindow;
  console.log(`> Window size is ${pipWindow.width}x${pipWindow.height}`);
  pipWindow.addEventListener('resize', onPipWindowResize);
});

videoElement.addEventListener('leavepictureinpicture', function(event) {
  pipWindow.removeEventListener('resize', onPipWindowResize);
});

function onPipWindowResize(event) {
  console.log(`> Window size changed to ${pipWindow.width}x${pipWindow.height}`);
  // TODO: Change video quality based on Picture-in-Picture window size.
}           

我建議不要直接綁定到resize事件,因為對畫中畫視窗大小進行的每個小改動都會觸發一個單獨的事件,如果你在每次調整大小時都做了昂貴的操作,可能會導緻性能問題。換句話說,調整大小操作将反複觸發事件。我建議使用常用技術,如使用throttling(節流閥) 和 debouncing(防抖動)來解決這個問題。

特性支援

可能你的浏覽器不支援畫中畫Web API,是以您必須進行特性檢測以提供漸進增強功能。即使支援它,它也可能被使用者關閉或被功能政策禁用。幸運的是,您可以使用document.pictureInPictureEnabled來進行檢測。

if (!('pictureInPictureEnabled' in document)) {
  console.log('The Picture-in-Picture Web API is not available.');
}
else if (!document.pictureInPictureEnabled) {
  console.log('The Picture-in-Picture Web API is disabled.');
}           

應用于視訊的特定按鈕元素,這是您可能想要處理畫中畫按鈕可見性的方式。

if ('pictureInPictureEnabled' in document) {
  // Set button ability depending on whether Picture-in-Picture can be used.
  setPipButton();
  videoElement.addEventListener('loadedmetadata', setPipButton);
  videoElement.addEventListener('emptied', setPipButton);
} else {
  // Hide button if Picture-in-Picture is not supported.
  pipButtonElement.hidden = true;
}

function setPipButton() {
  pipButtonElement.disabled = (videoElement.readyState === 0) ||
                              !document.pictureInPictureEnabled ||
                              videoElement.disablePictureInPicture;
}           

你可以通過以下連結獲得Ddemo和代碼:

https://googlechrome.github.io/samples/picture-in-picture/

還有哪些新東西?

首先,檢視支援狀态頁面,了解目前在Chrome和其他浏覽器中API支援的情況。

以下是您在不久的将來可以看到的内容:

  • Chrome OS和Android O将支援畫中畫。
  • MediaDevices.getUserMedia()的MediaStreams将與Picture-in-Picture一起使用。
  • Web開發人員将能夠添加自定義Picture-in-Picture控件。

英文原文:

https://developers.google.com/web/updates/2018/10/watch-video-using-picture-in-picture

繼續閱讀