Przewijanie i powiększanie zarejestrowanej karty

François Beaufort
François Beaufort

Udostępnianie kart, okien i ekranów jest już możliwe na platformie internetowej dzięki interfejsowi Screen Capture API. Gdy aplikacja internetowa wywołuje getDisplayMedia(), Chrome prosi użytkownika o udostępnienie karty, okna lub ekranu jako filmu MediaStreamTrack.

Wiele aplikacji internetowych korzystających z getDisplayMedia() wyświetla użytkownikowi podgląd zarejestrowanej powierzchni. Na przykład aplikacje do rozmów wideo często przesyłają strumieniowo film do użytkowników zdalnych, a jednocześnie renderują go w lokalnym środowisku HTMLVideoElement, dzięki czemu użytkownik lokalny stale widzi podgląd tego, co udostępniają.

W tej dokumentacji omawiamy nowy interfejs Captured Surface Control API w Chrome, który pozwala aplikacji internetowej przewijać przechwyconą kartę, a także odczytywać i zapisywać poziom powiększenia przechwyconej karty.

Użytkownik przewija i powiększa przechwyconą kartę (demonstracja).

Dlaczego warto korzystać z kontroli przechwyconej powierzchni?

Wszystkie aplikacje do rozmów wideo mają tę samą wadę: jeśli użytkownik chce skorzystać z przechwyconej karty lub okna, musi przełączyć się na tę platformę, co spowoduje opuszczenie aplikacji do rozmów wideo. Wiąże się to z pewnymi wyzwaniami:

  • Użytkownik nie będzie mógł jednocześnie wyświetlić przechwyconej aplikacji i filmów użytkowników zdalnych, chyba że użyje trybu Obraz w obrazie lub osobnego okien obok siebie dla karty rozmowy wideo i udostępnionej karty. Na mniejszym ekranie może to być trudne.
  • Użytkownik jest obciążany koniecznością przechodzenia między aplikacją do rozmów wideo a przechwyconą powierzchnią.
  • Użytkownik utraci dostęp do elementów sterujących, które wyświetla aplikacja do rozmów wideo, gdy nie jest w pobliżu, np. umieszczonej aplikacji do obsługi czatu, reakcji emotikonem, powiadomień o prośbach użytkowników o dołączenie do rozmowy, elementów sterujących multimediami i układu oraz innych przydatnych funkcji rozmów wideo.
  • Prezenter nie może przekazać sterowania uczestnikom zdalnym. Prowadzi to do dosyć znanych sytuacji, w których użytkownicy zdalni proszą prezentera o zmianę slajdu, przewinięcie trochę w górę i w dół albo dostosowanie poziomu powiększenia.

Interfejs Captured Surface Control API rozwiązuje te problemy.

Jak korzystać z kontroli przechwyconej powierzchni?

Żeby skorzystać z funkcji Przechwycona powierzchnia, trzeba wykonać kilka czynności, takich jak jawne przechwycenie karty przeglądarki i uzyskiwanie uprawnień użytkownika przed przewijaniem i powiększaniem zapisanej karty.

Przechwytywanie karty przeglądarki

Najpierw poproś użytkownika o wybranie powierzchni do udostępnienia za pomocą getDisplayMedia(). Następnie powiąż obiekt CaptureController z sesją przechwytywania. Wkrótce użyjemy tego obiektu, aby kontrolować sfotografowaną powierzchnię.

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

Następnie utwórz lokalny podgląd przechwyconej powierzchni w postaci elementu <video>:

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

Jeśli użytkownik zdecyduje się udostępnić okno lub ekran, nie jest to na razie możliwe. Jeśli jednak zdecyduje się udostępnić kartę, możemy kontynuować.

const [track] = stream.getVideoTracks();

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

Prośba o zgodę

Pierwsze wywołanie sendWheel() lub setZoomLevel() w danym obiekcie CaptureController powoduje wyświetlenie prośby o uprawnienia. Jeśli użytkownik przyzna odpowiednie uprawnienia, dozwolone będzie dalsze wywoływanie tych metod w obiekcie CaptureController. Jeśli użytkownik odmówi zgody, zwrócona obietnica zostanie odrzucona.

Pamiętaj, że obiekty CaptureController są jednoznacznie powiązane z konkretną sesją przechwytywania, nie można ich powiązać z inną sesją przechwytywania ani nie przetrwają poruszania się po stronie, na której zostały zdefiniowane. Sesje przechwytywania pozostają jednak aktywne po przejściu na przechwyconą stronę.

