API захвата экрана позволяет пользователю выбрать вкладку, окно или экран для захвата в виде медиапотока. Этот поток затем можно записать или передать другим пользователям по сети. В этой документации представлен механизм условного фокуса (Conditional Focus), позволяющий веб-приложениям управлять тем, будет ли захваченная вкладка или окно находиться в фокусе при начале захвата, или же фокус будет сохраняться на странице, осуществляющей захват.
Поддержка браузеров
Функция «Условный фокус» доступна начиная с 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 и расскажите, где и как вы его используете.
Полезные ссылки
Благодарности
Благодарим Рэйчел Эндрю за проверку этого документа.