Blader en zoom op een vastgelegd tabblad.Scroll en zoom in op een vastgelegd tabblad

François Beaufort
François Beaufort

Het delen van tabbladen, vensters en schermen is al mogelijk op het webplatform met de Screen Capture API . Wanneer een webapp getDisplayMedia() aanroept, vraagt ​​Chrome de gebruiker om een ​​tabblad, venster of scherm met de webapp te delen als een MediaStreamTrack video.

Veel webapps die getDisplayMedia() gebruiken, tonen de gebruiker een videovoorbeeld van het vastgelegde oppervlak. Apps voor videoconferenties streamen deze video bijvoorbeeld vaak naar externe gebruikers en renderen deze ook naar een lokaal HTMLVideoElement , zodat de lokale gebruiker voortdurend een voorbeeld ziet van wat hij deelt.

Deze documentatie introduceert de nieuwe Captured Surface Control API in Chrome, waarmee uw webapp door een vastgelegd tabblad kan scrollen en het zoomniveau van een vastgelegd tabblad kan lezen en schrijven.

Een gebruiker scrollt en zoomt in op een vastgelegd tabblad ( demo ).

Waarom Captured Surface Control gebruiken?

Alle apps voor videoconferenties hebben hetzelfde nadeel: als de gebruiker wil communiceren met een vastgelegd tabblad of venster, moet de gebruiker naar dat oppervlak overschakelen en deze weghalen van de app voor videoconferenties. Dit brengt enkele uitdagingen met zich mee:

  • De gebruiker kan de vastgelegde app en de video's van externe gebruikers niet tegelijkertijd zien, tenzij ze Picture-in-Picture gebruiken of aparte zij-aan-zij vensters voor het videoconferentietabblad en het gedeelde tabblad. Op een kleiner scherm kan dit lastig zijn.
  • De gebruiker wordt belast door de noodzaak om tussen de videoconferentie-app en het vastgelegde oppervlak te springen.
  • De gebruiker verliest de toegang tot de bedieningselementen die door de app voor videoconferenties worden weergegeven terwijl hij er niet is; bijvoorbeeld een ingebouwde chat-app, emoji-reacties, meldingen over gebruikers die vragen om deel te nemen aan het gesprek, multimedia- en lay-outbedieningen en andere handige functies voor videoconferenties.
  • De presentator kan de controle niet delegeren aan deelnemers op afstand. Dit leidt tot het maar al te bekende scenario waarin externe gebruikers de presentator vragen om de dia te veranderen, een beetje op en neer te scrollen of het zoomniveau aan te passen.

De Captured Surface Control API lost deze problemen op.

Hoe gebruik ik Captured Surface Control?

Om Captured Surface Control succesvol te kunnen gebruiken, zijn een paar stappen nodig, zoals het expliciet vastleggen van een browsertabblad en het verkrijgen van toestemming van de gebruiker voordat hij kan scrollen en inzoomen op het vastgelegde tabblad.

Leg een browsertabblad vast

Begin door de gebruiker te vragen een oppervlak te kiezen om te delen met behulp van getDisplayMedia() en koppel daarbij een CaptureController object aan de opnamesessie. We zullen dat object snel genoeg gebruiken om het veroverde oppervlak te controleren.

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

Maak vervolgens een lokaal voorbeeld van het vastgelegde oppervlak in de vorm van een <video> -element:

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

Als de gebruiker ervoor kiest een venster of scherm te delen, valt dat voorlopig buiten het bereik, maar als hij ervoor kiest een tabblad te delen, kunnen we doorgaan.

const [track] = stream.getVideoTracks();

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

Toestemmingsprompt

De eerste aanroep van sendWheel() of setZoomLevel() op een bepaald CaptureController object produceert een toestemmingsprompt. Als de gebruiker toestemming verleent, zijn verdere aanroepen van deze methoden op dat CaptureController object toegestaan. Als de gebruiker toestemming weigert, wordt de geretourneerde belofte afgewezen.

