Khám phá các tính năng mới và sắp ra mắt của trình duyệt cho PWA: From Fugu With Love

1. Trước khi bắt đầu

Ứng dụng web tiến bộ (PWA) là một loại phần mềm ứng dụng được phân phối thông qua web, được xây dựng bằng các công nghệ web phổ biến, bao gồm HTML, CSS và JavaScript. Chúng được thiết kế để hoạt động trên mọi nền tảng sử dụng trình duyệt tuân thủ tiêu chuẩn.

Trong lớp học lập trình này, bạn sẽ bắt đầu với một PWA cơ bản, sau đó khám phá các chức năng mới của trình duyệt để cuối cùng có thể giúp PWA của bạn có được những siêu năng lực 🦸.

Nhiều tính năng mới của trình duyệt đang được triển khai và vẫn đang được chuẩn hoá, vì vậy, đôi khi bạn sẽ cần đặt cờ trình duyệt để sử dụng các tính năng này.

Điều kiện tiên quyết

Đối với lớp học lập trình này, bạn nên làm quen với JavaScript hiện đại, đặc biệt là các promise và async/await. Vì không phải tất cả các bước của lớp học lập trình đều được hỗ trợ trên mọi nền tảng, nên bạn nên kiểm thử nếu có thêm thiết bị, chẳng hạn như điện thoại Android hoặc máy tính xách tay sử dụng hệ điều hành khác với thiết bị mà bạn chỉnh sửa mã. Ngoài thiết bị thực, bạn có thể thử dùng các trình mô phỏng như trình mô phỏng Android hoặc các dịch vụ trực tuyến như BrowserStack để kiểm thử trên thiết bị hiện tại. Nếu không, bạn cũng có thể bỏ qua bất kỳ bước nào vì các bước này không phụ thuộc vào nhau.

Sản phẩm bạn sẽ tạo ra

Bạn sẽ tạo một ứng dụng web thiệp chúc mừng và tìm hiểu cách các tính năng mới và sắp ra mắt của trình duyệt có thể cải thiện ứng dụng của bạn để mang lại trải nghiệm nâng cao trên một số trình duyệt (nhưng vẫn hữu ích trên tất cả các trình duyệt hiện đại).

Bạn sẽ tìm hiểu cách thêm các chức năng hỗ trợ, chẳng hạn như quyền truy cập vào hệ thống tệp, quyền truy cập vào bảng nhớ tạm của hệ thống, truy xuất danh bạ, đồng bộ hoá định kỳ ở chế độ nền, khoá đánh thức màn hình, các tính năng chia sẻ và nhiều chức năng khác.

Sau khi hoàn thành lớp học lập trình này, bạn sẽ hiểu rõ cách tăng cường dần các ứng dụng web của mình bằng các tính năng mới của trình duyệt mà không gây gánh nặng tải xuống cho một nhóm nhỏ người dùng đang sử dụng trình duyệt không tương thích, và quan trọng nhất là không loại trừ họ khỏi ứng dụng của bạn ngay từ đầu.

Bạn cần có

Các trình duyệt được hỗ trợ đầy đủ hiện tại là:

Bạn nên sử dụng Kênh nhà phát triển cụ thể.

2. Project Fugu

Ứng dụng web tiến bộ (PWA) được xây dựng và nâng cao bằng các API hiện đại để mang lại khả năng, độ tin cậy và khả năng cài đặt nâng cao, đồng thời tiếp cận mọi người trên web, ở mọi nơi trên thế giới, bằng mọi loại thiết bị.

Một số API trong số này rất mạnh mẽ và nếu xử lý không đúng cách, mọi thứ có thể trở nên tồi tệ. Giống như cá nóc 🐡: Khi bạn cắt đúng cách, đó là một món ngon, nhưng khi bạn cắt sai cách, nó có thể gây tử vong (nhưng đừng lo lắng, không có gì thực sự có thể bị hỏng trong lớp học lập trình này).

