Scorrimento e zoom di una scheda acquisita

François Beaufort
François Beaufort

La condivisione di schede, finestre e schermate è già possibile sulla piattaforma web con l'API Screen Capture. Quando un'app web chiama getDisplayMedia(), Chrome chiede all'utente di condividere una scheda, una finestra o uno schermo con l'app web sotto forma di video MediaStreamTrack.

Molte app web che utilizzano getDisplayMedia() mostrano all'utente un'anteprima video della piattaforma acquisita. Ad esempio, le app di videoconferenza spesso trasmettono questo video in streaming agli utenti remoti e al contempo lo visualizzano in un'istanza HTMLVideoElement locale, in modo che l'utente locale possa vedere costantemente un'anteprima di ciò che sta condividendo.

Questa documentazione presenta la nuova API Captured Surface Control in Chrome, che consente all'app web di scorrere una scheda acquisita e di leggere e scrivere il livello di zoom di una scheda acquisita.

Un utente scorre e ingrandisce una scheda acquisita (demo).

Perché usare il Controllo della superficie acquisita?

Tutte le app di videoconferenza presentano lo stesso svantaggio: se l'utente vuole interagire con una scheda o una finestra acquisita, deve passare a quella piattaforma, allontanandola dall'app. Ciò presenta alcune sfide:

  • L'utente non può vedere contemporaneamente l'app acquisita e i video degli utenti remoti, a meno che non utilizzino Picture in picture o finestre separate affiancate per la scheda della videoconferenza e la scheda condivisa. Su uno schermo più piccolo potrebbe essere difficile.
  • L'utente è gravato dalla necessità di passare dall'app per videoconferenze alla piattaforma acquisita.
  • L'utente non può più accedere ai controlli esposti all'app per videoconferenze. ad esempio, un'app di chat incorporata, reazioni con emoji, notifiche che invitano gli utenti a partecipare alla chiamata, controlli multimediali e di layout e altre utili funzionalità di videoconferenza.
  • Il presentatore non può delegare il controllo ai partecipanti da remoto. Questo conduce a uno scenario fin troppo familiare in cui gli utenti da remoto chiedono al presentatore di cambiare la slide, scorrere un po' su e giù o regolare il livello di zoom.

L'API Captured Surface Control risolve questi problemi.

Come si utilizza il Controllo della superficie acquisita?

L'utilizzo corretto di Captured Surface Control richiede alcuni passaggi, ad esempio l'acquisizione esplicita di una scheda del browser e l'ottenimento dell'autorizzazione dall'utente prima di poter scorrere e ingrandire la scheda acquisita.

Acquisisci una scheda del browser

Per iniziare, chiedi all'utente di scegliere una piattaforma da condividere utilizzando getDisplayMedia(), dopodiché associa un oggetto CaptureController alla sessione di acquisizione. Presto utilizzeremo quell'oggetto per controllare la superficie acquisita.

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

Quindi, crea un'anteprima locale della piattaforma acquisita sotto forma di elemento <video>:

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

Se l'utente sceglie di condividere una finestra o una schermata, per il momento non rientra nell'ambito dell'applicazione, ma se sceglie di condividere una scheda, possiamo procedere.

const [track] = stream.getVideoTracks();

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

Richiesta di autorizzazione

La prima chiamata di sendWheel() o setZoomLevel() su un determinato oggetto CaptureController produce un prompt di autorizzazione. Se l'utente concede l'autorizzazione, sono consentite ulteriori chiamate di questi metodi sull'oggetto CaptureController in questione. Se l'utente nega l'autorizzazione, la promessa restituita viene rifiutata.

Tieni presente che gli oggetti CaptureController sono associati in modo univoco a una specifica capture-session, non possono essere associati a un'altra sessione di acquisizione e non sopravvivono alla navigazione della pagina in cui sono definiti. Tuttavia, le sessioni di acquisizione rimangono durante la navigazione nella pagina acquisita.

