Quản lý một số màn hình bằng Window Management API

Nhận thông tin về màn hình đã kết nối và định vị cửa sổ tương ứng với các màn hình đó.

API Quản lý cửa sổ

API Quản lý cửa sổ cho phép bạn liệt kê các màn hình được kết nối với máy của bạn và để đặt cửa sổ trên các màn hình cụ thể.

Các trường hợp sử dụng được đề xuất

Ví dụ về các trang web có thể sử dụng API này bao gồm:

  • Trình chỉnh sửa đồ hoạ nhiều cửa sổ à la Gimp có thể đặt nhiều vị trí vào các cửa sổ được đặt chính xác.
  • Các bộ phận chuyên trách giao dịch ảo có thể cho thấy xu hướng thị trường qua nhiều cửa sổ, bạn có thể xem bất kỳ cửa sổ nào trong số đó chế độ toàn màn hình.
  • Ứng dụng trình chiếu có thể hiển thị ghi chú của người thuyết trình trên màn hình chính nội bộ và bản trình bày trên máy chiếu bên ngoài.

Cách sử dụng API Quản lý cửa sổ

Vấn đề

Phương pháp kiểm thử lâu dài để kiểm soát cửa sổ, Rất tiếc, Window.open() không biết về màn hình bổ sung. Mặc dù một số khía cạnh của API này có vẻ hơi cũ, chẳng hạn như windowFeatures DOMString, nhưng nền tảng này đã phục vụ chúng tôi tốt trong những năm qua. Để chỉ định vị trí, bạn có thể chuyển hàm có toạ độ dưới dạng lefttop (hoặc screenXscreenY tương ứng) rồi chuyển giá trị mong muốn size thành widthheight (hoặc innerWidthinnerHeight tương ứng). Ví dụ: để mở một Cửa sổ 400 × 300 ở 50 pixel từ bên trái và 50 pixel từ trên cùng, đây là mã mà bạn có thể sử dụng:

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

Bạn có thể nhận thông tin về màn hình hiện tại bằng cách xem Thuộc tính window.screen sẽ trả về một đối tượng Screen. Đây là trên MacBook Pro 13 inch của tôi:

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
*/

Giống như hầu hết những người làm việc trong ngành công nghệ, tôi phải thích ứng với thực tế công việc mới và thiết lập văn phòng cá nhân tại nhà. Của tôi trông giống như trên bức ảnh dưới đây (nếu bạn quan tâm, bạn có thể đọc toàn bộ thông tin chi tiết về cách thiết lập của tôi). iPad bên cạnh MacBook của tôi được kết nối với máy tính xách tay qua Sidecar, để bất cứ khi nào cần, tôi có thể nhanh chóng bật iPad vào màn hình thứ hai.

Ghế dài cho trường học trên hai chiếc ghế. Trên băng ghế của trường học là những chiếc hộp giày để đỡ một chiếc máy tính xách tay và hai chiếc iPad xung quanh.
Thiết lập nhiều màn hình.

Nếu muốn tận dụng màn hình lớn hơn, tôi có thể đặt cửa sổ bật lên từ mã mẫu ở trên chuyển sang màn hình thứ hai. Tôi làm được như sau:

popup.moveTo(2500, 50);

Đây chỉ là phỏng đoán, vì không có cách nào để biết kích thước của màn hình thứ hai. Thông tin từ window.screen chỉ che màn hình tích hợp chứ không che màn hình iPad. width bị báo cáo màn hình tích hợp là 1680 pixel, vì vậy việc di chuyển đến 2500 pixel có thể hiệu quả để chuyển sang iPad, vì tôi biết rằng ứng dụng này nằm ở bên phải MacBook của mình. Cách thức tôi có thể làm điều này đối với trường hợp chung không? Hoá ra vẫn có một cách hay hơn là đoán. Đó chính là API Quản lý cửa sổ.

Phát hiện tính năng

Để kiểm tra xem API Quản lý cửa sổ có được hỗ trợ hay không, hãy sử dụng:

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

Quyền window-management

Trước khi có thể sử dụng API Quản lý cửa sổ, tôi phải yêu cầu người dùng cấp quyền để làm việc đó. Bạn có thể truy vấn quyền window-management bằng API Quyền như vậy:

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

Mặc dù các trình duyệt có tên quyền cũ và tên quyền mới đang được sử dụng, hãy nhớ sử dụng mã phòng vệ khi yêu cầu quyền, như trong ví dụ dưới đây.

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

Trình duyệt có thể chọn tự động hiển thị lời nhắc cấp quyền vào lần đầu tiên sử dụng bất kỳ phương thức nào API mới. Hãy đọc tiếp để tìm hiểu thêm.

Thuộc tính window.screen.isExtended

Để tìm hiểu xem có nhiều màn hình được kết nối với thiết bị của tôi hay không, tôi truy cập Thuộc tính window.screen.isExtended. Hàm này trả về true hoặc false. Đối với chế độ thiết lập của tôi, kết quả trả về sẽ là true.

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

Phương thức getScreenDetails()

