Zarządzanie kilkoma wyświetlaczami za pomocą interfejsu Window Management API

uzyskiwać informacje o podłączonych wyświetlaczach i oknach w stosunku do tych wyświetlaczy;

Window Management API

Interfejs Window Management API umożliwia zliczanie wyświetlaczy połączonych z urządzeniem oraz umieszczanie okien na określonych ekranach.

Sugerowane zastosowania

Przykłady witryn, które mogą korzystać z tego interfejsu API:

  • Edytory grafiki z wieloma oknami w stylu Gimp mogą umieszczać różne narzędzia do edycji w precyzyjnie rozmieszczonych oknach.
  • Wirtualne biura transakcyjne mogą wyświetlać trendy rynkowe w wielu oknach, z których każde można wyświetlić w trybie pełnoekranowym.
  • Aplikacje do pokazów slajdów mogą wyświetlać notatki na wewnętrznym ekranie głównym, a prezentację na zewnętrznym projektorze.

Jak korzystać z interfejsu Window Management API

Problem

Sprawdzona metoda sterowania oknamiWindow.open() nie uwzględnia niestety dodatkowych ekranów. Chociaż niektóre aspekty tego interfejsu API wydają się nieco przestarzałe, np. parametr windowFeatures DOMString, to przez lata dobrze nam służył. Aby określić położenie okna, możesz przekazać współrzędne jako lefttop (odpowiednio screenXscreenY) oraz przekazać żądany rozmiar jako widthheight (odpowiednio innerWidthinnerHeight). Jeśli na przykład chcesz otworzyć okno o wymiarach 400 × 300 w odległości 50 pikseli od lewej i 50 pikseli od góry, możesz użyć tego kodu:

const popup = window.open(
  'https://example.com/',
  'My Popup',
  'left=50,top=50,width=400,height=300',
);

Informacje o bieżącym ekranie możesz uzyskać, korzystając z właściwości window.screen, która zwraca obiekt Screen. Oto wynik na moim MacBooku Pro 13″:

window.screen;
/* Output from my MacBook Pro 13″:
  availHeight: 969
  availLeft: 0
  availTop: 25
  availWidth: 1680
  colorDepth: 30
  height: 1050
  isExtended: true
  onchange: null
  orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
  pixelDepth: 30
  width: 1680
*/

Podobnie jak większość osób pracujących w branży technologicznej musiałem dostosować się do nowej rzeczywistości i urządzić sobie domowe biuro. Mój wygląda tak jak na zdjęciu poniżej (jeśli chcesz, możesz przeczytać szczegóły dotyczące mojego konfiguracji). iPad obok MacBooka jest połączony z laptopem przez Sidecar, więc w razie potrzeby mogę szybko przekształcić iPada w drugi ekran.

Ławka szkolna na 2 krzesłach. Na szkolnej ławce znajdują się pudełka po butach, na których stoi laptop, a wokół niego 2 iPady.
Konfiguracja wielu ekranów.

Jeśli chcę skorzystać z większego ekranu, mogę umieścić wyskakujące okienko z przykładowego kodu powyżej na drugim ekranie. Robię to tak:

popup.moveTo(2500, 50);

Jest to przybliżony szacunek, ponieważ nie ma możliwości określenia wymiarów drugiego ekranu. Informacje z window.screen dotyczą tylko wbudowanego ekranu, a nie ekranu iPada. Raportowany width ekranu wbudowanego urządzenia to 1680 pikseli, więc przeniesienie do 2500 pikseli może pomóc w przesunięciu okna na iPada, ponieważ wiem, że znajduje się on po prawej stronie mojego MacBooka. Jak mogę to zrobić w ogóle? Okazuje się, że istnieje lepszy sposób niż zgadywanie. Jest to interfejs Window Management API.

Wykrywanie cech

Aby sprawdzić, czy interfejs Window Management API jest obsługiwany, użyj:

if ('getScreenDetails' in window) {
  // The Window Management API is supported.
}

Uprawnienie window-management

Zanim użyję interfejsu Window Management API, muszę poprosić użytkownika o pozwolenie. Uprawnienie window-management można zapytać za pomocą interfejsu Permissions API w ten sposób:

let granted = false;
try {
  const { state } = await navigator.permissions.query({ name: 'window-management' });
  granted = state === 'granted';
} catch {
  // Nothing.
}

Podczas korzystania z przeglądarek z uprawnieniami o starej i nowej nazwie pamiętaj, aby podczas prośby o uprawnienia używać kodu obronnego, jak w przykładzie poniżej.

async function getWindowManagementPermissionState() {
  let state;
  // The new permission name.
  try {
    ({ state } = await navigator.permissions.query({
      name: "window-management",
    }));
  } catch (err) {
    return `${err.name}: ${err.message}`;
  }
  return state;
}

document.querySelector("button").addEventListener("click", async () => {
  const state = await getWindowManagementPermissionState();
  document.querySelector("pre").textContent = state;
});

Przeglądarka może wyświetlić prośbę o uprawnienia dynamicznie przy pierwszej próbie użycia dowolnej metody nowego interfejsu API. Aby dowiedzieć się więcej, czytaj dalej.

Właściwość window.screen.isExtended

Aby sprawdzić, czy do mojego urządzenia jest podłączonych więcej niż 1 ekran, otwieram usługę window.screen.isExtended. Zwraca wartość true lub false. W moim przypadku zwraca on wartość true.

window.screen.isExtended;
// Returns `true` or `false`.

Metoda getScreenDetails()

Teraz, gdy wiem, że bieżąca konfiguracja to konfiguracja wieloekranowa, mogę uzyskać więcej informacji o drugim ekranie, używając Window.getScreenDetails(). Po wywołaniu tej funkcji wyświetli się prośba o uprawnienia, w której pytam, czy witryna może otwierać i umieszczać okna na moim ekranie. Funkcja zwraca obietnicę, która zwraca obiekt ScreenDetailed. Na moim MacBooku Pro 13 z podłączonym iPadem obejmuje to pole screens z 2 obiektmi ScreenDetailed:

await window.getScreenDetails();
/* Output from my MacBook Pro 13″ with the iPad attached:
{
  currentScreen: ScreenDetailed {left: 0, top: 0, isPrimary: true, isInternal: true, devicePixelRatio: 2, …}
  oncurrentscreenchange: null
  onscreenschange: null
  screens: [{
    // The MacBook Pro
    availHeight: 969
    availLeft: 0
    availTop: 25
    availWidth: 1680
    colorDepth: 30
    devicePixelRatio: 2
    height: 1050
    isExtended: true
    isInternal: true
    isPrimary: true
    label: "Built-in Retina Display"
    left: 0
    onchange: null
    orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
    pixelDepth: 30
    top: 0
    width: 1680
  },
  {
    // The iPad
    availHeight: 999
    availLeft: 1680
    availTop: 25
    availWidth: 1366
    colorDepth: 24
    devicePixelRatio: 2
    height: 1024
    isExtended: true
    isInternal: false
    isPrimary: false
    label: "Sidecar Display (AirPlay)"
    left: 1680
    onchange: null
    orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
    pixelDepth: 24
    top: 0
    width: 1366
  }]
}
*/

Informacje o połączonych ekranach są dostępne w tablicy screens. Zwróć uwagę, że wartość left dla iPada zaczyna się od 1680, co odpowiada dokładnie width wbudowanego wyświetlacza. Pozwala mi to określić, jak dokładnie ekrany są logicznie rozmieszczone (obok siebie, jeden na drugim itp.). W przypadku każdego ekranu są też dane określające, czy jest to ekran isInternalczy isPrimary. Pamiętaj, że wbudowany ekran niekoniecznie jest ekranem głównym.

Pole currentScreen to aktywny obiekt odpowiadający bieżącemu window.screen. Obiekt jest aktualizowany w przypadku umieszczenia okna na różnych ekranach lub zmiany urządzenia.

Zdarzenie screenschange

Jedyną rzeczą, której brakuje, jest sposób wykrywania zmian w układzie ekranu. Nowe zdarzenie screenschange działa właśnie w ten sposób: jest wywoływane, gdy nastąpi zmiana konstelacji ekranu. (Zwróć uwagę, że w nazwie zdarzenia „screens” występuje w formie liczby mnogiej.) Oznacza to, że zdarzenie jest wywoływane za każdym razem, gdy nowy lub istniejący ekran jest podłączany lub odłączany (fizycznie lub wirtualnie w przypadku Sidecar).