È necessario un gesto dell'utente per mostrare una richiesta di autorizzazione all'utente. Solo le chiamate sendWheel() e setZoomLevel() richiedono un gesto dell'utente e solo se è necessario mostrare la richiesta. Se l'utente fa clic su un pulsante di aumento o riduzione dello zoom nell'app web, quel gesto dell'utente viene considerato un dato; Tuttavia, se l'app intende prima offrire il controllo dello scorrimento, gli sviluppatori devono tenere presente che lo scorrimento non costituisce un gesto dell'utente. Una possibilità è offrire innanzitutto all'utente di "iniziare a scorrere i contenuti" come mostrato nell'esempio seguente:

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.
  }
});

Scorri

Con sendWheel(), un'app di acquisizione può pubblicare eventi ruota della grandezza scelta rispetto alle coordinate di sua scelta all'interno dell'area visibile di una scheda. L'evento non è distinguibile dall'app acquisita dall'interazione diretta dell'utente.

Presumendo che l'app di acquisizione utilizzi un elemento <video> denominato "previewTile", il seguente codice mostra come inoltrare gli eventi wheel invia gli eventi alla scheda acquisita:

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.
    // ...
  }
});

Il metodo sendWheel() utilizza un dizionario con due insiemi di valori:

  • x e y: le coordinate in cui deve essere erogato l'evento ruota.
  • wheelDeltaX e wheelDeltaY: la grandezza degli scorrimenti, in pixel, per gli scorrimenti orizzontali e verticali, rispettivamente. Tieni presente che questi valori vengono invertiti rispetto all'evento wheel originale.

Una possibile implementazione di 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)];
}

Tieni presente che nel codice vengono utilizzate tre dimensioni diverse:

  • Le dimensioni dell'elemento <video>.
  • La dimensione dei frame acquisiti (qui rappresentati come trackSettings.width e trackSettings.height).
  • Le dimensioni della scheda.

Le dimensioni dell'elemento <video> rientrano completamente nel dominio dell'app di acquisizione e sono sconosciute al browser. Le dimensioni della scheda rientrano completamente nel dominio del browser e sono sconosciute all'app web.

L'app web utilizza translateCoordinates() per tradurre gli offset relativi all'elemento <video> in coordinate all'interno dello spazio delle coordinate della traccia video. Allo stesso modo, il browser tradurrà le dimensioni dei frame acquisiti e le dimensioni della scheda e pubblicherà l'evento di scorrimento con un offset corrispondente alle aspettative dell'app web.

La promessa restituita da sendWheel() può essere rifiutata nei seguenti casi:

  • Se la sessione di acquisizione non è ancora iniziata o è già stata interrotta, incluso l'interruzione in modo asincrono mentre l'azione sendWheel() viene gestita dal browser.
  • Se l'utente non ha concesso all'app l'autorizzazione a usare sendWheel().
  • Se l'app di acquisizione tenta di inviare un evento di scorrimento in coordinate esterne a [trackSettings.width, trackSettings.height]. Tieni presente che questi valori potrebbero cambiare in modo asincrono, perciò è buona norma individuare l'errore e ignorarlo. Tieni presente che in genere 0, 0 non rientra nei limiti, quindi puoi utilizzarlo in sicurezza per richiedere l'autorizzazione all'utente.

Zoom

L'interazione con il livello di zoom della scheda acquisita viene eseguita tramite le seguenti piattaforme CaptureController:

  • getSupportedZoomLevels() restituisce un elenco di livelli di zoom supportati dal browser, rappresentati come percentuali del "livello di zoom predefinito", definito come 100%. Questo elenco è in aumento monotonico e contiene il valore 100.
  • getZoomLevel() restituisce il livello di zoom corrente della scheda.
  • setZoomLevel() imposta il livello di zoom della scheda su un qualsiasi valore intero presente in getSupportedZoomLevels() e restituisce una promessa quando riesce. Tieni presente che il livello di zoom non viene reimpostato al termine della sessione di acquisizione.
  • oncapturedzoomlevelchange ti consente di ascoltare i cambiamenti del livello di zoom di una scheda acquisita, poiché gli utenti possono cambiarlo tramite l'app di acquisizione o tramite l'interazione diretta con la scheda acquisita.