Bây giờ, tôi đã biết rằng thiết lập hiện tại là nhiều màn hình, tôi có thể biết thêm thông tin về màn hình thứ hai bằng Window.getScreenDetails(). Khi gọi hàm này, lời nhắc cấp quyền sẽ hiển thị hỏi tôi liệu trang web có thể mở và đặt các cửa sổ trên màn hình của tôi hay không. Hàm này trả về một lời hứa (promise) phân giải bằng đối tượng ScreenDetailed. Trên MacBook Pro 13 có iPad được kết nối, bao gồm một trường screens có 2 đối tượng 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
  }]
}
*/

Thông tin về các màn hình đã kết nối có trong mảng screens. Lưu ý giá trị của left dành cho iPad có giá từ 1680, chính xác là width của màn hình tích hợp. Chiến dịch này cho phép tôi xác định chính xác cách các màn hình được sắp xếp hợp lý (cạnh nhau, trên đầu với nhau, v.v.). Giờ đây, mỗi màn hình cũng đã có dữ liệu để cho biết đó có phải là màn hình isInternal hay không và liệu đó có phải là isPrimary hay không. Lưu ý rằng màn hình tích hợp không nhất thiết là màn hình chính.

Trường currentScreen là một đối tượng trực tiếp tương ứng với window.screen hiện tại. Đối tượng được cập nhật theo các vị trí cửa sổ trên nhiều màn hình hoặc các thay đổi về thiết bị.

Sự kiện screenschange

Điều duy nhất còn thiếu bây giờ là cách phát hiện khi nào chế độ thiết lập màn hình của tôi thay đổi. Một sự kiện mới, screenschange thực hiện chính xác điều đó: nó sẽ kích hoạt bất cứ khi nào chòm sao trên màn hình được sửa đổi. (Thông báo rằng "màn hình" ở dạng số nhiều trong tên sự kiện.) Điều này có nghĩa là sự kiện sẽ kích hoạt bất cứ khi nào một màn hình mới hoặc một màn hình hiện tại đang được cắm điện hoặc rút phích cắm (thực tế hoặc ảo trong trường hợp là của Sidecar).

Lưu ý rằng bạn cần tra cứu thông tin chi tiết của màn hình mới một cách không đồng bộ, sự kiện screenschange không cung cấp dữ liệu này. Để tra cứu thông tin chi tiết trên màn hình, hãy sử dụng đối tượng trực tiếp từ một bộ nhớ đệm Giao diện Screens.

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

Sự kiện currentscreenchange

Nếu tôi chỉ quan tâm đến các thay đổi đối với màn hình hiện tại (tức là giá trị của đối tượng trực tiếp currentScreen), tôi có thể theo dõi sự kiện currentscreenchange.

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

Sự kiện change

Cuối cùng, nếu chỉ quan tâm đến các thay đổi đối với một màn hình cụ thể, tôi có thể lắng nghe Sự kiện change.

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

Tuỳ chọn toàn màn hình mới

Cho đến nay, bạn có thể yêu cầu hiển thị các phần tử ở chế độ toàn màn hình thông qua requestFullScreen() . Phương thức này sẽ lấy tham số options mà bạn có thể truyền FullscreenOptions. Cho đến nay, tài sản duy nhất là navigationUI. API Quản lý cửa sổ thêm một thuộc tính screen mới cho phép bạn xác định màn hình nào để bắt đầu chế độ xem toàn màn hình. Ví dụ: nếu bạn muốn đặt làm màn hình chính toàn màn hình:

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

Ống polyfill

Không thể polyfill API Quản lý cửa sổ, nhưng bạn có thể chèn hình dạng của nó để bạn có thể lập trình độc quyền dựa trên API mới:

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

Các khía cạnh khác của API, đó là các sự kiện thay đổi màn hình khác nhau và thuộc tính screen của FullscreenOptions, sẽ không bao giờ kích hoạt hoặc tự động bị bỏ qua tương ứng bằng các trình duyệt không hỗ trợ.

Bản minh hoạ

Nếu bạn là bất kỳ ai như tôi, bạn hãy theo dõi chặt chẽ sự phát triển của tiền mã hoá. (Thực ra thì không phải vì tôi yêu hành tinh này, nhưng cho mục đích của bài viết này, giả sử tôi did.) Để theo dõi các loại tiền mã hoá mà tôi sở hữu, tôi đã phát triển một ứng dụng web cho phép tôi quan sát thị trường trong mọi tình huống trong cuộc sống, chẳng hạn như khi tôi nằm trên giường, nơi tôi có thiết lập màn hình đơn.

Màn hình TV khổng lồ ở cuối giường, cho thấy một phần chân của tác giả. Trên màn hình là một quầy giao dịch tiền mã hoá giả mạo.
Thư giãn và xem chợ.

Vấn đề tiền mã hóa đang diễn ra và thị trường có thể trở nên sôi động bất cứ lúc nào. Nếu điều này xảy ra, tôi có thể nhanh chóng hãy di chuyển đến bàn làm việc của tôi tại nơi tôi đã thiết lập nhiều màn hình. Tôi có thể nhấp vào cửa sổ của bất kỳ đơn vị tiền tệ nào và xem nhanh toàn bộ chi tiết ở chế độ xem toàn màn hình trên màn hình đối diện. Dưới đây là ảnh gần đây của tôi đã chụp trong lần tắm máu YCY vừa qua. Nó bắt gặp tôi hoàn toàn mất cảnh giác và để tôi đi đặt tay lên mặt.

