Tuỳ chỉnh lớp phủ điều khiển cửa sổ của thanh tiêu đề PWA của bạn

Sử dụng khu vực thanh tiêu đề bên cạnh các chế độ điều khiển cửa sổ để giúp PWA của bạn trông giống một ứng dụng hơn.

Nếu còn nhớ bài viết của tôi Làm cho PWA cảm giác như một ứng dụng, bạn có thể nhớ cách tôi đề cập đến việc tuỳ chỉnh thanh tiêu đề của ứng dụng làm để tạo trải nghiệm giống ứng dụng hơn. Dưới đây là một ví dụ về giao diện cho thấy ứng dụng Podcasts trên macOS.

Thanh tiêu đề của ứng dụng Podcasts trên macOS cho thấy các nút điều khiển nội dung nghe nhìn và siêu dữ liệu về podcast đang phát.
Thanh tiêu đề tuỳ chỉnh giúp PWA trông giống như một ứng dụng dành riêng cho từng nền tảng.

Giờ đây, có thể bạn sẽ muốn phản đối bằng cách nói rằng Podcasts là một ứng dụng macOS dành riêng cho nền tảng này, không chạy trong trình duyệt và do đó có thể làm những gì nó muốn mà không phải chạy bởi quy tắc. Đúng, nhưng tin tốt là tính năng Lớp phủ chế độ điều khiển cửa sổ, vốn là chủ đề của trong bài viết này, chúng tôi sẽ sớm hỗ trợ bạn tạo giao diện người dùng tương tự cho PWA của bạn.

Thành phần lớp phủ chế độ điều khiển cửa sổ

Lớp phủ điều khiển cửa sổ bao gồm bốn tính năng phụ:

  1. Giá trị "window-controls-overlay" cho trường "display_override" trong tệp kê khai ứng dụng web.
  2. Các biến môi trường CSS titlebar-area-x, titlebar-area-y, titlebar-area-widthtitlebar-area-height.
  3. Tiêu chuẩn hoá thuộc tính CSS thuộc quyền sở hữu riêng trước đây -webkit-app-region dưới dạng Thuộc tính app-region để xác định các vùng có thể kéo trong nội dung web.
  4. Cơ chế truy vấn và xử lý vùng kiểm soát cửa sổ thông qua windowControlsOverlay thành viên của window.navigator.

Lớp phủ chế độ điều khiển cửa sổ là gì

Khu vực thanh tiêu đề là không gian ở bên trái hoặc bên phải của các thành phần điều khiển cửa sổ (tức là các nút để thu nhỏ, phóng to, đóng, v.v.) và thường chứa tiêu đề của ứng dụng. Cửa sổ Lớp phủ chế độ điều khiển giúp các ứng dụng web tiến bộ (PWA) mang lại trải nghiệm giống ứng dụng hơn bằng cách hoán đổi thanh tiêu đề hiện có trên toàn bộ chiều rộng cho lớp phủ nhỏ chứa các nút điều khiển cửa sổ. Điều này cho phép đặt nội dung tuỳ chỉnh vào khu vực mà trước đây là vùng thanh tiêu đề do trình duyệt kiểm soát.

Trạng thái hiện tại

Bước Trạng thái
1. Tạo thông báo giải thích Hoàn tất
2. Tạo bản nháp ban đầu của thông số kỹ thuật Hoàn tất
3. Thu thập ý kiến phản hồi và lặp lại thiết kế Đang tiến hành
4. Bản dùng thử theo nguyên gốc Hoàn thành
5. Ra mắt Hoàn tất (trong Chromium 104)

Cách sử dụng lớp phủ chế độ điều khiển cửa sổ

Thêm window-controls-overlay vào tệp kê khai ứng dụng web

Một ứng dụng web tiến bộ có thể chọn sử dụng lớp phủ kiểm soát cửa sổ bằng cách thêm "window-controls-overlay" là thành phần chính của "display_override" trong tệp kê khai ứng dụng web:

{
  "display_override": ["window-controls-overlay"]
}

Lớp phủ điều khiển cửa sổ sẽ chỉ hiển thị khi tất cả các điều kiện sau được đáp ứng:

  1. Ứng dụng không được mở trong trình duyệt mà mở trong một cửa sổ PWA riêng.
  2. Tệp kê khai bao gồm "display_override": ["window-controls-overlay"]. (Các giá trị khác là được phép sau đó).
  3. PWA đang chạy trên hệ điều hành máy tính.
  4. Nguồn gốc hiện tại khớp với nguồn gốc mà PWA được cài đặt.