Đó là lý do tên mã nội bộ của dự án Web Capabilities (các công ty liên quan đang phát triển những API mới này) là Project Fugu.

Các chức năng trên web (hiện tại) cho phép các doanh nghiệp lớn và nhỏ xây dựng dựa trên các giải pháp thuần tuý dựa trên trình duyệt, thường cho phép triển khai nhanh hơn với chi phí phát triển thấp hơn so với việc đi theo hướng dành riêng cho nền tảng.

3. Bắt đầu

Tải một trong hai trình duyệt này xuống, sau đó đặt cờ thời gian chạy 🚩 sau đây bằng cách chuyển đến about://flags. Cờ này hoạt động trong cả Chrome và Edge:

  • #enable-experimental-web-platform-features

Sau khi bật, hãy khởi động lại trình duyệt.

Bạn sẽ sử dụng nền tảng Glitch vì nền tảng này cho phép bạn lưu trữ PWA và có một trình chỉnh sửa phù hợp. Glitch cũng hỗ trợ nhập và xuất sang GitHub, nên bạn không bị phụ thuộc vào một nhà cung cấp. Truy cập vào fugu-paint.glitch.me để dùng thử ứng dụng. Đây là một ứng dụng vẽ cơ bản 🎨 mà bạn sẽ cải thiện trong lớp học lập trình.

PWA cơ bản Fugu Greetings có một khung vẽ lớn với từ "Google" được vẽ trên đó.

Sau khi dùng thử ứng dụng, hãy phối lại ứng dụng đó để tạo bản sao của riêng bạn mà bạn có thể chỉnh sửa. URL của bản phối lại sẽ có dạng như glitch.com/edit/#!/bouncy-candytuft ("bouncy-candytuft" sẽ là một tên khác đối với bạn). Bản phối lại này có thể truy cập trực tiếp trên toàn thế giới. Đăng nhập vào tài khoản hiện có hoặc tạo tài khoản mới trên Glitch để lưu công việc của bạn. Bạn có thể xem ứng dụng của mình bằng cách nhấp vào nút "🕶 Show" (Hiện), và URL của ứng dụng được lưu trữ sẽ có dạng như bouncy-candytuft.glitch.me (lưu ý rằng miền cấp cao nhất là .me chứ không phải .com).

Bây giờ, bạn đã sẵn sàng chỉnh sửa và cải thiện ứng dụng của mình. Bất cứ khi nào bạn thực hiện thay đổi, ứng dụng sẽ tải lại và các thay đổi của bạn sẽ xuất hiện ngay lập tức.

Glitch IDE cho thấy quá trình chỉnh sửa một tài liệu HTML.

Tốt nhất là bạn nên hoàn thành các việc sau theo thứ tự. Tuy nhiên, như đã lưu ý ở trên, bạn luôn có thể bỏ qua một bước nếu không có quyền truy cập vào thiết bị tương thích. Xin lưu ý rằng mỗi tác vụ đều được đánh dấu bằng 🐟 (một loài cá nước ngọt vô hại) hoặc 🐡 (một loài cá nóc "cần xử lý cẩn thận"), cho bạn biết mức độ thử nghiệm của một tính năng.

Kiểm tra Bảng điều khiển trong DevTools để xem một API có được hỗ trợ trên thiết bị hiện tại hay không. Chúng tôi cũng sử dụng Glitch để bạn có thể dễ dàng kiểm tra cùng một ứng dụng trên nhiều thiết bị, chẳng hạn như trên điện thoại di động và máy tính để bàn.

Khả năng tương thích của API được ghi vào Bảng điều khiển trong Công cụ cho nhà phát triển.

4. 🐟 Thêm tính năng hỗ trợ Web Share API

Việc tạo ra những bức vẽ tuyệt vời nhất sẽ trở nên nhàm chán nếu không có ai đánh giá cao chúng. Thêm một tính năng cho phép người dùng chia sẻ bản vẽ của họ với mọi người dưới dạng thiệp chúc mừng.

