Jelajahi kemampuan browser baru dan yang akan datang untuk PWA: From Fugu With Love

1. Sebelum memulai

Progressive Web Applications (PWA) adalah jenis software aplikasi yang dikirim melalui web, yang dibuat menggunakan teknologi web umum termasuk HTML, CSS, dan JavaScript. Iklan ini ditujukan untuk berfungsi pada platform apa pun yang menggunakan browser yang mematuhi standar.

Dalam codelab ini, Anda akan memulai dengan PWA dasar, lalu mengeksplorasi kemampuan browser baru yang pada akhirnya akan memberikan kekuatan super PWA 🦸.

Banyak dari kemampuan browser baru ini sedang berlangsung dan masih distandarisasi, jadi terkadang Anda perlu menyetel tanda browser untuk menggunakannya.

Prasyarat

Untuk codelab ini, Anda harus memahami JavaScript modern, khususnya promise dan async/await. Karena tidak semua langkah codelab didukung di semua platform, akan membantu jika Anda menguji apakah ada perangkat tambahan di ponsel, misalnya, ponsel Android atau laptop yang menggunakan sistem operasi yang berbeda dengan perangkat yang digunakan untuk mengedit kode. Sebagai alternatif untuk perangkat sungguhan, Anda dapat mencoba menggunakan simulator seperti simulator Android atau layanan online seperti BrowserStack yang memungkinkan Anda menguji dari perangkat saat ini. Selain itu, Anda juga dapat melewati langkah apa pun, keduanya tidak bergantung satu sama lain.

Yang akan Anda build

Anda akan membuat aplikasi web kartu ucapan, dan mempelajari bagaimana kemampuan browser baru dan yang akan datang dapat meningkatkan aplikasi Anda agar dapat memberikan pengalaman lanjutan pada browser tertentu (tetapi tetap berguna di semua browser modern).

Anda akan mempelajari cara menambahkan kemampuan dukungan, seperti akses sistem file, akses papan klip sistem, pengambilan kontak, sinkronisasi latar belakang berkala, penguncian layar saat aktif, fitur berbagi, dan lainnya.

Setelah mengerjakan codelab, Anda akan memiliki pemahaman yang kuat tentang cara meningkatkan aplikasi web Anda secara bertahap dengan fitur browser baru, sambil tidak membebani subset pengguna yang kebetulan berada di browser yang tidak kompatibel, dan yang terpenting, meskipun tidak mengecualikannya dari aplikasi Anda.

Yang Anda butuhkan

Browser yang didukung penuh saat ini adalah:

Sebaiknya Anda menggunakan saluran Dev tertentu.

2. Proyek Fugu

Progressive Web App (PWA) dibuat dan disempurnakan dengan API modern untuk memberikan kemampuan, keandalan, dan kemudahan penginstalan yang ditingkatkan saat menjangkau siapa saja di web, di mana saja di seluruh dunia, menggunakan jenis perangkat apa saja.

Beberapa API ini sangat andal, dan jika ditangani dengan tidak benar, ada berbagai hal yang bisa berjalan dengan tidak benar. Sama seperti ikan fugu 🐡: Ketika Anda memotongnya dengan benar, itu adalah kelezatan, tetapi ketika Anda salah memotongnya, itu bisa mematikan (tetapi jangan khawatir, tidak ada yang bisa rusak dalam codelab ini).

Inilah alasan mengapa nama kode internal dari project Kemampuan Web (yang digunakan perusahaan yang terlibat untuk mengembangkan API baru ini) adalah Project Fugu.

Kemampuan web—kini ini—memungkinkan perusahaan besar dan kecil membangun solusi berbasis browser murni, yang sering kali memungkinkan penerapan yang lebih cepat dengan biaya pengembangan yang lebih rendah dibandingkan dengan rute khusus platform.

3. Mulai

Download salah satu browser, lalu setel tanda runtime berikut 🚩 dengan membuka about://flags, yang berfungsi di Chrome dan Edge:

  • #enable-experimental-web-platform-features

Setelah Anda mengaktifkannya, mulai ulang browser.