Kết quả là một vùng thanh tiêu đề trống với các nút điều khiển cửa sổ thông thường ở bên trái hoặc phải, tuỳ thuộc vào hệ điều hành.

Cửa sổ ứng dụng có thanh tiêu đề trống và các nút điều khiển cửa sổ ở bên trái.
Thanh tiêu đề trống sẵn sàng cho nội dung tuỳ chỉnh.

Đang di chuyển nội dung vào thanh tiêu đề

Giờ đây khi đã có khoảng trống trong thanh tiêu đề, bạn có thể di chuyển nội dung nào đó tới đó. Đối với bài viết này, tôi tạo một ứng dụng web tiến bộ (PWA) Nội dung nổi bật Wikimedia. Một tính năng hữu ích của ứng dụng này là tìm kiếm các từ bằng tiêu đề bài viết. HTML cho tính năng tìm kiếm có dạng như sau:

<div class="search">
  <img src="logo.svg" alt="Wikimedia logo." width="32" height="32" />
  <label>
    <input type="search" />
    Search for words in articles
  </label>
</div>

Để di chuyển div này lên thanh tiêu đề, cần có một số CSS:

.search {
  /* Make sure the `div` stays there, even when scrolling. */
  position: fixed;
  /**
   * Gradient, because why not. Endless opportunities.
   * The gradient ends in `#36c`, which happens to be the app's
   * `<meta name="theme-color" content="#36c">`.
   */
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
  /* Use the environment variable for the left anchoring with a fallback. */
  left: env(titlebar-area-x, 0);
  /* Use the environment variable for the top anchoring with a fallback. */
  top: env(titlebar-area-y, 0);
  /* Use the environment variable for setting the width with a fallback. */
  width: env(titlebar-area-width, 100%);
  /* Use the environment variable for setting the height with a fallback. */
  height: env(titlebar-area-height, 33px);
}

Bạn có thể thấy tác dụng của mã này trong ảnh chụp màn hình bên dưới. Thanh tiêu đề hoàn toàn phản hồi. Thời gian bạn đổi kích thước cửa sổ PWA, thanh tiêu đề sẽ phản ứng như thể nó bao gồm nội dung HTML thông thường, và trên thực tế, nó là như vậy.

Cửa sổ ứng dụng có thanh tìm kiếm trên thanh tiêu đề.
Thanh tiêu đề mới hoạt động và thích ứng.

Xác định các phần của thanh tiêu đề có thể kéo được

Mặc dù ảnh chụp màn hình ở trên cho thấy rằng bạn đã hoàn thành, nhưng bạn vẫn chưa hoàn tất. Cửa sổ PWA đang không còn kéo được (ngoài một khu vực rất nhỏ) vì các nút điều khiển cửa sổ không kéo được và phần còn lại của thanh tiêu đề bao gồm tiện ích tìm kiếm. Khắc phục vấn đề này bằng thuộc tính CSS app-region có giá trị drag. Trong trường hợp cụ thể, bạn có thể mọi thứ ngoài phần tử input có thể kéo được.

/* The entire search `div` is draggable… */
.search {
  -webkit-app-region: drag;
  app-region: drag;
}