Aby wyświetlić użytkownikowi prośbę o przyznanie uprawnień, wymagany jest gest użytkownika. Tylko wywołania sendWheel() i setZoomLevel() wymagają gestu użytkownika i tylko wtedy, gdy trzeba wyświetlić komunikat. Jeśli użytkownik kliknie w aplikacji internetowej przycisk powiększenia lub pomniejszenia, taki gest zostanie uznany. Jeśli aplikacja chce najpierw sterować przewijaniem, deweloperzy powinni pamiętać, że przewijanie nie jest gestem użytkownika. Jedną z możliwości jest zaoferowanie użytkownikowi najpierw przycisku „Rozpocznij przewijanie”, jak w tym przykładzie:

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

Przewiń

Dzięki sendWheel() aplikacja przechwytująca może przesyłać zdarzenia koła o wybranej skali w zależności od wybranych współrzędnych w widocznym obszarze karty. Od bezpośredniej interakcji z użytkownikiem nie można odróżnić zdarzenia od przechwyconej aplikacji.

Zakładając, że aplikacja przechwytująca korzysta z elementu <video> o nazwie "previewTile", poniższy kod pokazuje, jak przekazywać do przechwyconej karty zdarzenia wysyłania danych:

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

Metoda sendWheel() korzysta ze słownika z 2 zestawami wartości:

  • x i y: współrzędne miejsca, w którym ma być dostarczone zdarzenie diabelskiego młyna.
  • wheelDeltaX i wheelDeltaY: wielkość przewijania w pikselach odpowiednio do przewinięcia w poziomie i w pionie. Pamiętaj, że te wartości są odwrócone w porównaniu z pierwotnym zdarzeniem koła.

Możliwa implementacja translateCoordinates() to:

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

Zwróć uwagę, że we wcześniejszym kodzie są 3 różne rozmiary:

  • Rozmiar elementu <video>.
  • Rozmiar przechwyconych klatek (przedstawiony tutaj jako trackSettings.width i trackSettings.height).
  • Rozmiar karty.

Rozmiar elementu <video> znajduje się w całości w domenie aplikacji przechwytującej i nie jest znany przeglądarce. Rozmiar karty mieści się w całości w domenie przeglądarki i nie jest znany aplikacji internetowej.

Aplikacja internetowa używa funkcji translateCoordinates() do tłumaczenia przesunięcia względem elementu <video> na współrzędne we własnej przestrzeni współrzędnych ścieżki wideo. Przeglądarka będzie również przesuwać wielkość przechwyconych ramek a rozmiar karty i wyświetlać zdarzenie przewijania z przesunięciem odpowiadającym oczekiwaniom aplikacji internetowej.

Obietnica zwrócona przez usługę sendWheel() może zostać odrzucona w tych przypadkach:

  • Jeśli sesja przechwytywania jeszcze się nie rozpoczęła lub została już zatrzymana, w tym zatrzymanie asynchroniczne, gdy działanie sendWheel() jest obsługiwane przez przeglądarkę.
  • Jeśli użytkownik nie przyznał aplikacji uprawnień do korzystania z sendWheel().
  • Jeśli aplikacja przechwytująca próbuje dostarczyć zdarzenie przewijania o współrzędnych spoza [trackSettings.width, trackSettings.height]. Pamiętaj, że te wartości mogą zmieniać się asynchronicznie, dlatego warto wychwycić błąd i go zignorować. (Pamiętaj, że pliki 0, 0 zwykle nie są poza zakresem, więc można bezpiecznie użyć go, aby poprosić użytkownika o przyznanie uprawnień).

Zoom

Poziom powiększenia zarejestrowanej karty można wykonywać za pomocą tych platform CaptureController:

  • getSupportedZoomLevels() zwraca listę poziomów powiększenia obsługiwanych przez przeglądarkę, wyrażoną jako wartości procentowe „domyślnego poziomu powiększenia” zdefiniowanego jako 100%. Ta lista rośnie monotonicznie i zawiera wartość 100.
  • getZoomLevel() zwraca bieżący poziom powiększenia karty.
  • Funkcja setZoomLevel() ustawia poziom powiększenia karty na dowolną wartość całkowitą występującą w polu getSupportedZoomLevels() i zwraca obietnicę, jeśli operacja się uda. Pamiętaj, że poziom powiększenia nie jest resetowany po zakończeniu sesji nagrywania.
  • oncapturedzoomlevelchange umożliwia odsłuchiwanie zmian poziomu powiększenia na przechwyconej karcie. Użytkownicy mogą zmieniać poziom powiększenia za pomocą aplikacji do przechwytywania lub bezpośredniej interakcji z przechwyconą kartą.