Pamiętaj, że informacje o nowym ekranie musisz wyszukiwać asynchronicznie, ponieważ zdarzenie screenschange samo w sobie nie zawiera tych danych. Aby sprawdzić szczegóły ekranu, użyj obiektu na żywo z interfejsu z pamięci podręcznejScreens.

const screenDetails = await window.getScreenDetails();
let cachedScreensLength = screenDetails.screens.length;
screenDetails.addEventListener('screenschange', (event) => {
  if (screenDetails.screens.length !== cachedScreensLength) {
    console.log(
      `The screen count changed from ${cachedScreensLength} to ${screenDetails.screens.length}`,
    );
    cachedScreensLength = screenDetails.screens.length;
  }
});

Zdarzenie currentscreenchange

Jeśli interesują mnie tylko zmiany na bieżącym ekranie (czyli wartość obiektu na żywocurrentScreen), mogę nasłuchiwać zdarzenia currentscreenchange.

const screenDetails = await window.getScreenDetails();
screenDetails.addEventListener('currentscreenchange', async (event) => {
  const details = screenDetails.currentScreen;
  console.log('The current screen has changed.', event, details);
});

Zdarzenie change

Jeśli interesują mnie tylko zmiany na konkretnym ekranie, mogę odsłuchać zdarzenie change tego ekranu.

const firstScreen = (await window.getScreenDetails())[0];
firstScreen.addEventListener('change', async (event) => {
  console.log('The first screen has changed.', event, firstScreen);
});

Nowe opcje pełnego ekranu

Do tej pory można było poprosić o wyświetlanie elementów w trybie pełnoekranowym za pomocą odpowiedniej metody requestFullScreen(). Metoda przyjmuje parametr options, do którego możesz przekazać wartość FullscreenOptions. Do tej pory jego jedyną usługą była usługa navigationUI. Interfejs Window Management API dodaje nową właściwość screen, która pozwala określić, na którym ekranie ma się rozpocząć widok pełnoekranowy. Jeśli na przykład chcesz wyświetlić ekran główny na pełnym ekranie:

try {
  const primaryScreen = (await getScreenDetails()).screens.filter((screen) => screen.isPrimary)[0];
  await document.body.requestFullscreen({ screen: primaryScreen });
} catch (err) {
  console.error(err.name, err.message);
}

Watolina

Nie można polyfillować interfejsu Window Management API, ale możesz użyć szablonu, aby móc kodować wyłącznie w nowym interfejsie API:

if (!('getScreenDetails' in window)) {
  // Returning a one-element array with the current screen,
  // noting that there might be more.
  window.getScreenDetails = async () => [window.screen];
  // Set to `false`, noting that this might be a lie.
  window.screen.isExtended = false;
}

Inne aspekty interfejsu API, czyli różne zdarzenia zmiany ekranu i właściwość screen obiektu FullscreenOptions, nie byłyby w ogóle wywoływane lub byłyby ignorowane przez przeglądarki, które nie obsługują tej funkcji.

Prezentacja

Jeśli jesteś podobny do mnie, na pewno uważnie śledzisz rozwój różnych kryptowalut. (W rzeczywistości nie chcę tego robić, ponieważ kocham tę planetę, ale na potrzeby tego artykułu załóżmy, że tak jest). Aby śledzić swoje kryptowaluty, stworzyłem aplikację internetową, która pozwala mi obserwować rynki w każdej sytuacji życiowej, na przykład z wygody łóżka, gdzie mam przyzwoite ustawienie z jednym ekranem.

Duży telewizor na końcu łóżka. Widoczne są częściowo nogi autora. Na ekranie widać fałszywy dział handlu kryptowalutami.
Relaks i obserwowanie rynków.

W przypadku kryptowalut sytuacja na rynkach może się w każdej chwili zmienić. W takiej sytuacji mogę szybko przejść do mojego biurka, gdzie mam konfigurację z wieloma ekranami. Mogę kliknąć okno dowolnej waluty i szybko wyświetlić pełne szczegóły w widoku pełnoekranowym na drugim ekranie. Poniżej znajduje się moje zdjęcie zrobione podczas ostatniej krwawej bitwy YCY. Złapał mnie całkowicie nieprzygotowanego i umieścił moje ręce na twarzy.