Houd er rekening mee dat CaptureController objecten uniek zijn geassocieerd met een specifieke capture-session , niet kunnen worden geassocieerd met een andere capture-session en de navigatie op de pagina waar ze zijn gedefinieerd niet overleven. Vastlegsessies overleven echter wel de navigatie op de vastgelegde pagina.

Er is een gebruikersgebaar vereist om een ​​toestemmingsprompt aan de gebruiker te tonen. Alleen sendWheel() en setZoomLevel() aanroepen vereisen een gebruikersgebaar, en alleen als de prompt moet worden weergegeven. Als de gebruiker op een in- of uitzoomknop in de webapp klikt, is dat gebruikersgebaar een gegeven; maar als de app eerst scrollcontrole wil bieden, moeten ontwikkelaars er rekening mee houden dat scrollen geen gebruikersgebaar is. Een mogelijkheid is om de gebruiker eerst een knop 'start scrollen' aan te bieden, zoals in het volgende voorbeeld:

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

Rol

Met behulp van sendWheel() kan een vastleggende app wielgebeurtenissen van de gekozen omvang leveren via coördinaten naar keuze binnen de viewport van een tabblad. De gebeurtenis is voor de vastgelegde app niet te onderscheiden van directe gebruikersinteractie.

Ervan uitgaande dat de opname-app een <video> -element gebruikt met de naam "previewTile" , laat de volgende code zien hoe stuurwielgebeurtenissen naar het vastgelegde tabblad kunnen worden doorgestuurd:

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

De methode sendWheel() gebruikt een woordenboek met twee sets waarden:

  • x en y : de coördinaten waar de wielgebeurtenis moet worden afgeleverd.
  • wheelDeltaX en wheelDeltaY : de grootte van de scrolls, in pixels, voor respectievelijk horizontale en verticale scrolls. Merk op dat deze waarden omgekeerd zijn vergeleken met de originele wielgebeurtenis.

Een mogelijke implementatie van translateCoordinates() is:

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

Merk op dat er drie verschillende maten in de code eerder voorkomen:

  • De grootte van het <video> -element.
  • De grootte van de vastgelegde frames (hier weergegeven als trackSettings.width en trackSettings.height ).
  • De grootte van het tabblad.

De grootte van het <video> -element valt volledig binnen het domein van de vastleggende app en is onbekend voor de browser. De grootte van het tabblad valt volledig binnen het domein van de browser en is onbekend voor de webapp.

De webapp maakt gebruik van translateCoordinates() om de verschuivingen ten opzichte van het <video> -element te vertalen naar coördinaten binnen de eigen coördinatenruimte van het videospoor. De browser vertaalt op dezelfde manier tussen de grootte van de vastgelegde frames en de grootte van het tabblad, en levert de scrollgebeurtenis af met een offset die overeenkomt met de verwachting van de webapp.

De door sendWheel() geretourneerde belofte kan in de volgende gevallen worden afgewezen:

  • If the capture session has not yet started or has already stopped, including stopping asynchronously while the sendWheel() action is handled by the browser.
  • Als de gebruiker de app geen toestemming heeft gegeven om sendWheel() te gebruiken.
  • Als de vastleggende app probeert een scrollgebeurtenis af te leveren in coördinaten die buiten [trackSettings.width, trackSettings.height] liggen. Houd er rekening mee dat deze waarden asynchroon kunnen veranderen. Het is dus een goed idee om de fout op te sporen en te negeren. (Merk op dat 0, 0 normaal gesproken niet buiten het bereik valt, dus het is veilig om ze te gebruiken om de gebruiker om toestemming te vragen.)

Zoom

Interactie met het zoomniveau van het vastgelegde tabblad vindt plaats via de volgende CaptureController oppervlakken:

  • getSupportedZoomLevels() retourneert een lijst met zoomniveaus die door de browser worden ondersteund, weergegeven als percentages van het "standaardzoomniveau", dat is gedefinieerd als 100%. Deze lijst is monotoon stijgend en bevat de waarde 100.
  • getZoomLevel() retourneert het huidige zoomniveau van het tabblad.
  • setZoomLevel() stelt het zoomniveau van het tabblad in op een geheel getal dat aanwezig is in getSupportedZoomLevels() , en retourneert een belofte als dit lukt. Houd er rekening mee dat het zoomniveau niet wordt gereset aan het einde van de opnamesessie.
  • oncapturedzoomlevelchange kunt u luisteren naar de wijzigingen in het zoomniveau van een vastgelegd tabblad, aangezien gebruikers het zoomniveau kunnen wijzigen via de vastgelegde app of via directe interactie met het vastgelegde tabblad.