Anda akan menggunakan platform Glitch, karena ini memungkinkan Anda menghosting PWA dan karena platform ini memiliki editor yang layak. Glitch juga mendukung impor dan ekspor ke GitHub, sehingga tidak ada penguncian vendor. Buka fugu-paint.glitch.me untuk mencoba aplikasi ini. Ini adalah aplikasi gambar dasar 🎨 yang akan Anda tingkatkan selama codelab.

Fugu Greetings dasar PWA dengan kanvas besar dengan kata “Google” dilukis di atasnya.

Setelah bermain dengan aplikasi, remix aplikasi untuk membuat salinan sendiri yang dapat diedit. URL remix Anda akan terlihat seperti glitch.com/editguides/bouncy-candytuft ("bouncy-candytuft" akan menjadi hal lain untuk Anda). Remix ini dapat diakses secara langsung di seluruh dunia. Login ke akun Anda yang sudah ada atau buat akun baru di Glitch untuk menyimpan tugas. Anda dapat melihat aplikasi dengan mengklik tombol "🕶 Tampilkan" dan URL aplikasi yang dihosting akan terlihat seperti bouncy-candytuft.glitch.me (perhatikan .me, bukan .com sebagai domain level teratas).

Sekarang Anda sudah siap mengedit aplikasi dan meningkatkannya. Setiap kali Anda melakukan perubahan, aplikasi akan dimuat ulang dan perubahan Anda akan langsung terlihat.

IDE Glitch yang menampilkan pengeditan dokumen HTML.

Tugas berikut idealnya harus diselesaikan secara berurutan, tetapi seperti yang disebutkan di atas, Anda selalu dapat melewati langkah jika tidak memiliki akses ke perangkat yang kompatibel. Ingat, setiap tugas ditandai dengan kubectl, ikan air tawar tidak berbahaya, atau 🐡, sebuah "menangani dengan hati-hati" ikan fugu, mengingatkan Anda tentang seberapa eksperimental atau tidaknya suatu fitur.

Periksa Console di DevTools untuk melihat apakah API didukung di perangkat saat ini. Kami juga menggunakan Glitch agar Anda dapat memeriksa aplikasi yang sama di perangkat yang berbeda dengan mudah, misalnya di ponsel dan komputer desktop Anda.

Kompatibilitas API dicatat ke Konsol di DevTools.

4. Systrace Menambahkan Dukungan Web Share API

Membuat gambar paling menakjubkan akan membosankan jika tidak ada yang akan menghargainya. Tambahkan fitur yang memungkinkan pengguna berbagi gambar dengan dunia, dalam bentuk kartu ucapan.

Web Share API mendukung berbagi file, dan seperti yang mungkin Anda ingat, File hanyalah jenis Blob tertentu. Oleh karena itu, dalam file bernama share.mjs, impor tombol berbagi dan fungsi praktis toBlob() yang mengonversi konten kanvas menjadi blob dan menambahkan fungsi berbagi sesuai kode di bawah.

Jika Anda telah menerapkannya, namun tidak melihat tombolnya, hal ini dikarenakan browser tidak menerapkan 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. ⋅ Tambahkan Dukungan API Target Berbagi Web

Kini pengguna dapat membagikan kartu ucapan yang dibuat menggunakan aplikasi, tetapi Anda juga dapat mengizinkan pengguna berbagi gambar ke aplikasi Anda dan mengubahnya menjadi kartu ucapan. Untuk ini, Anda dapat menggunakan Web Share Target API.

Dalam Manifes Aplikasi Web, Anda perlu memberi tahu aplikasi jenis file yang dapat Anda terima dan URL apa yang harus dipanggil oleh browser saat satu atau beberapa file dibagikan. Nukilan di bawah file manifest.webmanifest menunjukkan hal ini.

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

Pekerja layanan kemudian menangani file yang diterima. URL ./share-target/ tidak ada, aplikasi hanya menindaklanjutinya di pengendali fetch dan mengalihkan permintaan ke URL root dengan menambahkan parameter kueri ?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 */

  /* ... */
});

Saat aplikasi dimuat, aplikasi akan memeriksa apakah parameter kueri ini telah disetel, dan jika ya, akan menggambar gambar yang dibagikan ke kanvas dan menghapusnya dari cache. Semua ini terjadi di 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');
  }
};

Fungsi ini kemudian digunakan saat aplikasi melakukan inisialisasi.

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

