您現在可以使用 Screen Capture API,在網頁平台上分享分頁、視窗和螢幕畫面。當網頁應用程式呼叫 getDisplayMedia()
時,Chrome 會提示使用者將分頁、視窗或畫面以 MediaStreamTrack
影片的形式分享給網頁應用程式。
許多使用 getDisplayMedia()
的網路應用程式會向使用者顯示擷取畫面的影片預覽畫面。舉例來說,視訊會議應用程式通常會將這部影片串流傳送至遠端使用者,同時將影片轉譯至本機 HTMLVideoElement
,讓本機使用者持續看到分享內容的預覽畫面。
本文件說明 Chrome 中的新 Captured Surface Control API,可讓網頁應用程式捲動已擷取的分頁,以及讀取及寫入已擷取分頁的縮放等級。
為什麼要使用已擷取的表面控制項?
所有視訊會議應用程式都會遇到相同的缺點:如果使用者想要與擷取的程式碼分頁或視窗互動,就必須切換至該途徑,離開視訊會議應用程式。這會帶來一些挑戰:
- 除非使用者使用子母畫面功能,或將視訊會議分頁和共用分頁分開顯示,否則無法同時查看擷取的應用程式和遠端使用者的影片。在較小的螢幕上,這可能會很困難。
- 使用者必須在視訊會議應用程式和擷取畫面之間切換,這會造成負擔。
- 使用者離開視訊會議應用程式時,將無法存取該應用程式提供的控制項,例如內嵌的即時通訊應用程式、表情符號回應、要求加入通話的使用者通知、多媒體和版面配置控制項,以及其他實用的視訊會議功能。
- 主持人無法將控制權委派給遠端參與者。這會導致常見的情況:遠端使用者要求講者變更投影片、上下捲動畫面或調整縮放等級。
Captured Surface Control API 可解決這些問題。
如何使用已擷取的表面控制項?
要成功使用「Captured Surface Control」,您必須先完成幾個步驟,例如明確擷取瀏覽器分頁,並取得使用者的權限,才能捲動及縮放已擷取的分頁。
擷取瀏覽器分頁
首先,請使用 getDisplayMedia()
提示使用者選擇要分享的途徑,並在過程中將 CaptureController
物件與擷取工作階段建立關聯。我們很快就會使用該物件控制已擷取的表面。
const controller = new CaptureController();
const stream = await navigator.mediaDevices.getDisplayMedia({ controller });
接下來,請以 <video>
元素的形式產生擷取表面的本機預覽畫面:
const previewTile = document.querySelector('video');
previewTile.srcObject = stream;
如果使用者選擇分享視窗或螢幕畫面,目前不在範圍內,但如果他們選擇分享分頁,我們可以繼續進行。
const [track] = stream.getVideoTracks();
if (track.getSettings().displaySurface !== 'browser') {
// Bail out early if the user didn't pick a tab.
return;
}
權限提示
在指定 CaptureController
物件上首次呼叫 sendWheel()
或 setZoomLevel()
時,系統會顯示權限提示。如果使用者授予權限,系統就會允許在該 CaptureController
物件上進一步叫用這些方法。如果使用者拒絕權限,系統會拒絕傳回的承諾。
請注意,CaptureController
物件會與特定的擷取工作階段建立唯一關聯,無法與其他擷取工作階段建立關聯,且不會保留在定義該物件的頁面中。不過,擷取工作階段會在擷取的網頁導覽後保留。
您必須使用手勢,才能向使用者顯示權限提示。只有 sendWheel()
和 setZoomLevel()
呼叫需要使用者手勢,且只有在需要顯示提示時才需要。如果使用者在網頁應用程式中點選放大或縮小按鈕,系統會視為使用者動作;但如果應用程式想先提供捲動控制項,開發人員應記住,捲動動作「並非」使用者動作。其中一個方法是先向使用者提供「開始捲動」按鈕,如下例所示:
const startScrollingButton = document.querySelector('button');
startScrollingButton.addEventListener('click', async () => {
try {
const noOpWheelAction = {};
await controller.sendWheel(noOpWheelAction);
// The user approved the permission prompt.
// You can now scroll and zoom the captured tab as shown later in the article.
} catch (error) {
return; // Permission denied. Bail.
}
});
捲動
使用 sendWheel()
時,擷取應用程式可在分頁檢視區內,以所選座標提供所選大小的輪詢事件。事件無法讓擷取的應用程式區分直接使用者互動。
假設擷取應用程式採用名為 "previewTile"
的 <video>
元素,以下程式碼說明如何將傳送輪盤事件轉送至擷取的分頁:
const previewTile = document.querySelector('video');
previewTile.addEventListener('wheel', async (event) => {
// Translate the offsets into coordinates which sendWheel() can understand.
// The implementation of this translation is explained further below.
const [x, y] = translateCoordinates(event.offsetX, event.offsetY);
const [wheelDeltaX, wheelDeltaY] = [-event.deltaX, -event.deltaY];
try {
// Relay the user's action to the captured tab.
await controller.sendWheel({ x, y, wheelDeltaX, wheelDeltaY });
} catch (error) {
// Inspect the error.
// ...
}
});
sendWheel()
方法會使用含有兩組值的字典:
x
和y
:輪盤事件的座標。wheelDeltaX
和wheelDeltaY
:水平和垂直捲動的幅度,以像素為單位。請注意,這些值與原始輪盤事件相反。
translateCoordinates()
的可能實作方式如下:
function translateCoordinates(offsetX, offsetY) {
const previewDimensions = previewTile.getBoundingClientRect();
const trackSettings = previewTile.srcObject.getVideoTracks()[0].getSettings();
const x = trackSettings.width * offsetX / previewDimensions.width;
const y = trackSettings.height * offsetY / previewDimensions.height;
return [Math.floor(x), Math.floor(y)];
}
請注意,先前的程式碼中包含三種不同的大小:
<video>
元素的大小。- 擷取的畫面大小 (此處以
trackSettings.width
和trackSettings.height
表示)。 - 分頁大小。
<video>
元素的大小完全在擷取應用程式的網域中,瀏覽器無法得知。分頁大小完全在瀏覽器的網域中,網頁應用程式無法得知。
網路應用程式會使用 translateCoordinates()
,將相對於 <video>
元素的偏移量轉譯為影片軌道本身座標空間中的座標。瀏覽器也會在擷取的框架大小和分頁大小之間轉換,並以與網頁應用程式預期相符的偏移量傳送捲動事件。
sendWheel()
傳回的承諾可能會在下列情況下遭到拒絕:
- 如果擷取工作階段尚未開始或已停止,包括在瀏覽器處理
sendWheel()
動作時異步停止。 - 如果使用者未授予應用程式使用
sendWheel()
的權限。 - 如果擷取應用程式嘗試在
[trackSettings.width, trackSettings.height]
以外的座標中傳送捲動事件,請注意,這些值可能會非同步變更,因此建議您擷取錯誤並加以忽略。(請注意,0, 0
通常不會超出範圍,因此可以安全地使用這些元素,向使用者提示權限要求)。
縮放
您可以透過下列 CaptureController
途徑,與擷取的分頁的縮放等級互動:
getSupportedZoomLevels()
會傳回瀏覽器支援的縮放等級清單,以「預設縮放等級」的百分比表示,該等級的定義為 100%。這個清單是單調遞增的,且包含值 100。getZoomLevel()
會傳回分頁的目前縮放等級。setZoomLevel()
會將分頁的縮放等級設為getSupportedZoomLevels()
中的任何整數值,並在成功時傳回承諾。請注意,在擷取工作階段結束時,系統不會重設縮放等級。oncapturedzoomlevelchange
可讓您監聽已擷取的分頁縮放等級變更,因為使用者可能會透過擷取應用程式,或直接與已擷取的網頁互動來變更縮放等級。
對 setZoomLevel()
的呼叫會受到權限限制;對其他唯讀縮放方法的呼叫則是「免費」,事件監聽也是如此。
以下範例說明如何在現有擷取工作階段中,增加已擷取分頁的縮放等級:
const zoomIncreaseButton = document.getElementById('zoomInButton');
zoomIncreaseButton.addEventListener('click', async (event) => {
const levels = CaptureController.getSupportedZoomLevels();
const index = levels.indexOf(controller.getZoomLevel());
const newZoomLevel = levels[Math.min(index + 1, levels.length - 1)];
try {
await controller.setZoomLevel(newZoomLevel);
} catch (error) {
// Inspect the error.
// ...
}
});
以下範例說明如何回應擷取的分頁縮放等級變更:
controller.addEventListener('capturedzoomlevelchange', (event) => {
const zoomLevel = controller.getZoomLevel();
document.querySelector('#zoomLevelLabel').textContent = `${zoomLevel}%`;
});
特徵偵測
如要確認系統是否支援傳送輪盤事件,請使用:
if (!!window.CaptureController?.prototype.sendWheel) {
// CaptureController sendWheel() is supported.
}
如要檢查是否支援控制縮放功能,請使用:
if (!!window.CaptureController?.prototype.setZoomLevel) {
// CaptureController setZoomLevel() is supported.
}
啟用已擷取的表面控制項
Captured Surface Control API 可在 Chrome 桌面版的 Captured Surface Control 旗標後方使用,並可在 chrome://flags/#captured-surface-control
啟用。
這項功能也在 Chrome 122 版開始在電腦上進行來源測試,開發人員可以為網站訪客啟用這項功能,收集真實使用者的資料。如要進一步瞭解原始試播功能及其運作方式,請參閱「開始使用原始試播」。
安全性和隱私權
"captured-surface-control"
權限政策可讓您管理擷取應用程式和嵌入的第三方 iframe 如何存取「Captured Surface Control」。如要瞭解安全性取捨,請參閱「Captured Surface Control」說明中的「隱私權和安全性考量事項」一節。
示範
您可以在 Glitch 上執行示範,體驗 Captured Surface Control 的運作方式。請務必查看原始碼。
與先前 Chrome 版本的差異
以下是您應留意的幾個關於 Captured Surface Control 的重要行為差異:
- 在 Chrome 124 以下版本中:
- 如果授予權限,則範圍會限於與該
CaptureController
相關聯的擷取工作階段,而非擷取來源。
- 如果授予權限,則範圍會限於與該
- 在 Chrome 122 中:
getZoomLevel()
會傳回含有分頁目前縮放等級的承諾。- 如果使用者未授予應用程式使用權限,
sendWheel()
會傳回拒絕的承諾,並附上錯誤訊息"No permission."
。在 Chrome 123 以上版本中,錯誤類型為"NotAllowedError"
。 - 無法使用
oncapturedzoomlevelchange
,您可以使用setInterval()
為這項功能提供 polyfill。
意見回饋
Chrome 團隊和網路標準社群希望瞭解您使用 Captured Surface Control 的體驗。
請提供設計相關資訊
Captured Surface Capture 是否無法正常運作?或者,您是否缺少實作想法所需的方法或屬性?如對安全性模型有任何問題或意見,在 GitHub 存放區中提出規格問題,或在現有問題中加入您的想法。
導入時發生問題?
你是否發現 Chrome 實作項目有錯誤?或者實作方式與規格不同?請前往 https://new.crbug.com 提交錯誤。請務必提供盡可能多的詳細資訊,以及重現問題的操作說明。Glitch 非常適合用於分享可重現的錯誤。