Rolar e aplicar zoom em uma guia capturada

François Beaufort
François Beaufort

Já é possível compartilhar guias, janelas e telas na plataforma da Web com a API Screen Capture. Quando um app da Web chama getDisplayMedia(), o Chrome solicita que o usuário compartilhe uma guia, janela ou tela com o app da Web como um vídeo MediaStreamTrack.

Muitos apps da Web que usam getDisplayMedia() mostram ao usuário uma prévia em vídeo da superfície capturada. Por exemplo, apps de videoconferência geralmente transmitem esse vídeo para usuários remotos e também o renderizam para um HTMLVideoElement local, para que o usuário local tenha uma prévia constante do que está sendo compartilhado.

Esta documentação apresenta a nova API Captured Surface Control no Chrome, que permite que seu app da Web role uma guia capturada e leia e grave o nível de zoom de uma guia capturada.

Um usuário rola e aumenta o zoom de uma guia capturada (demonstração).

Por que usar o controle de superfície capturada?

Todos os apps de videoconferência têm a mesma desvantagem. Se o usuário quiser interagir com uma guia ou janela capturada, ele terá que mudar para essa plataforma, saindo do app de videoconferência. Isso apresenta alguns desafios:

  • O usuário não pode ver o app capturado e os feeds de vídeo dos usuários remotos ao mesmo tempo, a menos que use o recurso Picture-in-Picture ou separe janelas lado a lado para a guia de videoconferência e a guia compartilhada. Em uma tela menor, isso pode ser difícil.
  • O usuário é sobrecarregado pela necessidade de alternar entre o app de videoconferência e a superfície capturada.
  • O usuário perde o acesso aos controles expostos pelo app de videoconferência enquanto está ausente dele, por exemplo, um app de chat incorporado, reações com emojis, notificações sobre usuários que pedem para entrar na chamada, controles multimídia e de layout e outros recursos úteis de videoconferência.
  • O apresentador não pode delegar o controle a participantes remotos. Isso leva ao cenário muito conhecido em que os usuários remotos pedem ao apresentador para mudar o slide, rolar um pouco para cima e para baixo ou ajustar o nível de zoom.

A API Captured Surface Control resolve esses problemas.

Como usar o controle de superfície capturada?

O uso do controle de superfície capturada exige algumas etapas, como capturar explicitamente uma guia do navegador e receber permissão do usuário antes de rolar e aplicar zoom na guia capturada.

Capturar uma guia do navegador

Comece pedindo ao usuário para escolher uma superfície para compartilhar usando getDisplayMedia() e, no processo, associe um objeto CaptureController à sessão de captura. Em breve, vamos usar esse objeto para controlar a superfície capturada.

const controller = new CaptureController();
const stream = await navigator.mediaDevices.getDisplayMedia({ controller });

Em seguida, produza uma visualização local da superfície capturada na forma de um elemento <video>:

const previewTile = document.querySelector('video');
previewTile.srcObject = stream;

Se o usuário escolher compartilhar uma janela ou tela, isso não será possível por enquanto. Mas se ele escolher compartilhar uma guia, poderemos continuar.

const [track] = stream.getVideoTracks();

if (track.getSettings().displaySurface !== 'browser') {
  // Bail out early if the user didn't pick a tab.
  return;
}

Solicitação de permissão

A primeira invocação de forwardWheel(), increaseZoomLevel(), decreaseZoomLevel() ou resetZoomLevel() em um determinado objeto CaptureController gera uma solicitação de permissão. Se o usuário conceder permissão, outras invocações desses métodos serão permitidas.

Um gesto do usuário é necessário para mostrar uma solicitação de permissão ao usuário. Portanto, o app só pode chamar os métodos acima se já tiver a permissão ou em resposta a um gesto do usuário, como um click em um botão relevante no app da Web.

Rolagem

Usando forwardWheel(), um app de captura pode encaminhar eventos da roda de um elemento de origem no próprio app de captura para a viewport da guia capturada. Esses eventos não podem ser distinguidos do app capturado da interação direta do usuário.

Supondo que o app de captura use um elemento <video> chamado "previewTile", o código a seguir mostra como encaminhar eventos de roda para a guia capturada:

const previewTile = document.querySelector('video');
try {
  // Relay the user's action to the captured tab.
  await controller.forwardWheel(previewTile);
} catch (error) {
  // Inspect the error.
  // ...
}

O método forwardWheel() recebe uma única entrada, que pode ser uma das seguintes:

  • Um elemento HTML de onde os eventos da roda serão encaminhados para a guia capturada.
  • null, indicando que o encaminhamento precisa ser interrompido.

Uma chamada bem-sucedida para forwardWheel() substitui as chamadas anteriores.

A promessa retornada por forwardWheel() pode ser rejeitada nos seguintes casos:

  • Se a sessão de captura ainda não começou ou já foi interrompida.
  • Se o usuário não concedeu a permissão relevante.

Zoom

A interação com o nível de zoom da guia capturada é feita pelas seguintes plataformas da API CaptureController:

getSupportedZoomLevels()

