API захвата экрана позволяет пользователю выбрать вкладку, окно или экран для захвата в виде медиапотока. Этот поток затем можно записать или поделиться им с другими пользователями по сети. В этой документации рассматривается условный фокус — механизм, позволяющий веб-приложениям управлять тем, будет ли фокусироваться захваченная вкладка или окно при начале захвата, или же фокус останется на странице захвата.
Поддержка браузеров
Условный фокус доступен в Chrome 109.
Фон
Когда веб-приложение начинает захват вкладки или окна, браузеру приходится решать: вывести захваченную поверхность на передний план или оставить в фокусе страницу, на которой был сделан захват? Ответ зависит от причины вызова getDisplayMedia()
и от того, какую поверхность в итоге выберет пользователь.
Рассмотрим гипотетическое веб-приложение для видеоконференций. Считывая track.getSettings().displaySurface
и потенциально проверяя Capture Handle , веб-приложение для видеоконференций может понять, что именно решил показать пользователь. Затем:
- Если захваченной вкладкой или окном можно управлять удаленно, держите видеоконференцию в фокусе.
- В противном случае сфокусируйтесь на захваченной вкладке или окне.
В приведенном выше примере веб-приложение для видеоконференций сохранит фокус при совместном использовании набора слайдов, позволяя пользователю удаленно пролистывать слайды; но если пользователь решит предоставить общий доступ к текстовому редактору, веб-приложение для видеоконференций немедленно переключит фокус на захваченную вкладку или окно.
Использование API условного фокуса
Создайте экземпляр CaptureController
и передайте его в getDisplayMedia()
. Вызывая setFocusBehavior()
сразу после завершения возвращаемого промиса getDiplayMedia()
, вы можете управлять фокусировкой захваченной вкладки или окна. Это возможно только в том случае, если пользователь предоставил общий доступ к вкладке или окну.
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 });
Вы можете вызывать setFocusBehavior()
произвольное количество раз до завершения обещания или не более одного раза сразу после его завершения. Последний вызов переопределяет все предыдущие.
Точнее: - Промис, возвращаемый getDisplayMedia()
разрешается на микрозадаче. Вызов setFocusBehavior()
после завершения этой микрозадачи приводит к ошибке. - Вызов 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()
не является «живой» . - после того, как
getDisplayMedia()
вернет обещание, если пользователь открыл общий экран (а не вкладку или окно).
Образец
Вы можете поэкспериментировать с условным фокусом, запустив демо-версию .
Обнаружение особенностей
Чтобы проверить, поддерживается ли CaptureController.setFocusBehavior()
, используйте:
if (
"CaptureController" in window &&
"setFocusBehavior" in CaptureController.prototype
) {
// CaptureController.setFocusBehavior() is supported.
}
Обратная связь
Команда Chrome и сообщество веб-стандартов хотят узнать о вашем опыте использования Conditional Focus.
Расскажите нам о дизайне
Есть ли что-то в условном фокусе, что работает не так, как вы ожидали? Или не хватает методов или свойств, необходимых для реализации вашей идеи? Есть вопросы или комментарии по модели безопасности?
- Отправьте сообщение о проблеме со спецификацией в репозиторий GitHub или добавьте свои мысли к существующей проблеме.
Проблема с реализацией?
Вы обнаружили ошибку в реализации Chrome? Или реализация отличается от спецификации?
- Сообщите об ошибке по адресу https://new.crbug.com . Опишите её как можно подробнее и предоставьте простые инструкции по её воспроизведению.
Выразить поддержку
Планируете ли вы использовать условный фокус? Ваша публичная поддержка помогает команде Chrome расставлять приоритеты в отношении функций и показывает другим разработчикам браузеров, насколько важна их поддержка.
Отправьте твит @ChromiumDev и расскажите, где и как вы его используете.
Полезные ссылки
Благодарности
Автор изображения — Елена Тараненко .
Спасибо Рэйчел Эндрю за рецензирование этой статьи.