Tác giả với đôi tay đặt trên khuôn mặt hoảng loạn, nhìn chằm chằm vào bàn giao dịch tiền mã hoá giả mạo.
Panicky, chứng kiến cảnh tắm máu tại YCY.

Bạn có thể phát với bản minh hoạ được nhúng bên dưới hoặc xem mã nguồn của bản minh hoạ có sự cố.

Tính bảo mật và quyền truy cập

Nhóm Chrome đã thiết kế và triển khai API Quản lý cửa sổ bằng cách sử dụng các phiên bản cốt lõi nguyên tắc xác định trong Kiểm soát quyền truy cập vào các tính năng nền tảng web mạnh mẽ, bao gồm cả quyền kiểm soát của người dùng, tính minh bạch và công thái học. Window Management API hiển thị thông tin mới về các màn hình kết nối với một thiết bị, tăng bề mặt tạo vân tay số của người dùng, đặc biệt là những người có nhiều màn hình được kết nối liên tục với thiết bị của họ. Như một giảm thiểu mối lo ngại về quyền riêng tư này, nên các thuộc tính màn hình bị lộ sẽ được giới hạn ở mức tối thiểu cần thiết cho các trường hợp sử dụng vị trí phổ biến. Cần có sự cho phép của người dùng để các trang web sử dụng được chế độ nhiều màn hình thông tin và đặt cửa sổ lên màn hình khác. Mặc dù Chromium trả về nhãn màn hình chi tiết, trình duyệt có thể trả về các nhãn ít mang tính mô tả hơn (hoặc thậm chí là trống).

Quyền kiểm soát của người dùng

Người dùng có toàn quyền kiểm soát mức độ hiển thị của chế độ thiết lập này. Họ có thể chấp nhận hoặc từ chối và thu hồi quyền đã cấp trước đó thông qua tính năng thông tin trang web trong trình duyệt.

Kiểm soát doanh nghiệp

Người dùng Chrome Enterprise có thể kiểm soát một số khía cạnh của API Quản lý cửa sổ như được nêu trong phần có liên quan của Các nhóm chính sách nguyên tử phần cài đặt.

Sự minh bạch

Thực tế cho thấy quyền sử dụng API Quản lý cửa sổ đã được cấp hay chưa hiển thị trong thông tin trang web của trình duyệt và cũng có thể truy vấn được qua API Quyền.

Khả năng lưu trữ cố định quyền

Trình duyệt vẫn duy trì các quyền được cấp. Quyền này có thể bị thu hồi qua trang web của trình duyệt của bạn.

Phản hồi

Nhóm Chrome muốn biết ý kiến của bạn về trải nghiệm của bạn với API Quản lý cửa sổ.

Cho chúng tôi biết về thiết kế API

Có điều gì về API không hoạt động như bạn mong đợi không? Hoặc có phương thức nào bị thiếu hoặc thuộc tính nào bạn cần để triển khai ý tưởng của mình? Có câu hỏi hoặc nhận xét về bảo mật mẫu?

  • Gửi vấn đề về thông số kỹ thuật trên kho lưu trữ GitHub tương ứng hoặc thêm ý kiến của bạn vào một kho lưu trữ hiện có vấn đề.

Báo cáo sự cố về triển khai

Bạn có phát hiện lỗi trong quá trình triển khai Chrome không? Hay cách triển khai có khác với thông số kỹ thuật không?

  • Báo cáo lỗi tại new.crbug.com. Hãy nhớ cung cấp càng nhiều thông tin chi tiết càng tốt có thể, hướng dẫn đơn giản để tái tạo và nhập Blink>Screen>MultiScreen vào Hộp Thành phần. Glitch rất hữu ích khi chia sẻ các bản dựng lại nhanh chóng và dễ dàng.

Hiện thông tin hỗ trợ về API này

Bạn có dự định sử dụng API Quản lý cửa sổ không? Sự hỗ trợ công khai của bạn sẽ giúp Chrome ưu tiên các tính năng và cho các nhà cung cấp trình duyệt khác biết tầm quan trọng của việc hỗ trợ họ.

  • Chia sẻ cách bạn dự định sử dụng tài khoản này trên chuỗi bài thuyết trình về WICG.
  • Gửi một bài đăng đến @ChromiumDev kèm theo hashtag #WindowManagement và cho chúng tôi biết bạn đang sử dụng ở đâu và như thế nào.
  • Hãy yêu cầu các nhà cung cấp trình duyệt khác triển khai API này.

Các đường liên kết hữu ích

Xác nhận

Chỉnh sửa thông số API Quản lý cửa sổ Victor Costan, Joshua BellMike Wasserman. API này được triển khai bởi Mike WassermanAdrienne Walker. Bài viết này được đánh giá bởi Joe Medley, François Beaufort, và Kayce Basques. Cảm ơn Laura Torrent Puig đã chụp ảnh.