Le chiamate a setZoomLevel() sono controllate da autorizzazione; le chiamate all'altro metodi di zoom di sola lettura sono "senza costi", così come l'ascolto di eventi.

L'esempio seguente mostra come aumentare il livello di zoom di una scheda acquisita in una sessione di acquisizione esistente:

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.
    // ...
  }
});

L'esempio seguente mostra come reagire alle modifiche del livello di zoom di una scheda acquisita:

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

Rilevamento delle caratteristiche

Per verificare se l'invio di eventi wheel è supportato, utilizza:

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

Per verificare se il controllo dello zoom è supportato, utilizza:

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

Attiva controllo della superficie acquisita

L'API Captured Surface Control è disponibile in Chrome su computer desktop dietro il flag Captured Surface Control e può essere attivata all'indirizzo chrome://flags/#captured-surface-control.

Questa funzionalità sta anche permettendo di entrare in una prova dell'origine a partire da Chrome 122 su computer, che consente agli sviluppatori di abilitare la funzionalità per consentire ai visitatori dei loro siti di raccogliere dati da utenti reali. Consulta la Guida introduttiva alle prove dell'origine per saperne di più sulle prove dell'origine e su come funzionano.

Sicurezza e privacy

Le norme di autorizzazione di "captured-surface-control" ti consentono di gestire il modo in cui l'app di acquisizione e gli iframe di terze parti incorporati hanno accesso al controllo della superficie acquisita. Per comprendere i compromessi in termini di sicurezza, consulta la sezione Considerazioni su privacy e sicurezza della spiegazione del controllo della superficie acquisita.

Demo

Puoi giocare con Controllo della superficie acquisita eseguendo la demo su Glitch. Assicurati di controllare il codice sorgente.

Modifiche rispetto alle versioni precedenti di Chrome

Di seguito sono riportate alcune differenze comportamentali chiave relative al controllo della superficie acquisita di cui dovresti essere a conoscenza:

  • In Chrome 124 e versioni precedenti:
    • L'autorizzazione, se concessa, ha come ambito la sessione di acquisizione associata a questo CaptureController, non l'origine di acquisizione.
  • In Chrome 122:
    • getZoomLevel() restituisce una promessa con il livello di zoom corrente della scheda.
    • sendWheel() restituisce una promessa rifiutata con il messaggio di errore "No permission." se l'utente non ha concesso all'app l'autorizzazione all'uso. Il tipo di errore è "NotAllowedError" in Chrome 123 e versioni successive.
    • oncapturedzoomlevelchange non è disponibile. Puoi eseguire il polyfill di questa funzionalità utilizzando setInterval().

Feedback

Il team di Chrome e la community degli standard web vogliono sapere cosa ne pensi della tua esperienza con Captured Surface Control.

Parlaci del design

C'è qualcosa in Captured Surface Capture che non funziona come ti aspettavi? Oppure mancano metodi o proprietà che ti servono per implementare la tua idea? Hai una domanda o un commento sul modello di sicurezza? Segnala un problema relativo alle specifiche sul repository GitHub o aggiungi le tue opinioni a un problema esistente.

Problemi con l'implementazione?

Hai trovato un bug nell'implementazione di Chrome? Oppure l'implementazione è diversa dalle specifiche? Segnala un bug all'indirizzo https://new.crbug.com. Assicurati di includere il maggior numero di dettagli possibile, oltre alle istruzioni per la riproduzione. Glitch è ottimo per la condivisione di bug riproducibili.