透過 Screen Capture API,網路平台已支援分享分頁、視窗和畫面。當網頁應用程式呼叫 getDisplayMedia()
時,Chrome 會提示使用者以 MediaStreamTrack
影片的形式,將分頁、視窗或螢幕畫面與網頁應用程式分享。
許多使用 getDisplayMedia()
的網頁應用程式都會向使用者顯示所擷取介面的影片預覽畫面,舉例來說,視訊會議應用程式通常會向遠端使用者串流播放這部影片,同時發布到本機 HTMLVideoElement
,讓本機使用者持續預覽自己分享的內容。
本文件介紹 Chrome 新推出的 Captured Surface Control API,這個 API 可讓網頁應用程式捲動擷取的分頁,以及讀取及寫入已擷取分頁的縮放等級。
為什麼要使用擷取表面控制項?
所有視訊會議應用程式都面臨同樣的缺點:如果使用者想使用擷取的分頁或視窗,就必須切換至該介面,而不使用視訊會議應用程式。這種情況下會遇到以下問題:
- 使用者無法同時觀看拍攝的應用程式和影片,除非他們使用子母畫面或視訊會議分頁和共用分頁並排顯示視窗。在較小的螢幕上,這可能非常困難。
- 使用者不費吹灰之力,就能在視訊會議應用程式和擷取的介面之間進行切換。
- 不在視訊會議期間,使用者無法再存取視訊會議應用程式提供的控制選項,例如嵌入式即時通訊應用程式、表情符號回應、要求加入通話的通知、多媒體和版面配置控制項,以及其他實用的視訊會議功能。
- 簡報者無法將控制權委派給遠端參與者。如此一來,遠端使用者就會要求簡報者變更投影片、上下捲動畫面,或是調整縮放等級。
Captured Surface Control API 已解決這些問題。
如何使用擷取表面控制項?
如要成功使用擷取的介面控制項,您必須完成幾個步驟,例如明確擷取瀏覽器分頁並取得使用者授權,才能捲動及縮放已擷取的分頁。
擷取瀏覽器分頁
首先,請提示使用者利用 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
通常不會超過邊界,因此可以放心透過此方式提示使用者授予權限)。
Zoom
系統會透過下列 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 的存取權。如要瞭解安全性取捨,請參閱「擷取途徑控制」說明的「隱私權和安全性注意事項」一節。
操作示範
你可以在 Glitch 上執行示範,藉此使用 Captured Surface Control 進行遊戲。請務必查看原始碼。
舊版 Chrome 的變更內容
以下是有關擷取途徑控制項的一些重要行為差異,請注意:
- 在 Chrome 124 以下版本中:
- 權限 (如已授予權限) 將範圍限定為與
CaptureController
相關聯的擷取工作階段,而非擷取來源。
- 權限 (如已授予權限) 將範圍限定為與
- 在 Chrome 122 中:
getZoomLevel()
會傳回 承諾,以及分頁目前的縮放等級。- 如果使用者未授予應用程式使用權限,
sendWheel()
會傳回 promise,並顯示錯誤訊息"No permission."
。Chrome 123 以上版本中的錯誤類型為"NotAllowedError"
。 - 「
oncapturedzoomlevelchange
」無法使用。您可以使用setInterval()
補充這項功能。
意見回饋:
Chrome 團隊和網路標準社群想瞭解 Captured Surface Control 的體驗。
請向我們介紹設計
拍攝的途徑擷取功能有沒有正常運作的問題?或者您需要實現想法的方法或屬性嗎?如果您對安全性模型有任何疑問或意見,前往 GitHub 存放區提交規格問題,或是新增想法至現有的問題。
實作時遇到問題嗎?
您在執行 Chrome 時發現錯誤了嗎?或者實作與規格不同?前往 https://new.crbug.com 回報錯誤。請盡可能提供詳細資訊及重現指示。Glitch 適合用來分享可重現的錯誤,