Aanroepen naar setZoomLevel() zijn beveiligd met toestemming; oproepen naar de andere, alleen-lezen zoommethoden zijn "gratis", net als het luisteren naar gebeurtenissen.

In het volgende voorbeeld ziet u hoe u het zoomniveau van een vastgelegd tabblad in een bestaande vastlegsessie kunt verhogen:

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

In het volgende voorbeeld ziet u hoe u moet reageren op wijzigingen in het zoomniveau van een vastgelegd tabblad:

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

Functiedetectie

Om te controleren of het verzenden van wielgebeurtenissen wordt ondersteund, gebruikt u:

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

Om te controleren of het bedienen van de zoom wordt ondersteund, gebruikt u:

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

Schakel vastgelegde oppervlaktecontrole in

De Captured Surface Control API is beschikbaar in Chrome op het bureaublad achter de vlag Captured Surface Control en kan worden ingeschakeld via chrome://flags/#captured-surface-control .

Deze functie gaat ook een origin-proefperiode in, te beginnen met Chrome 122 op desktop , waarmee ontwikkelaars de functie kunnen inschakelen waarmee bezoekers van hun sites gegevens van echte gebruikers kunnen verzamelen. Zie Aan de slag met origin-proeven voor meer informatie over origin-proeven en hoe deze werken.

Beveiliging en privacy

Met het toestemmingsbeleid "captured-surface-control" kunt u beheren hoe uw capture-app en ingebedde iframes van derden toegang hebben tot Captured Surface Control. Om de afwegingen op het gebied van beveiliging te begrijpen, bekijk je het gedeelte Privacy- en beveiligingsoverwegingen van de uitleg over Captured Surface Control.

Demo

Je kunt met Captured Surface Control spelen door de demo op Glitch uit te voeren. Zorg ervoor dat u de broncode bekijkt .

Wijzigingen ten opzichte van eerdere versies van Chrome

Hier zijn enkele belangrijke gedragsverschillen met betrekking tot Captured Surface Control waarvan u op de hoogte moet zijn:

  • In Chrome 124 en eerder:
    • De toestemming (indien verleend) is van toepassing op de opnamesessie die is gekoppeld aan die CaptureController , niet op de oorsprong van het vastleggen.
  • In Chroom 122:
    • getZoomLevel() retourneert een belofte met het huidige zoomniveau van het tabblad.
    • sendWheel() retourneert een afgewezen belofte met de foutmelding "No permission." als de gebruiker de app geen toestemming heeft gegeven om te gebruiken. Het fouttype is "NotAllowedError" in Chrome 123 en hoger.
    • oncapturedzoomlevelchange is niet beschikbaar. U kunt deze functie polyfillen met setInterval() .

Feedback

Het Chrome-team en de webstandaardgemeenschap willen graag horen over uw ervaringen met Captured Surface Control.

Vertel ons over het ontwerp

Is er iets aan Captured Surface Capture dat niet werkt zoals je had verwacht? Of ontbreken er methoden of eigenschappen die je nodig hebt om je idee te implementeren? Heeft u een vraag of opmerking over het beveiligingsmodel? Dien een spec-probleem in op de GitHub-repository of voeg uw mening toe aan een bestaand probleem.

Probleem met de implementatie?

Heeft u een bug gevonden in de implementatie van Chrome? Of wijkt de uitvoering af van de specificaties? Dien een bug in op https://new.crbug.com . Zorg ervoor dat u zoveel mogelijk details vermeldt, evenals instructies voor reproductie. Glitch werkt prima voor het delen van reproduceerbare bugs.