Przewijanie i powiększanie zarejestrowanej karty

François Beaufort
François Beaufort

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

Wiele aplikacji internetowych korzystających z getDisplayMedia() wyświetla użytkownikowi podgląd nagranej powierzchni. Na przykład aplikacje do rozmów wideo często przesyłają ten film strumieniowo do zdalnych użytkowników, jednocześnie renderując go na lokalnym urządzeniu HTMLVideoElement, dzięki czemu lokalny użytkownik może stale widzieć podgląd tego, co udostępniają.

W tej dokumentacji opisujemy nowy interfejs Captured Surface Control API w Chrome, który umożliwia aplikacji internetowej przewijanie przechwyconej karty oraz odczytywanie i zapisywanie poziomu powiększenia przechwyconej karty.

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

Dlaczego warto korzystać z funkcji kontroli powierzchni rejestrowanej?

Wszystkie aplikacje do rozmów wideo mają tę samą wadę: jeśli użytkownik chce wejść w interakcję z przechwyconą kartą lub oknem, musi przełączyć się na tę platformę, aby odsunąć go od aplikacji do rozmów wideo. Wiąże się to z pewnymi wyzwaniami:

  • Użytkownik nie może jednocześnie zobaczyć zarejestrowanej aplikacji oraz filmów użytkowników zdalnych, chyba że użyje funkcji Obraz w obrazie lub osobnych okien obok siebie dla karty Rozmowy wideo i karty udostępnionej. Na mniejszym ekranie może to być trudne.
  • Użytkownik jest zmęczony koniecznością przełączania się między aplikacją do rozmów wideo a rejestrowaną powierzchnią.
  • Użytkownik traci dostęp do elementów sterujących aplikacji do rozmów wideo, gdy nie znajduje się w jego pobliżu. na przykład umieszczonej aplikacji do czatu, emotikonów, powiadomień o użytkownikach proszących o dołączenie do rozmowy, sterowania multimediami i układem oraz inne przydatne funkcje rozmów wideo.
  • Osoba prowadząca nie może przekazać kontroli uczestnikom zdalnym. Prowadzi to do dobrze znanej sytuacji, w której użytkownicy zdalni proszą prowadzącego o zmianę slajdu, przesunięcie trochę w górę lub w dół albo dostosowanie poziomu powiększenia.

Interfejs Captured Surface Control API rozwiązuje te problemy.

Jak korzystać z przechwyconej kontroli powierzchni?

Użycie funkcji Capture Surface Control wymaga wykonania kilku czynności, na przykład bezpośredniego przechwycenia karty przeglądarki i uzyskania zgody użytkownika przed jej przewinięciem i powiększeniem.

Przechwytywanie karty przeglądarki

Najpierw poproś użytkownika o wybranie platformy do udostępnienia za pomocą getDisplayMedia(), a następnie powiąż obiekt CaptureController z sesją przechwytywania. Będziemy używać tego obiektu do sterowania przechwyconą powierzchnią.

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

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

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

Jeśli użytkownik zdecyduje się udostępnić okno lub ekran, aktualnie wykracza to poza zakres dostępu. Jeśli jednak postanowił 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 metody sendWheel() lub setZoomLevel() dla danego obiektu CaptureController powoduje wyświetlenie prośby o przyznanie uprawnień. Jeśli użytkownik przyzna odpowiednie uprawnienia, dozwolone są kolejne wywołania 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ą capture-session, nie mogą być powiązane z inną sesją przechwytywania ani nie przeżyć po zmianie strony, na której zostały zdefiniowane. Sesje przechwytywania zachowują jednak ważność po poruszaniu się po przechwyconej stronie.

Aby wyświetlić prośbę o przyznanie uprawnień, wymagany jest gest użytkownika. Tylko połączenia sendWheel() i setZoomLevel() wymagają gestu użytkownika i tylko wtedy, gdy trzeba pokazać prośbę. jeśli użytkownik kliknie przycisk powiększania lub pomniejszania w aplikacji internetowej, wyświetli się gest tego użytkownika; ale jeśli aplikacja chce najpierw zaoferować sterowanie przewijaniem, deweloperzy powinni pamiętać, że przewijanie nie jest gestem użytkownika. Jedną z możliwości jest zaoferowanie użytkownikowi „rozpoczęcia przewijania”, zgodnie z poniższym przykładem:

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 funkcji sendWheel() aplikacja do przechwytywania może wyświetlać zdarzenia obrotowe o wybranej wielkości przez wybrane współrzędne w widocznym obszarze karty. Zdarzenia nie da się odróżnić od zarejestrowanej aplikacji od bezpośredniej interakcji użytkownika.

Zakładając, że aplikacja do przechwytywania ruchu wykorzystuje element <video> o nazwie "previewTile", poniższy kod pokazuje, jak przekazywać zdarzenia wysyłane kołami do przechwyconych kart:

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

Metoda sendWheel() wykorzystuje słownik z 2 zbiorami wartości:

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

Oto możliwa implementacja 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)];
}

Zwróć uwagę, że w wcześniejszym kodzie występują trzy różne rozmiary:

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

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

