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 của bạn: Từ Fugu with Love

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

Ứng dụng web tiến bộ (PWA) là loại phần mềm ứng dụng được phân phối 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. Các trò chơi này 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 bằng một ứng dụng web tiến bộ (PWA) cơ bản, sau đó khám phá những chức năng mới của trình duyệt mà cuối cùng sẽ mang lại khả năng mạnh mẽ cho PWA 🦸.

Nhiều chức năng mới của trình duyệt đang được áp dụng và vẫn được chuẩn hóa. Do đó, đôi khi bạn sẽ cần đặt cờ của trình duyệt để sử dụng chúng.

Đ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ụ thể là những lời hứa và không đồng bộ/không chờ đợi. 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 tất cả các nền tảng, nên bạn luôn có thể thử nghiệm nếu có các thiết bị khác, 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ã. Bên cạnh thiết bị thực, bạn có thể thử sử dụng 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 cho phép bạn thử nghiệm từ thiết bị đang dùng. Ngoài ra, bạn cũng có thể chỉ cần bỏ qua bất kỳ bước nào, các bước này không phụ thuộc lẫn nhau.

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

Bạn sẽ xây dựng 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 tới của trình duyệt có thể cải thiện ứng dụng của bạn để có thể cung cấp trải nghiệm nâng cao trên một số trình duyệt nhất định (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 tính 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, tính năng truy xuất danh bạ, tính năng đồng bộ hóa dưới nền định kỳ, tính năng khóa chế độ thức của màn hình, tính năng chia sẻ, v.v.

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

Bạn cần có

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

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

2. Dự án Fugu

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

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

Đây là lý do tại sao tên mã nội bộ của dự án Khả năng trên web (mà 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 web (hiện đã có) 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 đơn giản 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 sử dụng lộ trình dành riêng cho nền tảng.

3. Bắt đầu

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

  • #enable-experimental-web-platform-features

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

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

Fugu chào mừng PWA cơ sở với một canvas lớn có dòng chữ "amp;ldquo;Google” được vẽ trên đó.

Sau khi phát bằng ứng dụng này, 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ư glrick.com/editxxxxxxxxx/bouncy-candytuft ("bouncy-candytuft" sẽ là một URL khác cho 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 một tài khoản mới trên Glrick để 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" và URL của ứng dụng được lưu trữ sẽ có dạng như bouncy-candytuft.glchy.me (lưu ý .me thay vì .com là miền cấp cao nhất).

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ẽ hiển thị trực tiếp.

Tập hợp IDE cho thấy việc chỉnh sửa 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ự, nhưng 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. Hãy nhớ rằng mỗi nhiệm vụ được đánh dấu bằng 🐟, một con cá nước ngọt vô hại hoặc 🐡, một người xử lý cẩn thận & quot; cá nóc, báo cho bạn biết tính năng thử nghiệm có hoạt động hay không.

Hãy kiểm tra Bảng điều khiển trong Công cụ cho nhà phát triển để xem 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 tính năng Glrick để 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.

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

4. 🐟 Thêm hỗ trợ API chia sẻ web

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

API Chia sẻ web hỗ trợ việc chia sẻ tệp. 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à hàm tiện lợi toBlob() để chuyển đổi nội dung của canvas thành một blob và thêm chức năng chia sẻ theo mã dưới đây.

Nếu bạn đã triển khai chức năng này nhưng không thấy nút, đó là vì trình duyệt của bạn không triển khai API Chia sẻ web.

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 hỗ trợ API mục tiêu chia sẻ trên web

Giờ đây, người dùng của bạn có thể chia sẻ thiệp chúc mừng được tạo bằng ứng dụng này. Tuy nhiên, 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 chúng thành thẻ chào. Đối với trường hợp này, bạn có thể sử dụng API Mục tiêu chia sẻ trên web.

Trong Tệp kê khai ứng dụng web, bạn cần cho ứng dụng biết loại tệp bạn có thể chấp nhận và URL mà trình duyệt nên gọi khi một hoặc nhiều tệp được chia sẻ. Phần trích dẫn 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"]
        }
      ]
    }
  }
}

Trình chạy dịch vụ sau đó sẽ xử lý các tệp đã nhận. URL ./share-target/ không thực sự tồn tại, ứng dụng chỉ hoạt động trên trình xử lý fetch và chuyển hướng yêu cầu đến URL gốc bằng cách thêm thông 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 thông số truy vấn này có được đặt hay không và nếu có, hãy vẽ hình ảnh được chia sẻ lên canvas và xóa hình ảnh đó khỏi bộ nhớ đệm. Điều này xảy 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 được dùng khi ứng dụng chạy.

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

6. 🐟 Thêm hỗ trợ hình ảnh được nhập

Việc vẽ mọi thứ từ đầu sẽ khó khăn. 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ị lên ứng dụng của họ.

Trước tiên, hãy đọc tài liệu này trên canvas#39; drawImage(). Tiếp theo, hãy làm quen với phần tử <​input
type=file>
.

Dựa trên kiến thức này, bạn có thể chỉnh sửa tệp có tên là import_image_legacy.mjs và 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 blob vào 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ùy chọn Hỗ trợ hình ảnh xuất

Người dùng của bạn sẽ lưu tệp được tạo trong ứng dụng trên thiết bị của họ bằng cách nào? Thông thường, bạn có thể thực hiện điều này 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à một hàm tiện lợi 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 hỗ trợ API truy cập hệ thống tệp