6. 💡 Menambahkan Dukungan Impor Gambar

Menggambar semuanya dari awal sulit. Menambahkan fitur yang memungkinkan pengguna mengupload gambar lokal dari perangkat mereka ke dalam aplikasi.

Pertama, baca informasi tentang fungsi kanvas drawImage(). Selanjutnya, biasakan diri Anda dengan elemen <​input
type=file>
.

Berbekal pengetahuan ini, Anda dapat mengedit file yang disebut import_image_legacy.mjs dan menambahkan cuplikan berikut. Di bagian atas file, Anda mengimpor tombol impor dan fungsi drawBlob() yang memungkinkan Anda menggambar blob ke kanvas.

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. 🐷 Tambahkan Dukungan Ekspor Gambar

Bagaimana pengguna Anda menyimpan file yang dibuat dalam aplikasi ke perangkat? Secara tradisional, ini telah dicapai dengan elemen <​a
download>
.

Dalam file export_image_legacy.mjs, tambahkan konten seperti berikut di bawah. Impor tombol ekspor dan fungsi praktis toBlob() yang mengonversi konten kanvas menjadi 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 Systrace Menambahkan Dukungan API Akses Sistem File

Berbagi berarti peduli, tetapi pengguna Anda mungkin ingin menyimpan pekerjaan terbaik mereka di perangkat mereka sendiri. Menambahkan fitur yang memungkinkan pengguna menyimpan (dan membuka kembali) gambar mereka.

Sebelumnya, Anda menggunakan pendekatan lama <​input type=file> untuk mengimpor file dan <​a download> pendekatan lama untuk mengekspor file. Sekarang, Anda akan menggunakan File System Access API untuk meningkatkan pengalaman.

API ini memungkinkan untuk membuka dan menyimpan file dari sistem file sistem operasi. Edit kedua file, import_image.mjs dan export_image.mjs, dengan menambahkan konten di bawah. Agar file ini dapat dimuat, hapus 🐡 emoji dari script.mjs.

Ganti baris ini:

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

...dengan baris ini:

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

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

Di 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. Systrace Menambahkan Dukungan API Pemilih Kontak

Pengguna Anda mungkin ingin menambahkan pesan ke kartu ucapan mereka, dan menyapa seseorang secara pribadi. Tambahkan fitur yang memungkinkan pengguna memilih satu (atau beberapa) kontak lokalnya dan menambahkan namanya ke pesan berbagi.

Pada perangkat Android atau iOS, Contact Picker API memungkinkan Anda memilih kontak dari aplikasi pengelola kontak perangkat dan mengembalikannya ke aplikasi. Edit file contacts.mjs dan tambahkan kode di bawah ini.

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. Systrace Menambahkan Dukungan API Clipboard Asinkron

Pengguna mungkin ingin menempelkan gambar dari aplikasi lain ke aplikasi Anda, atau menyalin gambar dari aplikasi Anda ke aplikasi lain. Tambahkan fitur yang memungkinkan pengguna Anda untuk menyalin dan menempelkan gambar ke dalam dan ke luar aplikasi. Async Clipboard API mendukung gambar PNG, jadi sekarang Anda dapat membaca dan menulis data gambar ke papan klip.

Temukan file clipboard.mjs dan tambahkan hal berikut ini:

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. Systrace Menambahkan Dukungan API Badging

Saat pengguna menginstal aplikasi Anda, ikon akan muncul di layar utama mereka. Anda dapat menggunakan ikon ini untuk menyampaikan informasi seru, seperti jumlah sapuan kuas yang diambil gambar tertentu.

Tambahkan fitur yang menghitung badge setiap kali pengguna membuat sapuan kuas baru. Badging API memungkinkan badge numerik ditetapkan pada ikon aplikasi. Anda dapat memperbarui badge setiap kali peristiwa pointerdown terjadi (yaitu, saat sapuan kuas terjadi), dan mereset badge saat kanvas dihapus.

Masukkan kode di bawah ini dalam file 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. 💡 Menambahkan Dukungan Screen Wake Lock API