Aplikacja internetowa używa właściwości translateCoordinates() do przekształcania przesunięcia względem elementu <video> na współrzędne w przestrzeni współrzędnych ścieżki wideo. Przeglądarka również dostosuje rozmiar zarejestrowanych klatek do rozmiaru karty i wyświetli zdarzenie przewijania z przesunięciem zgodnym z oczekiwaniami aplikacji internetowej.

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

  • Sesja przechwytywania jeszcze się nie rozpoczęła lub już się zakończyła, 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 narzędzia sendWheel().
  • Jeśli aplikacja przechwytuje zdarzenie przewijania we współrzędnych, które są poza obszarem [trackSettings.width, trackSettings.height]. Pamiętaj, że te wartości mogą zmieniać się asynchronicznie, warto więc wychwycić i zignorować błąd. (Pamiętaj, że typ pola 0, 0 zwykle nie znajduje się poza zakresem, więc można go bezpiecznie użyć, aby poprosić użytkownika o zgodę).

Zoom

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

  • getSupportedZoomLevels() zwraca listę poziomów powiększenia obsługiwanych przez przeglądarkę wyrażoną jako procent „domyślnego poziomu powiększenia”, który wynosi 100%. Ta lista rośnie monotonicznie i zawiera wartość 100.
  • getZoomLevel() zwraca bieżący poziom powiększenia karty.
  • setZoomLevel() ustawia poziom powiększenia karty na dowolną wartość całkowitą występującą w polu getSupportedZoomLevels() i zwraca obietnicę, gdy operacja się uda. Pamiętaj, że poziom powiększenia nie jest resetowany na koniec sesji przechwytywania.
  • oncapturedzoomlevelchange umożliwia słuchanie zmian poziomu powiększenia zarejestrowanej karty, ponieważ użytkownicy mogą zmieniać poziom powiększenia z poziomu aplikacji do nagrywania lub przez bezpośrednią interakcję z otwartą kartą.

Połączenia z numerem setZoomLevel() są ograniczone przez pozwolenie. inne metody powiększenia (tylko do odczytu) są „bezpłatne”, podobnie jak nasłuchiwanie zdarzeń.

Poniższy przykład pokazuje, jak zwiększyć poziom powiększenia zarejestrowanej karty w dotychczasowej 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 zarejestrowanej karcie:

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

Wykrywanie cech

Aby sprawdzić, czy wysyłanie zdarzeń kółka myszy jest obsługiwane, użyj polecenia:

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

Aby sprawdzić, czy sterowanie powiększeniem jest obsługiwane, użyj tych poleceń:

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

Włącz przechwyconą kontrolę powierzchni

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

W ramach tej funkcji rozpoczyna się też wersja próbna origin Chrome 122 na komputery, dzięki której deweloperzy mogą włączyć tę funkcję dla użytkowników swoich witryn w celu zbierania danych od prawdziwych użytkowników. Więcej informacji o testowaniu origin i sposobie ich działania znajdziesz w artykule Pierwsze kroki z testowaniem origin.

Bezpieczeństwo i prywatność

Zasady dotyczące uprawnień "captured-surface-control" pozwalają zarządzać dostępem aplikacji do przechwytywania i umieszczonych elementów iframe innych firm do przechwyconych informacji o platformie. Aby lepiej zrozumieć wady bezpieczeństwa, zapoznaj się z sekcją Uwagi dotyczące prywatności i bezpieczeństwa w wyjaśnieniu dotyczącym przechwytywania ekranu.

Prezentacja

Możesz wypróbować funkcję Captured Surface Control, uruchamiając wersję demonstracyjną aplikacji Glitch. Koniecznie zapoznaj się z kodem źródłowym.

Zmiany w porównaniu z poprzednimi wersjami Chrome

Oto kilka głównych różnic w działaniu rejestrowanych urządzeń kontroli powierzchni, o których trzeba pamiętać:

  • W Chrome 124 i starszych wersjach:
    • Uprawnienie (jeśli zostało przyznane) jest ograniczone do sesji przechwytywania powiązanej z danym elementem CaptureController, a nie do źródła przechwytywania.
  • W Chrome 122:
    • getZoomLevel() zwraca obietnicę przy bieżącym poziomie powiększenia karty.
    • Jeśli użytkownik nie przyznał aplikacji uprawnień do korzystania, sendWheel() zwraca obietnicę odrzucona z komunikatem o błędzie "No permission.". Typ błędu to "NotAllowedError" w Chrome 123 i nowszych wersjach.
    • Domena oncapturedzoomlevelchange jest niedostępna. Możesz użyć kodu polyfill za pomocą setInterval().

Prześlij opinię

Zespół Chrome i społeczność zajmująca się standardami internetowymi chcą poznać Waszą opinię o funkcji Przechwycona kontrola powierzchni.

Opowiedz nam o projekcie

Czy jest coś, co w rejestrowanych obrazach powierzchni nie działa zgodnie z oczekiwaniami? A może brakuje Ci metod lub właściwości, które pozwolą Ci zrealizować Twój pomysł? Masz pytanie lub komentarz na temat modelu zabezpieczeń? Zgłoś problem ze specyfikacją w repozytorium GitHub lub opisz swój problem.

Problem z implementacją?

Czy wystąpił błąd z implementacją Chrome? Czy implementacja różni się od specyfikacji? Zgłoś błąd na https://new.crbug.com. Postaraj się podać jak najwięcej szczegółów oraz instrukcje odtwarzania. Glitch doskonale sprawdza się w przypadku udostępniania odtwarzanych błędów.