Việc chia sẻ rất quan trọng, nhưng có thể người dùng của bạn sẽ muốn lưu công việc tốt nhất của mình vào thiết bị của họ. 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 API Truy cập hệ thống tệp để cải thiện trải nghiệm.

API này cho phép mở và lưu tệp từ hệ điều hành của hệ điều hành. Chỉnh sửa hai tệp tương ứng là import_image.mjsexport_image.mjs bằng cách thêm nội dung bên dưới. Để tải những tệp này, hãy xóa 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 hỗ trợ API Bộ chọn địa chỉ liên hệ

Người dùng của bạn có thể muốn thêm tin nhắn vào thiệp chúc mừng và giải quyết cá nhân với 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ệ tại địa phương và thêm tên của họ vào tin nhắn chia sẻ.

Trên thiết bị Android hoặc iOS, API Bộ chọn địa chỉ liên hệ 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ả lại ứng dụng 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 hỗ trợ API bảng nhớ tạm không đồng bộ

Người dùng có thể muốn dán hình ảnh từ một ứng dụng khác vào ứng dụng của bạn hoặc sao chép bản vẽ từ ứng dụng đó vào một ứng dụng khác. Thêm 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. API bảng nhớ tạm không đồng bộ hỗ trợ các hình ảnh PNG, vì vậy, bây giờ 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 và thêm những thông tin 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 hỗ trợ API Badging

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ể sử dụng biểu tượng này để truyền tải thông tin thú vị, chẳng hạn như số lần vẽ vẽ mà một bản vẽ đã cho.

Thêm một tính năng tính huy hiệu bất cứ khi nào người dùng của bạn thực hiện thao tác nhấn phím mới. API huy hiệu cho phép đặ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 xảy ra sự kiện vẽ tay) và đặt lại huy hiệu khi canvas đã được xóa.

Đặ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 màn hình API hỗ trợ khóa chế độ thức

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

Tìm tệp wake_lock.mjs và thêm nội dung bên dưới. Để kiểm tra xem tính năng 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 hỗ trợ API đồng bộ hóa nền định kỳ

Bắt đầu với một canvas trống có thể nhàm chán. Bạn có thể sử dụng API đồng bộ hóa dưới nền định kỳ để khởi chạy người dùng\39; canvas với một hình ảnh mới mỗi ngày, ví dụ: ảnh fugu hằng ngày.

Thao tác này cần có 2 tệp, một tệp periodic_background_sync.mjs đăng ký tính năng Đồng bộ hóa dưới nền định kỳ và một tệp image_of_the_day.mjs khác giúp 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 hỗ trợ API phát hiện hình dạng

Đôi khi, người dùng\39; bản vẽ hoặc hình nền được sử dụng có thể chứa thông tin hữu ích, ví dụ như mã vạch. API Phát hiện hình dạng và cụ thể là API Phát hiện mã vạch 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ừ người dùng\39 của bạn; bản vẽ. Tìm tệp barcode.mjs và 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 hình ảnh có mã vạch vào canvas. Bạn có thể sao chép mã vạch ví dụ từ tìm kiếm hình ảnh 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. 🐡 Hỗ trợ API phát hiện trạng thái rảnh

Nếu bạn tưởng tượng rằng ứng dụng của mình đang chạy trong một chế độ thiết lập giống như kiosk, 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. API Phát hiện trạng thái rảnh cho phép bạn phát hiện người dùng không còn tương tác với thiết bị của họ nữa.

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

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 hỗ trợ API xử lý tệp

Điều gì sẽ xảy ra nếu người dùng chỉ có thể truy xuất một tệp hình ảnh và ứng dụng của bạn sẽ bật lê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. Việc này xảy ra trong tệp kê khai của ứng dụng web, đoạn trích bên dưới tệp manifest.webmanifest cho thấy điều này. (Đây đã là một phần của tệp kê khai, bạn không cần phải tự thêm tệp kê khai đó.)

{
  "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

🎉 Woohoo, bạn đã làm được!

Có rất nhiều API trình duyệt thú vị được phát triển trong bối cảnh Project Fugu 🐡 mà lớp học lập trình này hầu như không thể cào được bề mặt.

Để tìm hiểu kỹ hơn hoặc chỉ cần tìm hiểu thêm, hãy theo dõi các ấn bản của chúng tôi trên trang web web.dev của chúng tôi.

Trang đích của phần &ldquo;Cap lược&rdquo; phần của trang web web.dev.

Nhưng nó không kết thúc ở đó. Để cập nhật chưa công khai, bạn có thể truy cập trình theo dõi API Fuu của chúng tôi với các liên kết tới tất cả các đề xuất đã vận chuyển, đang trong giai đoạn dùng thử nguồn gốc hoặc dùng thử nhà phát triển, tất cả đề xuất mà tác vụ đã bắt đầu và mọi thứ đang được xem xét, nhưng chưa được bắt đầu.

Trang web theo dõi API Fugu

Lớp học lập trình này do Thomas Steiner viết (@tomayac), tôi rất sẵn lòng trả lời câu hỏi của bạn và rất mong đọc được ý kiến phản hồi của bạn! Đặc biệt, chúng tôi gửi lời cảm ơn đến ông Hemanth H.M (@GNUmanth), Cơ Đốc giáo!