/* …except for the `input`. */
input {
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

Với CSS này vào đúng vị trí, người dùng có thể kéo cửa sổ ứng dụng như bình thường bằng cách kéo div, img hoặc label. Chỉ có phần tử input là có tính tương tác nên có thể nhập cụm từ tìm kiếm.

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

Có thể phát hiện khả năng hỗ trợ Lớp phủ điều khiển cửa sổ bằng cách kiểm tra sự tồn tại của windowControlsOverlay:

if ('windowControlsOverlay' in navigator) {
  // Window Controls Overlay is supported.
}

Truy vấn khu vực kiểm soát cửa sổ bằng windowControlsOverlay

Cho đến nay, mã có một vấn đề: trên một số nền tảng, các chế độ điều khiển cửa sổ nằm ở bên phải, trên những người khác, họ ở bên trái. Tệ hơn là "ba dấu chấm" Trình đơn Chrome sẽ thay đổi vị trí dựa trên nền tảng. Điều này có nghĩa là hình nền chuyển màu tuyến tính cần phải được điều chỉnh một cách linh hoạt để chạy từ #131313maroon hoặc maroon#131313maroon, để pha trộn với màu nền maroon của thanh tiêu đề được xác định bằng <meta name="theme-color" content="maroon">. Có thể đạt được điều này bằng cách truy vấn API getTitlebarAreaRect() trên thuộc tính navigator.windowControlsOverlay.

if ('windowControlsOverlay' in navigator) {
  const { x } = navigator.windowControlsOverlay.getTitlebarAreaRect();
  // Window controls are on the right (like on Windows).
  // Chrome menu is left of the window controls.
  // [ windowControlsOverlay___________________ […] [_] [■] [X] ]
  if (x === 0) {
    div.classList.add('search-controls-right');
  }
  // Window controls are on the left (like on macOS).
  // Chrome menu is right of the window controls overlay.
  // [ [X] [_] [■] ___________________windowControlsOverlay [⋮] ]
  else {
    div.classList.add('search-controls-left');
  }
} else {
  // When running in a non-supporting browser tab.
  div.classList.add('search-controls-right');
}

Thay vì đặt hình nền trực tiếp trong các quy tắc CSS của lớp .search (như trước đây), mã được sửa đổi hiện sử dụng hai lớp mà mã ở trên đặt động.

/* For macOS: */
.search-controls-left {
  background-image: linear-gradient(90deg, #36c, 45%, #131313, 90%, #36c);
}

/* For Windows: */
.search-controls-right {
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
}

Xác định xem lớp phủ điều khiển cửa sổ có hiển thị hay không

Không phải trường hợp nào lớp phủ điều khiển cửa sổ cũng xuất hiện trong khu vực thanh tiêu đề. Trong khi tất nhiên sẽ không có trên các trình duyệt không hỗ trợ tính năng Lớp phủ điều khiển cửa sổ, tính năng này cũng sẽ không xuất hiện khi PWA được đề cập chạy trong một thẻ. Để phát hiện tình huống này, bạn có thể truy vấn thuộc tính visible của windowControlsOverlay:

if (navigator.windowControlsOverlay.visible) {
  // The window controls overlay is visible in the title bar area.
}

Ngoài ra, bạn cũng có thể sử dụng truy vấn phương tiện display-mode trong JavaScript và/hoặc CSS:

// Create the query list.
const mediaQueryList = window.matchMedia('(display-mode: window-controls-overlay)');

// Define a callback function for the event listener.
function handleDisplayModeChange(mql) {
  // React on display mode changes.
}

// Run the display mode change handler once.
handleDisplayChange(mediaQueryList);

// Add the callback function as a listener to the query list.
mediaQueryList.addEventListener('change', handleDisplayModeChange);
@media (display-mode: window-controls-overlay) { 
  /* React on display mode changes. */ 
}

Nhận thông báo về các thay đổi về hình học

Chỉ cần truy vấn khu vực lớp phủ kiểm soát cửa sổ bằng getTitlebarAreaRect() là bạn có thể thực hiện một lần những việc như đặt hình nền chính xác dựa vào vị trí của các nút điều khiển cửa sổ, nhưng trong các trường hợp khác, cần kiểm soát chi tiết hơn. Ví dụ: một trường hợp sử dụng có thể là điều chỉnh lớp phủ điều khiển cửa sổ cho phù hợp với không gian có sẵn và để thêm một câu chuyện cười ngay trong cửa sổ kiểm soát lớp phủ khi có đủ dung lượng.

Các tuỳ chọn điều khiển cửa sổ che phủ khu vực trên một cửa sổ hẹp có văn bản được rút ngắn.
Các chế độ điều khiển thanh tiêu đề được điều chỉnh cho phù hợp với một cửa sổ hẹp.

Bạn có thể được thông báo về những thay đổi về hình học bằng cách đăng ký navigator.windowControlsOverlay.ongeometrychange hoặc bằng cách thiết lập trình nghe sự kiện cho Sự kiện geometrychange. Sự kiện này sẽ chỉ kích hoạt khi lớp phủ điều khiển cửa sổ hiển thị, là khi navigator.windowControlsOverlay.visibletrue.

const debounce = (func, wait) => {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
};

if ('windowControlsOverlay' in navigator) {
  navigator.windowControlsOverlay.ongeometrychange = debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250);
}

Thay vì chỉ định một hàm cho ongeometrychange, bạn cũng có thể thêm một trình nghe sự kiện vào windowControlsOverlay như bên dưới. Bạn có thể đọc về sự khác biệt giữa hai công cụ này trên MDN.

navigator.windowControlsOverlay.addEventListener(
  'geometrychange',
  debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250),
);

Khả năng tương thích khi chạy trong một thẻ và trên các trình duyệt không hỗ trợ