Autor z rękami na twarzy w panice wpatrujący się w fałszywy pulpit do handlu kryptowalutami.
Panicky, świadek masakry w YCY.

Możesz wypróbować demo umieszczone poniżej lub sprawdzić kod źródłowy na stronie glitch.

Zabezpieczenia i uprawnienia

Zespół Chrome zaprojektował i wdrożył interfejs Window Management API, korzystając z podstawowych zasad określonych w artykule Controlling Access to Powerful Web Platform Features (Zarządzanie dostępem do zaawansowanych funkcji platformy internetowej), w tym kontroli użytkownika, przejrzystości i ergonomiki. Interfejs Window Management API udostępnia nowe informacje o ekranach połączonych z urządzeniem, co zwiększa powierzchnię odcisku palca użytkowników, zwłaszcza tych, którzy mają wiele ekranów stale połączonych ze swoimi urządzeniami. Aby rozwiązać ten problem, ograniczyliśmy liczbę wyświetlanych właściwości ekranu do minimum potrzebnego w najczęstszych przypadkach użycia. Aby witryny mogły uzyskiwać informacje o wielu ekranach i rozmieszczać okna na innych ekranach, muszą mieć na to zgodę użytkownika. Podczas gdy Chromium zwraca szczegółowe etykiety ekranu, inne przeglądarki mogą zwracać mniej opisowe (lub nawet puste) etykiety.

Kontrola użytkownika

Użytkownik ma pełną kontrolę nad ekspozycją swojego urządzenia. Mogą zaakceptować lub odrzucić prośbę o zgodę i cofnąć wcześniej udzieloną zgodę za pomocą funkcji informacji o witrynie w przeglądarce.

Kontrola firmowa

Użytkownicy Chrome Enterprise mogą kontrolować kilka aspektów interfejsu Window Management API, jak opisano w odpowiedniej sekcji ustawień Atomicznych grup zasad.

Przejrzystość

Informacje o tym, czy użytkownik zezwolił na korzystanie z interfejsu Window Management API, są dostępne w informacjach o witrynie w przeglądarce. Można je też uzyskać za pomocą interfejsu Permissions API.

Trwałość uprawnień

Przeglądarka zachowuje przyznane uprawnienia. Użytkownik może cofnąć zgodę w informacjach o witrynie przeglądarki.

Prześlij opinię

Zespół Chrome chce poznać Twoje wrażenia z korzystania z interfejsu Window Management API.

Poinformuj nas o projektowaniu interfejsu API

Czy coś w interfejsie API nie działa zgodnie z oczekiwaniami? A może brakuje metod lub właściwości, których potrzebujesz do wdrożenia swojego pomysłu? Masz pytania lub uwagi dotyczące modelu bezpieczeństwa?

  • Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub dodaj swoje uwagi do istniejącego problemu.

Zgłaszanie problemów z implementacją

Czy znalazłeś/znalazłaś błąd w implementacji Chrome? A może implementacja różni się od specyfikacji?

  • Zgłoś błąd na stronie new.crbug.com. Podaj jak najwięcej szczegółów, proste instrukcje odtwarzania błędu i wpisz Blink>Screen>MultiScreen w polu Components. Glitch to świetne narzędzie do szybkiego i łatwego udostępniania informacji o powtarzających się problemach.

Pokaż informacje o pomocy dotyczącej interfejsu API

Zamierzasz używać interfejsu Window Management API? Twoja publiczna pomoc pomaga zespołowi Chrome ustalać priorytety funkcji i pokazuje innym dostawcom przeglądarek, jak ważne jest wspieranie tych funkcji.

Przydatne linki

Podziękowania

Specyfikację interfejsu Window Management API opracowali Victor Costan, Joshua Bell i Mike Wasserman. Interfejs API został wdrożony przez Mike’a WassermanaAdrienne Walker. Ten artykuł został sprawdzony przez Joe Medley, François Beaufort i Kayce Basques. Dziękujemy Laurze Torrent Puig za zdjęcia.