Web Share API hỗ trợ chia sẻ tệp và như bạn có thể nhớ, File chỉ là một loại Blob cụ thể. Do đó, trong tệp có tên share.mjs, hãy nhập nút chia sẻ và một hàm tiện lợi toBlob() chuyển đổi nội dung của canvas thành một blob, đồng thời thêm chức năng chia sẻ theo mã bên dưới.

Nếu bạn đã triển khai tính năng này nhưng không thấy nút, thì đó là do trình duyệt của bạn không triển khai Web Share API.

import { shareButton, toBlob } from './script.mjs';  const share = async (title, text, blob) => {   const data = {     files: [       new File([blob], 'fugu-greeting.png', {         type: blob.type,       }),     ],     title: title,     text: text,   };   try {     if (!navigator.canShare(data)) {       throw new Error("Can't share data.", data);     }     await navigator.share(data);   } catch (err) {     console.error(err.name, err.message);   } };  shareButton.style.display = 'block'; shareButton.addEventListener('click', async () => {   return share('Fugu Greetings', 'From Fugu With Love', await toBlob()); }); 

5. 🐟 Thêm tính năng hỗ trợ Web Share Target API

Giờ đây, người dùng có thể chia sẻ thiệp chúc mừng được tạo bằng ứng dụng của bạn, nhưng bạn cũng có thể cho phép người dùng chia sẻ hình ảnh với ứng dụng của bạn và biến những hình ảnh đó thành thiệp chúc mừng. Để làm việc này, bạn có thể sử dụng Web Share Target API.

Trong Tệp kê khai ứng dụng web, bạn cần cho ứng dụng biết loại tệp mà bạn có thể chấp nhận và URL mà trình duyệt sẽ gọi khi một hoặc nhiều tệp được chia sẻ. Đoạn trích bên dưới của tệp manifest.webmanifest cho thấy điều này.