Có hai trường hợp có thể xảy ra để xem xét:

  • Trường hợp ứng dụng đang chạy trong trình duyệt hỗ trợ Lớp phủ điều khiển cửa sổ, nhưng nơi ứng dụng được sử dụng trong thẻ trình duyệt.
  • Trường hợp ứng dụng đang chạy trong trình duyệt không hỗ trợ Lớp phủ điều khiển cửa sổ.

Trong cả hai trường hợp, theo mặc định, HTML được xây dựng cho các cửa sổ điều khiển lớp phủ sẽ hiển thị cùng dòng như nội dung HTML thông thường và các biến env() giá trị dự phòng sẽ thúc đẩy vị trí. Trên các trình duyệt hỗ trợ, bạn cũng có thể quyết định không hiển thị HTML được chỉ định cho lớp phủ kiểm soát cửa sổ bằng cách kiểm tra thuộc tính visible của lớp phủ và nếu nó sẽ báo cáo false, sau đó ẩn nội dung HTML đó.

PWA đang chạy trong một thẻ trình duyệt có lớp phủ điều khiển cửa sổ hiển thị trong phần nội dung.
Các thành phần điều khiển dành cho thanh tiêu đề có thể dễ dàng hiển thị trong phần nội dung của các trình duyệt cũ.

Xin nhắc lại, những trình duyệt không hỗ trợ sẽ không xem xét "display_override" thuộc tính tệp kê khai ứng dụng web hoặc không nhận ra "window-controls-overlay", do đó sử dụng giá trị có thể có tiếp theo theo chuỗi dự phòng, ví dụ: "standalone".

PWA chạy ở chế độ độc lập với lớp phủ điều khiển cửa sổ hiển thị ở phần nội dung.
Các thành phần điều khiển dành cho thanh tiêu đề có thể dễ dàng hiển thị trong phần nội dung của các trình duyệt cũ.

Những điểm cần lưu ý về giao diện người dùng

Mặc dù điều này có thể hấp dẫn, nhưng bạn không nên tạo trình đơn thả xuống cổ điển trong khu vực Lớp phủ điều khiển cửa sổ. Làm như vậy sẽ vi phạm hướng dẫn thiết kế cho macOS, nền tảng mà người dùng mong đợi thanh trình đơn (cả nền tảng do hệ thống cung cấp và tuỳ chỉnh) ở đầu màn hình.

Nếu ứng dụng của bạn cung cấp trải nghiệm toàn màn hình, hãy cân nhắc kỹ xem việc đó có hợp lý hay không cho Lớp phủ Điều khiển cửa sổ là một phần của chế độ xem toàn màn hình. Bạn có thể muốn sắp xếp lại bố cục của mình khi onfullscreenchange kích hoạt sự kiện.

Bản minh hoạ

Tôi đã tạo một bản minh hoạ để bạn phát trong đó khác nhau giữa trình duyệt hỗ trợ và không hỗ trợ cũng như ở trạng thái đã cài đặt và chưa được cài đặt. Cho trải nghiệm Lớp phủ chế độ điều khiển cửa sổ thực tế, bạn cần cài đặt ứng dụng này. Bạn có thể thấy hai ảnh chụp màn hình về những việc sẽ xảy ra ở bên dưới. Chiến lược phát hành đĩa đơn mã nguồn của ứng dụng này hiện có trên Glitch.

Ứng dụng minh hoạ Nội dung nổi bật Wikimedia với Lớp phủ điều khiển cửa sổ.
Bạn có thể thử nghiệm bằng ứng dụng minh hoạ.

Tính năng tìm kiếm trong lớp phủ điều khiển cửa sổ có đầy đủ chức năng:

Ứng dụng minh hoạ về Nội dung nổi bật trên Wikimedia với Lớp phủ chế độ điều khiển cửa sổ và chức năng tìm kiếm chủ động cho cụm từ &#39;cleopa...&#39; làm nổi bật một trong các bài viết có cụm từ trùng khớp &quot;Cleopatra&quot;.
Tính năng tìm kiếm bằng Lớp phủ điều khiển cửa sổ.

Lưu ý về bảo mật

Nhóm Chromium đã thiết kế và triển khai API Lớp phủ chế độ điều khiển cửa sổ bằ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 nền tảng web mạnh mẽ, bao gồm cả người dùng khả năng kiểm soát, tính minh bạch và tính công thái học.

Giả mạo