Terkadang, pengguna Anda mungkin perlu beberapa saat untuk hanya menatap gambar, cukup lama hingga inspirasi datang. Tambahkan fitur yang membuat layar tetap menyala dan mencegah screensaver dimulai. Screen Wake Lock API mencegah layar pengguna tertidur. Penguncian layar saat aktif dilepaskan secara otomatis saat peristiwa perubahan visibilitas seperti yang ditentukan oleh Visibilitas Halaman terjadi. Oleh karena itu, penguncian layar saat aktif harus dibuka kembali saat halaman muncul kembali.

Temukan file wake_lock.mjs, lalu tambahkan konten di bawah. Untuk menguji apakah cara ini berfungsi, konfigurasikan screensaver agar ditampilkan setelah satu menit.

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. Systrace Menambahkan Dukungan API Sinkronisasi Latar Belakang Berkala

Memulai dengan kanvas kosong dapat membosankan. Anda dapat menggunakan Periodic Background Sync API untuk melakukan inisialisasi kanvas pengguna dengan gambar baru setiap hari, misalnya, foto fugu harian Unsplash.

Ini memerlukan dua file, file periodic_background_sync.mjs yang mendaftarkan Sinkronisasi Latar Belakang Berkala dan file image_of_the_day.mjs lainnya yang berhubungan dengan mengunduh gambar hari itu.

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

Di 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. Systrace Menambahkan Dukungan API Deteksi Bentuk

Terkadang pengguna atau gambar latar yang Anda gunakan mungkin berisi informasi yang bermanfaat, misalnya kode batang. Shape Detection API, dan khususnya, Barcode Detection API, memungkinkan Anda mengekstrak informasi ini. Tambahkan fitur yang berupaya mendeteksi kode batang dari gambar pengguna Anda. Cari file barcode.mjs dan tambahkan konten di bawah ini. Untuk menguji fitur ini, cukup muat atau tempel gambar dengan kode batang ke kanvas. Anda dapat menyalin contoh kode batang dari penelusuran gambar untuk kode 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. 🐡 Menambahkan Dukungan Idle Detection API

Jika Anda membayangkan aplikasi berjalan di penyiapan seperti kios, fitur yang berguna adalah mereset kanvas setelah tidak aktif selama beberapa waktu. Idle Detection API memungkinkan Anda mendeteksi saat pengguna tidak lagi berinteraksi dengan perangkat mereka.

Temukan file idle_detection.mjs dan tempelkan konten di bawah ini.

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. 🐡 Menambahkan Dukungan API Penanganan File

Bagaimana jika pengguna hanya dapat menggandakan file gambar dan aplikasi Anda akan muncul? File Handling API memungkinkan Anda melakukannya.

Anda harus mendaftarkan PWA sebagai pengendali file untuk gambar. Hal ini terjadi di Manifes Aplikasi Web, kutipan di bawah file manifest.webmanifest menunjukkan hal ini. (Ini sudah menjadi bagian dari manifes, tidak perlu menambahkannya sendiri.)

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

Untuk benar-benar menangani file yang dibuka, tambahkan kode di bawah ke file 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. Selamat

🎉 Hore, Anda berhasil!

Ada begitu banyak API browser menarik yang dikembangkan dalam konteks Project Fugu 🐡 sehingga codelab ini hampir tidak dapat membahasnya.

Untuk mempelajari lebih dalam, atau sekadar mempelajari lebih lanjut, ikuti publikasi kami di situs web.dev.

Halaman landing dari bagian &ldquo;Capabilities&rdquo; situs di web.dev.

Namun, bukan itu saja. Untuk update yang belum dipublikasikan, Anda dapat mengakses pelacak API Fug kami dengan link ke semua proposal yang telah dikirimkan, sedang dalam uji coba awal atau uji coba pengembangan, semua proposal yang mulai dikerjakan, dan semuanya sedang dipertimbangkan, tetapi belum dimulai.

Situs pelacak API Fugu

Codelab ini ditulis oleh Thomas Steiner (@tomayac), saya senang menjawab pertanyaan Anda dan menantikan masukan Anda. Terima kasih khususnya kepada Hemanth H.M (@GNUmanth), Christian Liebel (@christianliebel), Sven May (@Svenmay), Lars Knudsen (@larsgk), dan Jackie Han (@hanguokai) yang telah membantu membentuk codelab ini.