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

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

Window Management API

Window Management API (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 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:

  • Các trình chỉnh sửa đồ hoạ nhiều cửa sổ như Gimp có thể đặt nhiều công cụ chỉnh sửa trong các cửa sổ được định vị chính xác.
  • Bàn giao dịch ảo có thể cho thấy xu hướng thị trường trong nhiều cửa sổ và bạn có thể xem bất kỳ cửa sổ nào trong số đó ở chế độ toàn màn hình.
  • Các ứ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 bên trong và bản trình bày trên máy chiếu bên ngoài.

Cách sử dụng Window Management API

Vấn đề

Phương pháp kiểm soát các cửa sổ đã được kiểm chứng theo thời gian, Window.open(), không nhận biết được các 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ổ điển, chẳng hạn như tham số windowFeatures DOMString, nhưng API này vẫn hoạt động hiệu quả trong nhiều năm qua. Để chỉ định vị trí của cửa sổ, bạn có thể truyền toạ độ dưới dạng lefttop (hoặc screenXscreenY tương ứng) rồi truyền kích thước mong muốn dưới dạng widthheight (hoặc innerWidthinnerHeight tương ứng). Ví dụ: để mở cửa sổ 400×300 ở vị trí cách lề trái 50 pixel và cách lề trên cùng 50 pixel, bạn có thể dùng mã sau:

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. Thuộc tính này trả về một đối tượng Screen. Đây là đầu ra 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 nghi với thực tế công việc mới và thiết lập văn phòng tại nhà cho riêng mình. Thiết bị của tôi trông như trong ảnh bên dưới (nếu quan tâm, bạn có thể đọc thông tin chi tiết về chế độ thiết lập của tôi). Chiếc iPad bên cạnh chiếc MacBook của tôi được kết nối với máy tính xách tay qua tính năng Sidecar. Vì vậy, bất cứ khi nào cần, tôi có thể nhanh chóng biến chiếc iPad thành màn hình thứ hai.

Ghế dài trong trường học đặt trên hai chiếc ghế. Trên chiếc ghế dài ở trường là những hộp đựng 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 trong mã mẫu ở trên vào màn hình thứ hai. Tôi làm như sau:

popup.moveTo(2500, 50);

Đây chỉ là phỏng đoán sơ bộ 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ỉ bao gồm màn hình tích hợp, chứ không bao gồm màn hình iPad. width được báo cáo của màn hình tích hợp là 1680 pixel, vì vậy, việc chuyển sang 2500 pixel có thể giúp chuyển cửa sổ sang iPad, vì tôi biết rằng cửa sổ này nằm ở bên phải MacBook của tôi. Làm cách nào để thực hiện việc này trong trường hợp chung? Hoá ra, có một cách hay hơn là đoán. Đó là Window Management API.

Phát hiện đối tượng

Để kiểm tra xem Window Management API 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 Window Management API, tôi phải xin phép người dùng để làm như vậy. Có thể truy vấn quyền window-management bằng Permissions API như sau:

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

Trong khi sử dụng các trình duyệt có tên quyền cũ và mới, hãy nhớ sử dụng mã phòng thủ khi yêu cầu quyền, như trong ví dụ bên dưới.

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 hiển thị lời nhắc cấp quyền một cách linh động trong lần đầu tiên người dùng cố gắng sử dụng bất kỳ phương thức nào của API mới. Hãy đọc tiếp để tìm hiểu thêm.

Thuộc tính window.screen.isExtended

Để biết 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 vào thuộc tính window.screen.isExtended. Phương thức này trả về true hoặc false. Đối với chế độ thiết lập của tôi, phương thức này trả về true.

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

Phương thức getScreenDetails()

Giờ đây, khi biết rằng chế độ thiết lập hiện tại là chế độ nhiều màn hình, tôi có thể lấy thêm thông tin về màn hình thứ hai bằng cách sử dụng Window.getScreenDetails(). Khi gọi hàm này, một lời nhắc cấp quyền sẽ xuất hiện để hỏi xem 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 promise phân giải bằng đối tượng ScreenDetailed. Trên MacBook Pro 13 của tôi có kết nối với iPad, điều này bao gồm một trường screens có hai đố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 được kết nối có trong mảng screens. Lưu ý cách giá trị của left cho iPad bắt đầu từ 1680, chính xác là width của màn hình tích hợp. Điều này cho phép tôi xác định chính xác cách sắp xếp các màn hình một cách hợp lý (cạnh nhau, trên đầu nhau, v.v.). Hiện cũng có dữ liệu cho từng màn hình để cho biết màn hình đó có phải là màn hình isInternal hay không và có phải là màn hình isPrimary hay không. Xin lưu ý rằng màn hình tích hợp không nhất thiết phải 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 này được cập nhật khi có thay đổi về vị trí cửa sổ trên nhiều màn hình hoặc thiết bị.

Sự kiện screenschange

Điều duy nhất còn thiếu hiện tại là cách phát hiện thời điểm 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 đó: sự kiện này kích hoạt bất cứ khi nào chùm màn hình được sửa đổi. (Lưu ý rằng "screens" là 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àn hình hiện có được cắm (về mặt vật lý hoặc ảo trong trường hợp Sidecar) hoặc rút phích cắm.

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

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 chỉ quan tâm đến những 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 những thay đổi đối với một màn hình cụ thể, tôi có thể theo dõi sự kiện change của màn hình đó.

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

Các lựa chọn mới về chế độ toàn màn hình

Cho đến nay, bạn có thể yêu cầu các phần tử hiển thị ở chế độ toàn màn hình thông qua phương thức requestFullScreen() có tên phù hợp. Phương thức này lấy một tham số options mà bạn có thể truyền FullscreenOptions. Cho đến nay, thuộc tính duy nhất của nó là navigationUI. Window Management API (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 để bắt đầu chế độ xem toàn màn hình. Ví dụ: nếu bạn muốn đặt màn hình chính ở chế độ 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);
}

Polyfill

Bạn không thể polyfill Window Management API, nhưng có thể shim hình dạng của API này để chỉ có thể mã hoá theo 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, tức 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 bị các trình duyệt không hỗ trợ bỏ qua một cách âm thầm.

Bản minh hoạ

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

Màn hình TV lớn ở cuối giường, chân của tác giả xuất hiện một phần. Trên màn hình là một bàn giao dịch tiền mã hoá giả.
Thư giãn và theo dõi thị trường.

Vì đây là thị trường tiền mã hoá, nên 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 chuyển sang bàn làm việc có chế độ 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ỳ loại tiền tệ nào và nhanh chóng xem toàn bộ thông tin chi tiết ở chế độ toàn màn hình trên màn hình đối diện. Dưới đây là bức ảnh gần đây của tôi được chụp trong cuộc tắm máu YCY gần đây nhất. Tôi hoàn toàn không ngờ đến chuyện này và chỉ biết lấy tay che mặt.

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

Bạn có thể dùng thử bản minh hoạ được nhúng bên dưới hoặc xem mã nguồn của bản minh hoạ trên GitHub.

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

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

Sự minh bạch

Việc quyền sử dụng Window Management API đã được cấp hay chưa sẽ xuất hiện trong thông tin trang web của trình duyệt và cũng có thể truy vấn thông qua Permissions API.

Quyền có tác dụng lâu dài

Trình duyệt duy trì các quyền đã cấp. Bạn có thể thu hồi quyền này thông qua thông tin trang web của trình duyệt.

Phản hồi

Nhóm Chrome muốn biết ý kiến của bạn về trải nghiệm khi sử dụng Window Management API.

Hãy 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 hay thuộc tính nào bị thiếu mà bạn cần triển khai ý tưởng của mình không? Bạn có câu hỏi hoặc bình luận về mô hình bảo mật?

  • Báo cáo vấn đề về quy cách trên kho lưu trữ GitHub tương ứng hoặc thêm ý kiến của bạn vào một vấn đề hiện có.

Báo cáo vấn đề về việc triển khai

Bạn có phát hiện thấy lỗi trong quá trình triển khai của Chrome không? Hoặc việc triển khai có khác với quy cách không?

  • Báo cáo lỗi tại new.crbug.com. Nhớ cung cấp càng nhiều thông tin chi tiết càng tốt, hướng dẫn đơn giản để tái hiện và nhập Blink>Screen>MultiScreen vào hộp Thành phần.

Thể hiện sự ủng hộ đối với API

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

  • Chia sẻ cách bạn dự định sử dụng tính năng này trên chuỗi thảo luận WICG Discourse.
  • Gửi một tweet đến @ChromiumDev bằng thẻ bắt đầu bằng #WindowManagement và cho chúng tôi biết bạn đang sử dụng tính năng này ở đâu và như thế nào.
  • Yêu cầu các nhà cung cấp trình duyệt khác triển khai API này.

Đường liên kết hữu ích

Lời cảm ơn

Quy cách Window Management API (API Quản lý cửa sổ) do Victor Costan, Joshua BellMike Wasserman chỉnh sửa. API này được triển khai bởi Mike WassermanAdrienne Walker. Bài viết này được Joe Medley, François BeaufortKayce Basques xem xét. Cảm ơn Laura Torrent Puig đã cung cấp ảnh.