Esse método retorna uma lista de níveis de zoom aceitos pelo navegador para o tipo de superfície que está sendo capturado. Os valores na lista são representados como uma porcentagem em relação ao "nível de zoom padrão", que é definido como 100%. A lista aumenta monotonicamente e contém o valor 100.

Esse método só pode ser chamado para tipos de superfície de exibição com suporte, o que, no momento, significa apenas para guias.

controller.getSupportedZoomLevels() pode ser chamado se as seguintes condições forem atendidas:

  • controller está associado a uma captura ativa.
  • A captura é de uma guia.

Caso contrário, um erro será gerado.

A permissão "captured-surface-control" não é necessária para chamar esse método.

zoomLevel

Esse atributo somente leitura contém o nível de zoom atual da guia capturada. Ele é um atributo anulável e armazena null se o tipo de superfície capturada não tiver uma definição significativa do nível de zoom. No momento, o nível de zoom é definido apenas para guias, não para janelas ou telas.

Depois que a captura terminar, o atributo vai manter o último valor do nível de zoom.

A permissão "captured-surface-control" não é necessária para ler esse atributo.

onzoomlevelchange

Esse manipulador de eventos facilita a detecção de mudanças no nível de zoom da guia capturada. Isso acontece:

  • Quando o usuário interage com o navegador para mudar manualmente o nível de zoom da guia capturada.
  • Em resposta às chamadas do app de captura para os métodos de configuração de zoom (descritos abaixo).

A permissão "captured-surface-control" não é necessária para ler esse atributo.

increaseZoomLevel(), decreaseZoomLevel() e resetZoomLevel()

Esses métodos permitem a manipulação do nível de zoom da guia capturada.

increaseZoomLevel() e decreaseZoomLevel() mudam o nível de zoom para o próximo ou anterior, respectivamente, de acordo com a ordem retornada por getSupportedZoomLevels(). resetZoomLevel() define o valor como 100.

A permissão "captured-surface-control" é necessária para chamar esses métodos. Se o app de captura não tiver essa permissão, o usuário vai receber uma solicitação para conceder ou negar.

Todos esses métodos retornam uma promessa que é resolvida se a chamada for bem-sucedida e recusada. Estas são as possíveis causas de rejeição:

  • Permissão ausente.
  • Chamada antes do início da captura.
  • Chamada após o término da captura.
  • Chamada em um controller associado a uma captura de um tipo de superfície de exibição não compatível. Ou seja, qualquer coisa, exceto captura de guias.
  • Tenta aumentar ou diminuir além do valor máximo ou mínimo, respectivamente.

É recomendável evitar chamar decreaseZoomLevel() se controller.zoomLevel == controller.getSupportedZoomLevels().at(0) e proteger chamadas para increaseZoomLevel() de maneira semelhante com .at(-1).

O exemplo a seguir mostra como permitir que o usuário aumente o nível de zoom de uma guia capturada diretamente no app de captura:

const zoomIncreaseButton = document.getElementById('zoomInButton');
zoomIncreaseButton.addEventListener('click', async (event) => {
  if (controller.zoomLevel >= controller.getSupportedZoomLevels().at(-1)) {
    return;
  }
  try {
    await controller.increaseZoomLevel();
  } catch (error) {
    // Inspect the error.
    // ...
  }
});

O exemplo a seguir mostra como reagir às mudanças de nível de zoom de uma guia capturada:

controller.addEventListener('zoomlevelchange', (event) => {
  const zoomLevelLabel = document.querySelector('#zoomLevelLabel');
  zoomLevelLabel.textContent = `${controller.zoomLevel}%`;
});

Detecção de recursos

Para verificar se as APIs Captured Surface Control são compatíveis, use:

if (!!window.CaptureController?.prototype.forwardWheel) {
  // CaptureController forwardWheel() is supported.
}

Também é possível usar qualquer uma das outras plataformas da API Captured Surface Control, como increaseZoomLevel ou decreaseZoomLevel, ou até mesmo verificar todas elas.

Suporte ao navegador

O controle de superfície capturado está disponível apenas no Chrome 136 para computadores.

Segurança e privacidade

A política de permissões do "captured-surface-control" permite gerenciar como o app de captura e os iframes incorporados de terceiros têm acesso ao controle de superfície capturada. Para entender as compensações de segurança, consulte a seção Considerações sobre privacidade e segurança do texto explicativo sobre o controle de superfície capturada.

Demonstração

Você pode testar o controle de superfície capturada executando a demonstração no Glitch. Confira o código-fonte.

Feedback

A equipe do Chrome e a comunidade de padrões da Web querem saber sobre sua experiência com o controle de superfície capturada.

Fale sobre o design

Há algo sobre a captura de superfície que não funciona como você esperava? Ou há métodos ou propriedades ausentes que você precisa para implementar sua ideia? Tem alguma dúvida ou comentário sobre o modelo de segurança? Envie um problema de especificação no repositório do GitHub ou adicione sua opinião a um problema existente.

Problemas com a implementação?

Você encontrou um bug na implementação do Chrome? Ou a implementação é diferente da especificação? Registre um bug em https://new.crbug.com. Inclua o máximo de detalhes possível, além de instruções para reproduzir o problema. O Glitch é ótimo para compartilhar bugs reproduzíveis.