Việc cung cấp cho trang web quyền kiểm soát một phần thanh tiêu đề tạo điều kiện cho nhà phát triển giả mạo nội dung trước đây là một khu vực đáng tin cậy, do trình duyệt kiểm soát. Hiện tại, trong trình duyệt Chromium, các tính năng bao gồm thanh tiêu đề khi khởi chạy ban đầu hiển thị tiêu đề trang web ở bên trái, và nguồn gốc của trang ở bên phải (theo sau là nút "cài đặt và nội dung khác" và cửa sổ ). Sau vài giây, văn bản gốc sẽ biến mất. Nếu trình duyệt được đặt từ phải sang trái (RTL), bố cục này được lật để văn bản gốc nằm ở bên trái. Thao tác này sẽ mở ra các thành phần điều khiển cửa sổ sẽ phủ lên để giả mạo nguồn gốc nếu không có đủ khoảng đệm giữa nguồn gốc và cạnh phải của lớp phủ. Ví dụ: nguồn gốc "evil.ltd" có thể được thêm vào bằng "google.com", khiến người dùng tin rằng nguồn đó đáng tin cậy. Kế hoạch là duy trì để người dùng biết nguồn gốc của ứng dụng và có thể đảm bảo rằng ứng dụng đó khớp với kỳ vọng của họ. Đối với trình duyệt được định cấu hình theo cấu hình RTL, phải có đủ khoảng đệm ở bên phải nguồn gốc để ngăn trang web độc hại thêm nguồn gốc không an toàn đó với một nguồn đáng tin cậy.

Tạo vân tay số

Việc bật lớp phủ điều khiển cửa sổ và các vùng có thể kéo không được đặt ra nhiều mối lo ngại đáng kể về quyền riêng tư ngoài việc phát hiện tính năng. Tuy nhiên, do kích thước và vị trí khác nhau của các nút điều khiển cửa sổ khi hoạt động hệ thống, navigator.windowControlsOverlay.getTitlebarAreaRect() phương thức trả về một DOMRect có vị trí và kích thước tiết lộ thông tin về hệ điều hành dựa trên mà trình duyệt đang chạy. Hiện tại, nhà phát triển đã có thể khám phá hệ điều hành từ chuỗi tác nhân người dùng, nhưng do vấn đề về vân tay số, nên có thảo luận về việc cố định chuỗi UA và hợp nhất các phiên bản hệ điều hành. Có một trong cộng đồng trình duyệt thường xuyên tìm hiểu tần suất kích thước của lớp phủ điều khiển cửa sổ thay đổi trên các nền tảng, khi giả định rằng các giải pháp này khá ổn định trên các phiên bản hệ điều hành và do đó sẽ không sẽ giúp ích cho việc quan sát các phiên bản hệ điều hành nhỏ. Mặc dù đây là một tiềm năng vấn đề về tạo vân tay số, vấn đề này chỉ áp dụng cho những PWA đã cài đặt sử dụng và không áp dụng cho việc sử dụng trình duyệt nói chung. Ngoài ra, Không thể sử dụng API navigator.windowControlsOverlay cho iframe được nhúng bên trong PWA.

Việc chuyển sang một nguồn gốc khác trong PWA sẽ khiến PWA quay về chế độ độc lập thông thường thanh tiêu đề ngay cả khi thanh tiêu đề đáp ứng các tiêu chí nêu trên và được chạy bằng lớp phủ điều khiển cửa sổ. Điều này là để điều chỉnh cho phù hợp với thanh màu đen xuất hiện khi điều hướng đến một nguồn gốc khác. Sau khi quay lại điểm gốc ban đầu, lớp phủ điều khiển cửa sổ sẽ được sử dụng lại.

Một thanh URL màu đen để điều hướng ngoài nguồn gốc.
Một thanh màu đen sẽ xuất hiện khi người dùng di chuyển đến một nguồn gốc khác.

Phản hồi

Nhóm Chromium muốn biết ý kiến của bạn về trải nghiệm của bạn với API Lớp phủ chế độ điều khiển 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? Báo cáo 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 vấn đề hiện tại.

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 Chromium 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ác hướng dẫn đơn giản để tái tạo và nhập UI>Browser>WebAppInstalls vào phần Components (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 Lớp phủ chế độ điều khiển cửa sổ không? Sự hỗ trợ công khai của bạn giúp ích cho nhóm Chromium để ư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 đó.

Gửi tweet tới @ChromiumDev kèm theo #WindowControlsOverlay và cho chúng tôi biết bạn đang sử dụng hashtag đó ở đâu và như thế nào.

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

Xác nhận

Lớp phủ điều khiển cửa sổ đã được triển khai và chỉ định bởi Amanda Baker thuộc nhóm Microsoft Edge. Bài viết này do Joe Medley xem xét và Kenneth Rohde Christiansen. Hình ảnh chính của Sigmund trên Unsplash.