Wywołania funkcji setZoomLevel() są ograniczone przez uprawnienia. Wywołania innych metod powiększenia (tylko do odczytu) są „bezpłatne”, podobnie jak podsłuchiwanie zdarzeń.

Ten przykład pokazuje, jak zwiększyć poziom powiększenia przechwyconej karty w istniejącej sesji przechwytywania:

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

Poniższy przykład pokazuje reakcję na zmiany poziomu powiększenia na przechwyconej karcie:

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

Wykrywanie funkcji

Aby sprawdzić, czy wysyłanie zdarzeń koła zębatego jest obsługiwane, użyj polecenia:

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

Aby sprawdzić, czy można sterować powiększeniem, użyj:

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

Włącz przechwyconą powierzchnię

Interfejs Captured Surface Control API jest dostępny w Chrome na komputerach za pomocą flagi Captured Surface Control i można go włączyć na stronie chrome://flags/#captured-surface-control.

Ta funkcja rozpoczyna też okres próbny origin Chrome 122 na komputerach, który umożliwia deweloperom włączenie tej funkcji w przypadku użytkowników odwiedzających ich witryny, aby mogli zbierać dane od prawdziwych użytkowników. Więcej informacji o testach origin i sposobie ich działania znajdziesz w artykule Pierwsze kroki z testami origin.

Prywatność i bezpieczeństwo

Zasada uprawnień "captured-surface-control" pozwala zarządzać dostępem aplikacji przechwytującej i umieszczonych zewnętrznych elementów iframe do Przechwyconej kontroli powierzchni. Aby dowiedzieć się więcej o wpływie na bezpieczeństwo, zapoznaj się z sekcją Kwestie związane z prywatnością i bezpieczeństwem w wyjaśnieniu dotyczącym przechwytywania ekranu.

Pokaz

Możesz pobawić się funkcją przechwytywania powierzchni, uruchamiając prezentację w Glitchu. Pamiętaj, aby sprawdzić kod źródłowy.

Zmiany w porównaniu z poprzednimi wersjami Chrome

Oto kilka kluczowych różnic w działaniu funkcji Przechwycona kontrola powierzchni, o których należy pamiętać:

  • W Chrome 124 i starszych wersjach:
    • Uprawnienie – jeśli zostanie przyznane – jest ograniczone do sesji przechwytywania powiązanej z tym elementem CaptureController, a nie do źródła przechwytywania.
  • W Chrome 122:
    • Funkcja getZoomLevel() zwraca obietnicę z bieżącym poziomem powiększenia karty.
    • sendWheel() zwraca obietnicę odrzucona z komunikatem o błędzie "No permission.", jeśli użytkownik nie przyznał aplikacji uprawnień do korzystania. Rodzaj błędu to "NotAllowedError" w Chrome 123 i nowszych wersjach.
    • Domena oncapturedzoomlevelchange jest niedostępna. Tę funkcję możesz zastosować polyfill, używając: setInterval().

Prześlij opinię

Zespół Chrome i społeczność zajmująca się standardami internetowymi chcą poznać Twoje wrażenia z korzystania z aplikacji Captured Surface Control.

Opowiedz nam o projekcie

Czy jest coś, co nie działa zgodnie z oczekiwaniami w funkcji przechwytywania powierzchni ekranu? A może brakuje metod lub właściwości, których potrzebujesz do realizacji swojego pomysłu? Masz pytanie lub komentarz na temat modelu zabezpieczeń? Zgłoś problem ze specyfikacją w repozytorium GitHub lub dodaj swoje uwagi na temat istniejącego problemu.

Problem z implementacją?

Czy wystąpił błąd związany z implementacją przeglądarki Chrome? A może implementacja różni się od specyfikacji? Zgłoś błąd na https://new.crbug.com. Podaj jak najwięcej szczegółów oraz instrukcje dotyczące odtworzenia. Usterki to doskonały sposób na udostępnianie powtarzalnych błędów.