{   "share_target": {     "action": "./share-target/",     "method": "POST",     "enctype": "multipart/form-data",     "params": {       "files": [         {           "name": "image",           "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]         }       ]     }   } } 

Sau đó, service worker sẽ xử lý các tệp đã nhận. URL ./share-target/ thực sự không tồn tại, ứng dụng chỉ hoạt động trên URL đó trong trình xử lý fetch và chuyển hướng yêu cầu đến URL gốc bằng cách thêm một tham số truy vấn ?share-target:

self.addEventListener('fetch', (fetchEvent) => {   /* 🐡 Start Web Share Target */   if (     fetchEvent.request.url.endsWith('/share-target/') &&     fetchEvent.request.method === 'POST'   ) {     return fetchEvent.respondWith(       (async () => {         const formData = await fetchEvent.request.formData();         const image = formData.get('image');         const keys = await caches.keys();         const mediaCache = await caches.open(           keys.filter((key) => key.startsWith('media'))[0],         );         await mediaCache.put('shared-image', new Response(image));         return Response.redirect('./?share-target', 303);       })(),     );   }   /* 🐡 End Web Share Target */    /* ... */ }); 

Khi tải, ứng dụng sẽ kiểm tra xem tham số truy vấn này có được đặt hay không. Nếu có, ứng dụng sẽ vẽ hình ảnh được chia sẻ lên canvas và xoá hình ảnh đó khỏi bộ nhớ đệm. Tất cả những điều này diễn ra trong script.mjs:

const restoreImageFromShare = async () => {   const mediaCache = await getMediaCache();   const image = await mediaCache.match('shared-image');   if (image) {     const blob = await image.blob();     await drawBlob(blob);     await mediaCache.delete('shared-image');   } }; 

Sau đó, hàm này sẽ được dùng khi ứng dụng khởi động.

if (location.search.includes('share-target')) {   restoreImageFromShare(); } else {   drawDefaultImage(); } 

6. 🐟 Thêm tính năng hỗ trợ nhập hình ảnh

Việc vẽ mọi thứ từ đầu là rất khó. Thêm một tính năng cho phép người dùng tải hình ảnh trên thiết bị của họ lên ứng dụng.

Trước tiên, hãy đọc về hàm drawImage() của canvas. Tiếp theo, hãy làm quen với phần tử <​input
type=file>
.

Khi đã nắm được thông tin này, bạn có thể chỉnh sửa tệp có tên là import_image_legacy.mjs rồi thêm đoạn mã sau. Ở đầu tệp, bạn nhập nút nhập và một hàm tiện lợi drawBlob() cho phép bạn vẽ một đốm màu lên canvas.

import { importButton, drawBlob } from './script.mjs';  const importImage = async () => {   return new Promise((resolve) => {     const input = document.createElement('input');     input.type = 'file';     input.accept = 'image/png, image/jpeg, image/*';     input.addEventListener('change', () => {       const file = input.files[0];       input.remove();       return resolve(file);     });     input.click();   }); };  importButton.style.display = 'block'; importButton.addEventListener('click', async () => {   const file = await importImage();   if (file) {     await drawBlob(file);   } }); 

7. 🐟 Thêm tính năng hỗ trợ xuất hình ảnh

Người dùng sẽ lưu tệp được tạo trong ứng dụng vào thiết bị của họ như thế nào? Theo truyền thống, điều này được thực hiện bằng phần tử <​a
download>
.

Trong tệp export_image_legacy.mjs, hãy thêm nội dung như sau. Nhập nút xuất và hàm tiện ích toBlob() chuyển đổi nội dung canvas thành một blob.

import { exportButton, toBlob } from './script.mjs';  export const exportImage = async (blob) => {   const a = document.createElement('a');   a.download = 'fugu-greeting.png';   a.href = URL.createObjectURL(blob);   a.addEventListener('click', (e) => {     a.remove();     setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);   });   setTimeout(() => a.click(), 0); };  exportButton.style.display = 'block'; exportButton.addEventListener('click', async () => {   exportImage(await toBlob()); }); 

8. 🐟 Thêm chế độ hỗ trợ API Truy cập hệ thống tệp

Chia sẻ là quan tâm, nhưng người dùng có thể muốn lưu những tác phẩm đẹp nhất của họ vào thiết bị riêng. Thêm một tính năng cho phép người dùng lưu (và mở lại) bản vẽ của họ.

Trước đây, bạn đã sử dụng phương pháp cũ <​input type=file> để nhập tệp và phương pháp cũ <​a download> để xuất tệp. Giờ đây, bạn sẽ sử dụng File System Access API để cải thiện trải nghiệm.

API này cho phép mở và lưu các tệp trong hệ thống tệp của hệ điều hành. Chỉnh sửa hai tệp import_image.mjsexport_image.mjs bằng cách thêm nội dung bên dưới. Để tải các tệp này, hãy xoá biểu tượng cảm xúc 🐡 khỏi script.mjs.

Thay thế dòng này:

// Remove all the emojis for this feature test to succeed. if ('show🐡Open🐡File🐡Picker' in window) {   /* ... */ } 

...với dòng này:

if ('showOpenFilePicker' in window) {   /* ... */ } 

Trong import_image.mjs:

import { importButton, drawBlob } from './script.mjs';  const importImage = async () => {   try {     const [handle] = await window.showOpenFilePicker({       types: [         {           description: 'Image files',           accept: {             'image/*': ['.png', '.jpg', '.jpeg', '.avif', '.webp', '.svg'],           },         },       ],     });     return await handle.getFile();   } catch (err) {     console.error(err.name, err.message);   } };  importButton.style.display = 'block'; importButton.addEventListener('click', async () => {   const file = await importImage();   if (file) {     await drawBlob(file);   } }); 

Trong export_image.mjs:

import { exportButton, toBlob } from './script.mjs';  const exportImage = async () => {   try {     const handle = await window.showSaveFilePicker({       suggestedName: 'fugu-greetings.png',       types: [         {           description: 'Image file',           accept: {             'image/png': ['.png'],           },         },       ],     });     const blob = await toBlob();     const writable = await handle.createWritable();     await writable.write(blob);     await writable.close();   } catch (err) {     console.error(err.name, err.message);   } };  exportButton.style.display = 'block'; exportButton.addEventListener('click', async () => {   await exportImage(); }); 

9. 🐟 Thêm tính năng hỗ trợ API Bộ chọn danh bạ

Người dùng có thể muốn thêm thông điệp vào thiệp chúc mừng và gửi lời chúc đến một người nào đó. Thêm một tính năng cho phép người dùng chọn một (hoặc nhiều) người liên hệ trong danh bạ và thêm tên của họ vào tin nhắn chia sẻ.

Trên thiết bị Android hoặc iOS, Contact Picker API cho phép bạn chọn người liên hệ trong ứng dụng trình quản lý danh bạ của thiết bị và trả về cho ứng dụng. Chỉnh sửa tệp contacts.mjs và thêm mã bên dưới.

import { contactsButton, ctx, canvas } from './script.mjs';  const getContacts = async () => {   const properties = ['name'];   const options = { multiple: true };   try {     return await navigator.contacts.select(properties, options);   } catch (err) {     console.error(err.name, err.message);   } };  contactsButton.style.display = 'block'; contactsButton.addEventListener('click', async () => {   const contacts = await getContacts();   if (contacts) {     ctx.font = '1em Comic Sans MS';     contacts.forEach((contact, index) => {       ctx.fillText(contact.name.join(), 20, 16 * ++index, canvas.width);     });   } }); 

10. 🐟 Thêm tính năng hỗ trợ API Bảng nhớ tạm không đồng bộ

Người dùng có thể muốn dán một bức ảnh từ ứng dụng khác vào ứng dụng của bạn hoặc sao chép một bản vẽ từ ứng dụng của bạn vào một ứng dụng khác. Hãy thêm một tính năng cho phép người dùng sao chép và dán hình ảnh vào và ra khỏi ứng dụng của bạn. Async Clipboard API hỗ trợ hình ảnh PNG, vì vậy, giờ đây bạn có thể đọc và ghi dữ liệu hình ảnh vào bảng nhớ tạm.

Tìm tệp clipboard.mjs rồi thêm nội dung sau:

import { copyButton, pasteButton, toBlob, drawImage } from './script.mjs';  const copy = async (blob) => {   try {     await navigator.clipboard.write([       /* global ClipboardItem */       new ClipboardItem({         [blob.type]: blob,       }),     ]);   } catch (err) {     console.error(err.name, err.message);   } };  const paste = async () => {   try {     const clipboardItems = await navigator.clipboard.read();     for (const clipboardItem of clipboardItems) {       try {         for (const type of clipboardItem.types) {           const blob = await clipboardItem.getType(type);           return blob;         }       } catch (err) {         console.error(err.name, err.message);       }     }   } catch (err) {     console.error(err.name, err.message);   } };  copyButton.style.display = 'block'; copyButton.addEventListener('click', async () => {   await copy(await toBlob()); });  pasteButton.style.display = 'block'; pasteButton.addEventListener('click', async () => {   const image = new Image();   image.addEventListener('load', () => {     drawImage(image);   });   image.src = URL.createObjectURL(await paste()); }); 

11. 🐟 Thêm tính năng hỗ trợ Badging API

Khi người dùng cài đặt ứng dụng của bạn, một biểu tượng sẽ xuất hiện trên màn hình chính của họ. Bạn có thể dùng biểu tượng này để truyền tải thông tin thú vị, chẳng hạn như số nét vẽ mà một bản vẽ nhất định đã thực hiện.

Thêm một tính năng để tăng số lượng huy hiệu bất cứ khi nào người dùng thực hiện một nét vẽ mới. Badging API cho phép bạn đặt huy hiệu dạng số trên biểu tượng ứng dụng. Bạn có thể cập nhật huy hiệu bất cứ khi nào sự kiện pointerdown xảy ra (tức là khi một nét vẽ xuất hiện) và đặt lại huy hiệu khi xoá canvas.

Đặt mã bên dưới vào tệp badge.mjs:

import { canvas, clearButton } from './script.mjs';  let strokes = 0;  canvas.addEventListener('pointerdown', () => {   navigator.setAppBadge(++strokes); });  clearButton.addEventListener('click', () => {   strokes = 0;   navigator.setAppBadge(strokes); }); 

12. 🐟 Thêm tính năng hỗ trợ Screen Wake Lock API

Đôi khi, người dùng cần vài phút chỉ để nhìn chằm chằm vào một bức vẽ, đủ lâu để có được cảm hứng. Thêm một tính năng giúp màn hình luôn bật và ngăn trình bảo vệ màn hình kích hoạt. Screen Wake Lock API ngăn màn hình của người dùng chuyển sang chế độ ngủ. Khoá đánh thức sẽ tự động được giải phóng khi xảy ra sự kiện thay đổi chế độ hiển thị theo định nghĩa của Chế độ hiển thị trang. Do đó, bạn phải lấy lại khoá đánh thức khi trang xuất hiện trở lại.

Tìm tệp wake_lock.mjs rồi thêm nội dung bên dưới. Để kiểm tra xem chế độ này có hoạt động hay không, hãy định cấu hình trình bảo vệ màn hình để hiển thị sau một phút.

import { wakeLockInput, wakeLockLabel } from './script.mjs';  let wakeLock = null;  const requestWakeLock = async () => {   try {     wakeLock = await navigator.wakeLock.request('screen');     wakeLock.addEventListener('release', () => {       console.log('Wake Lock was released');     });     console.log('Wake Lock is active');   } catch (err) {     console.error(err.name, err.message);   } };  const handleVisibilityChange = () => {   if (wakeLock !== null && document.visibilityState === 'visible') {     requestWakeLock();   } };  document.addEventListener('visibilitychange', handleVisibilityChange);  wakeLockInput.style.display = 'block'; wakeLockLabel.style.display = 'block'; wakeLockInput.addEventListener('change', async () => {   if (wakeLockInput.checked) {     await requestWakeLock();   } else {     wakeLock.release();   } }); 

13. 🐟 Thêm tính năng hỗ trợ API Định kỳ đồng bộ hoá trong nền

Bắt đầu bằng một canvas trống có thể sẽ nhàm chán. Bạn có thể sử dụng Periodic Background Sync API để khởi động canvas của người dùng bằng một hình ảnh mới mỗi ngày, ví dụ: ảnh cá nóc hằng ngày của Unsplash.

Việc này yêu cầu 2 tệp, một tệp periodic_background_sync.mjs đăng ký tính năng Đồng bộ hoá định kỳ ở chế độ nền và một tệp image_of_the_day.mjs khác xử lý việc tải hình ảnh trong ngày xuống.

Trong periodic_background_sync.mjs:

import { periodicBackgroundSyncButton, drawBlob } from './script.mjs';  const getPermission = async () => {   const status = await navigator.permissions.query({     name: 'periodic-background-sync',   });   return status.state === 'granted'; };  const registerPeriodicBackgroundSync = async () => {   const registration = await navigator.serviceWorker.ready;   try {     registration.periodicSync.register('image-of-the-day-sync', {       // An interval of one day.       minInterval: 24 * 60 * 60 * 1000,     });   } catch (err) {     console.error(err.name, err.message);   } };  navigator.serviceWorker.addEventListener('message', async (event) => {   const fakeURL = event.data.image;   const mediaCache = await getMediaCache();   const response = await mediaCache.match(fakeURL);   drawBlob(await response.blob()); });  const getMediaCache = async () => {   const keys = await caches.keys();   return await caches.open(keys.filter((key) => key.startsWith('media'))[0]); };  periodicBackgroundSyncButton.style.display = 'block'; periodicBackgroundSyncButton.addEventListener('click', async () => {   if (await getPermission()) {     await registerPeriodicBackgroundSync();   }   const mediaCache = await getMediaCache();   let blob = await mediaCache.match('./assets/background.jpg');   if (!blob) {     blob = await mediaCache.match('./assets/fugu_greeting_card.jpg');   }   drawBlob(await blob.blob()); }); 

Trong image_of_the_day.mjs:

const getImageOfTheDay = async () => {   try {     const fishes = ['blowfish', 'pufferfish', 'fugu'];     const fish = fishes[Math.floor(fishes.length * Math.random())];     const response = await fetch(`https://source.unsplash.com/daily?${fish}`);     if (!response.ok) {       throw new Error('Response was', response.status, response.statusText);     }     return await response.blob();   } catch (err) {     console.error(err.name, err.message);   } };  const getMediaCache = async () => {   const keys = await caches.keys();   return await caches.open(keys.filter((key) => key.startsWith('media'))[0]); };  self.addEventListener('periodicsync', (syncEvent) => {   if (syncEvent.tag === 'image-of-the-day-sync') {     syncEvent.waitUntil(       (async () => {         try {           const blob = await getImageOfTheDay();           const mediaCache = await getMediaCache();           const fakeURL = './assets/background.jpg';           await mediaCache.put(fakeURL, new Response(blob));           const clients = await self.clients.matchAll();           clients.forEach((client) => {             client.postMessage({               image: fakeURL,             });           });         } catch (err) {           console.error(err.name, err.message);         }       })(),     );   } }); 

14. 🐟 Thêm tính năng hỗ trợ API Phát hiện hình dạng

Đôi khi, bản vẽ của người dùng hoặc hình nền được sử dụng có thể chứa thông tin hữu ích, chẳng hạn như mã vạch. Shape Detection API (cụ thể là Barcode Detection API) cho phép bạn trích xuất thông tin này. Thêm một tính năng cố gắng phát hiện mã vạch từ bản vẽ của người dùng. Tìm tệp barcode.mjs rồi thêm nội dung bên dưới. Để thử nghiệm tính năng này, bạn chỉ cần tải hoặc dán một hình ảnh có mã vạch lên canvas. Bạn có thể sao chép mã vạch mẫu từ một hình ảnh tìm kiếm mã QR.

/* global BarcodeDetector */ import {   scanButton,   clearButton,   canvas,   ctx,   CANVAS_BACKGROUND,   CANVAS_COLOR,   floor, } from './script.mjs';  const barcodeDetector = new BarcodeDetector();  const detectBarcodes = async (canvas) => {   return await barcodeDetector.detect(canvas); };  scanButton.style.display = 'block'; let seenBarcodes = []; clearButton.addEventListener('click', () => {   seenBarcodes = []; }); scanButton.addEventListener('click', async () => {   const barcodes = await detectBarcodes(canvas);   if (barcodes.length) {     barcodes.forEach((barcode) => {       const rawValue = barcode.rawValue;       if (seenBarcodes.includes(rawValue)) {         return;       }       seenBarcodes.push(rawValue);       ctx.font = '1em Comic Sans MS';       ctx.textAlign = 'center';       ctx.fillStyle = CANVAS_BACKGROUND;       const boundingBox = barcode.boundingBox;       const left = boundingBox.left;       const top = boundingBox.top;       const height = boundingBox.height;       const oneThirdHeight = floor(height / 3);       const width = boundingBox.width;       ctx.fillRect(left, top + oneThirdHeight, width, oneThirdHeight);       ctx.fillStyle = CANVAS_COLOR;       ctx.fillText(         rawValue,         left + floor(width / 2),         top + floor(height / 2),         width,       );     });   } }); 

15. 🐡 Thêm tính năng hỗ trợ API Phát hiện trạng thái rảnh

Nếu bạn hình dung ứng dụng của mình đang chạy trong một chế độ thiết lập giống như ki-ốt, thì một tính năng hữu ích sẽ là đặt lại canvas sau một khoảng thời gian không hoạt động nhất định. Idle Detection API (API Phát hiện trạng thái không hoạt động) cho phép bạn phát hiện thời điểm người dùng không còn tương tác với thiết bị của họ.

Tìm tệp idle_detection.mjs rồi dán nội dung bên dưới vào.

import { ephemeralInput, ephemeralLabel, clearCanvas } from './script.mjs';  let controller;  ephemeralInput.style.display = 'block'; ephemeralLabel.style.display = 'block';  ephemeralInput.addEventListener('change', async () => {   if (ephemeralInput.checked) {     const state = await IdleDetector.requestPermission();     if (state !== 'granted') {       ephemeralInput.checked = false;       return alert('Idle detection permission must be granted!');     }     try {       controller = new AbortController();       const idleDetector = new IdleDetector();       idleDetector.addEventListener('change', (e) => {         const { userState, screenState } = e.target;         console.log(`idle change: ${userState}, ${screenState}`);         if (userState === 'idle') {           clearCanvas();         }       });       idleDetector.start({         threshold: 60000,         signal: controller.signal,       });     } catch (err) {       console.error(err.name, err.message);     }   } else {     console.log('Idle detection stopped.');     controller.abort();   } }); 

16. 🐡 Thêm tính năng hỗ trợ API Xử lý tệp

Điều gì sẽ xảy ra nếu người dùng chỉ cần nhấp đúp vào một tệp hình ảnh và ứng dụng của bạn sẽ xuất hiện? API Xử lý tệp cho phép bạn làm việc đó.

Bạn cần đăng ký PWA làm trình xử lý tệp cho hình ảnh. Điều này xảy ra trong Tệp kê khai ứng dụng web, đoạn trích bên dưới của tệp manifest.webmanifest cho thấy điều này. (Quyền này đã có trong tệp kê khai, bạn không cần tự thêm.)

{   "file_handlers": [     {       "action": "./",       "accept": {         "image/*": [".jpg", ".jpeg", ".png", ".webp", ".svg"]       }     }   ] } 

Để thực sự xử lý các tệp đã mở, hãy thêm mã bên dưới vào tệp file-handling.mjs:

import { drawBlob } from './script.mjs';  const handleLaunchFiles = () => {   window.launchQueue.setConsumer((launchParams) => {     if (!launchParams.files.length) {       return;     }     launchParams.files.forEach(async (handle) => {       const file = await handle.getFile();       drawBlob(file);     });   }); };  handleLaunchFiles(); 

17. Xin chúc mừng

🎉 Chúc mừng bạn!

Có rất nhiều API trình duyệt thú vị đang được phát triển trong bối cảnh Dự án Fugu 🐡 đến nỗi lớp học lập trình này khó có thể đề cập hết.

Để tìm hiểu sâu hơn hoặc chỉ để biết thêm thông tin, hãy theo dõi các bài viết của chúng tôi trên trang web web.dev.

Trang đích của phần &quot;Chức năng&quot; trên trang web web.dev.

Nhưng đó chưa phải là tất cả. Đối với những nội dung cập nhật chưa được công bố, bạn có thể truy cập vào trình theo dõi Fugu API của chúng tôi. Trình theo dõi này có các đường liên kết đến tất cả đề xuất đã được triển khai, đang trong giai đoạn dùng thử nguồn gốc hoặc dùng thử cho nhà phát triển, tất cả đề xuất đã bắt đầu triển khai và mọi đề xuất đang được cân nhắc nhưng chưa bắt đầu.

Trang web theo dõi Fugu API

Lớp học lập trình này được viết bởi Thomas Steiner (@tomayac). Tôi rất vui khi được trả lời các câu hỏi của bạn và mong nhận được ý kiến phản hồi của bạn! Xin chân thành cảm ơn Hemanth H.M (@GNUmanth), Christian Liebel (@christianliebel), Sven May (@Svenmay), Lars Knudsen (@larsgk) và Jackie Han (@hanguokai) vì đã giúp định hình lớp học lập trình này!