通过条件聚焦功能更好地共享屏幕

弗朗索瓦·博福
François Beaufort
伊拉德·阿隆
Elad Alon

浏览器支持

  • 109
  • 109
  • x
  • x

来源

借助 Screen Capture API,用户可以选择要截取为媒体流的标签页、窗口或屏幕。然后,您便可录制此直播或通过网络与他人分享。本文档介绍了条件聚焦,这是一种 Web 应用机制,用于控制捕获的标签页或窗口在拍摄开始时是否聚焦,或者捕获的页面是否保持聚焦状态。

浏览器支持

Chrome 109 及更高版本提供条件聚焦功能。

背景

当 Web 应用开始捕获标签页或窗口时,浏览器需要作出决定:是将捕获的界面置于最前面,还是应让捕获的页面保持聚焦状态?答案取决于调用 getDisplayMedia() 的原因,以及用户最终选择的 Surface。

让我们以一个假设的视频会议 Web 应用为例。通过读取 track.getSettings().displaySurface 并可能检查捕获标识名,视频会议 Web 应用可以了解用户选择分享的内容。然后,执行以下操作:

  • 如果拍摄的标签页或窗口可以远程控制,请始终聚焦于视频会议。
  • 否则,请将焦点置于捕获的标签页或窗口。

在上面的示例中,如果共享幻灯片组,视频会议 Web 应用将保持焦点,允许用户远程浏览幻灯片;但如果用户选择共享文本编辑器,视频会议 Web 应用会立即将焦点切换到捕获的标签页或窗口。

使用 Conditional Focus API

实例化 CaptureController 并将其传递给 getDisplayMedia()。通过在 getDiplayMedia() 返回的 promise 解析后立即调用 setFocusBehavior(),您可以控制捕获的标签页或窗口是否聚焦。只有在用户共享标签页或窗口时才能执行此操作。

const controller = new CaptureController();

// Prompt the user to share a tab, a window or a screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

const [track] = stream.getVideoTracks();
const displaySurface = track.getSettings().displaySurface;
if (displaySurface == "browser") {
  // Focus the captured tab.
  controller.setFocusBehavior("focus-captured-surface");
} else if (displaySurface == "window") {
  // Do not move focus to the captured window.
  // Keep the capturing page focused.
  controller.setFocusBehavior("focus-capturing-application");
}

在决定是否对焦时,可以考虑拍摄手柄

// Retain focus if capturing a tab dialed to example.com.
// Focus anything else.
const origin = track.getCaptureHandle().origin;
if (displaySurface == "browser" && origin == "https://example.com") {
  controller.setFocusBehavior("focus-capturing-application");
} else if (displaySurface != "monitor") {
  controller.setFocusBehavior("focus-captured-surface");
}

您甚至可以在调用 getDisplayMedia() 之前决定是否聚焦。

// Focus the captured tab or window when capture starts.
const controller = new CaptureController();
controller.setFocusBehavior("focus-captured-surface");

// Prompt the user to share their screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

您可以在 promise 解析之前任意多次调用 setFocusBehavior(),也可以在 promise 解析后立即调用最多一次。最后一次调用会覆盖之前的所有调用。

更准确地说: - 返回的 getDisplayMedia() promise 通过微任务进行解析。在该微任务完成后调用 setFocusBehavior() 会抛出错误。- 在拍摄开始超过 1 秒后调用 setFocusBehavior() 是空操作。

也就是说,以下两个代码段都将失败:

// Prompt the user to share their screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

// Too late, because it follows the completion of the task
// on which the getDisplayMedia() promise resolved.
// This will throw.
setTimeout(() => {
  controller.setFocusBehavior("focus-captured-surface");
});
// Prompt the user to share their screen.
const stream =
    await navigator.mediaDevices.getDisplayMedia({ controller });

const start = new Date();
while (new Date() - start <= 1000) {
  // Idle for ≈1s.
}

// Because too much time has elapsed, the browser will have
// already decided whether to focus.
// This fails silently.
controller.setFocusBehavior("focus-captured-surface");

在以下情况下,调用 setFocusBehavior() 也会抛出:

  • getDisplayMedia() 返回的视频流的视频轨道不是 "live"
  • getDisplayMedia() 返回的 promise 解析之后,如果用户共享了一个屏幕(不是标签页或窗口)。

示例

您可以通过在 Glitch 上运行演示来体验条件焦点。请务必查看源代码

功能检测

如需检查 CaptureController.setFocusBehavior() 是否受支持,请使用以下命令:

if (
  "CaptureController" in window &&
  "setFocusBehavior" in CaptureController.prototype
) {
  // CaptureController.setFocusBehavior() is supported.
}

反馈

Chrome 团队和网络标准社区希望了解您使用条件聚焦的体验。

请告诉我们设计情况

条件焦点有没有什么功能无法按预期运行?或者是否缺少一些您需要的方法或属性来实现您的想法?对安全模型有疑问或意见?

  • GitHub 代码库上提交规范问题,或将您的想法添加到现有问题中。

实施时遇到了问题?

您是否发现了 Chrome 实现方面的错误?或者,实现方式是否不同于规范?

  • 请访问 https://new.crbug.com 提交 bug。请务必提供尽可能详细的信息,和简单的重现说明。Glitch 非常适合共享代码。

显示支持

您是否打算使用条件聚焦?您公开提供的支持可帮助 Chrome 团队确定各项功能的优先级,并向其他浏览器供应商说明支持这些功能的重要性。

请向 @ChromiumDev 发送 Twitter 微博,告诉我们您在哪里以及如何使用它。

致谢

主打图片:Elena Taranenko

感谢 Rachel Andrew 对本文进行审核。