قابلیت‌های مرورگر جدید و آینده را برای PWA خود کاوش کنید: From Fugu With Love

1. قبل از شروع

برنامه های کاربردی وب پیشرو (PWA) نوعی نرم افزار کاربردی هستند که از طریق وب ارائه می شوند و با استفاده از فناوری های رایج وب از جمله HTML، CSS و جاوا اسکریپت ساخته می شوند. آنها برای کار بر روی هر پلتفرمی که از یک مرورگر مطابق با استانداردها استفاده می کند در نظر گرفته شده است.

در این کد، شما با یک PWA پایه شروع می‌کنید و سپس قابلیت‌های جدید مرورگر را بررسی می‌کنید که در نهایت به PWA شما قدرت فوق‌العاده می‌دهد.

بسیاری از این قابلیت‌های مرورگر جدید در حال پرواز هستند و هنوز استاندارد می‌شوند، بنابراین گاهی اوقات برای استفاده از آنها باید پرچم‌های مرورگر را تنظیم کنید.

پیش نیازها

برای این کد لبه، شما باید با جاوا اسکریپت مدرن، به طور خاص وعده ها و async/wait آشنا باشید. از آنجایی که همه مراحل Codelab در همه پلتفرم‌ها پشتیبانی نمی‌شوند، برای آزمایش اینکه آیا دستگاه‌های دیگری در دسترس دارید، به عنوان مثال، یک تلفن Android یا یک لپ‌تاپ که از سیستم عاملی متفاوت از دستگاهی که کد را در آن ویرایش می‌کنید استفاده می‌کند، کمک می‌کند. جایگزین دستگاه های واقعی، می توانید سعی کنید از شبیه سازهایی مانند شبیه ساز اندروید یا سرویس های آنلاین مانند BrowserStack استفاده کنید که به شما امکان می دهد از دستگاه فعلی خود تست کنید. در غیر این صورت، شما همچنین می توانید فقط از هر مرحله بگذرید، آنها به یکدیگر وابسته نیستند.

چیزی که خواهی ساخت

شما یک برنامه وب کارت تبریک می‌سازید و یاد می‌گیرید که چگونه قابلیت‌های مرورگر جدید و آینده می‌توانند برنامه شما را ارتقا دهند تا تجربه‌ای پیشرفته در مرورگرهای خاص ارائه دهد (اما در همه مرورگرهای مدرن مفید باقی می‌ماند).

یاد خواهید گرفت که چگونه قابلیت‌های پشتیبانی مانند دسترسی به سیستم فایل، دسترسی به کلیپ بورد سیستم، بازیابی مخاطبین، همگام‌سازی دوره‌ای پس‌زمینه، قفل بیدار شدن صفحه، ویژگی‌های اشتراک‌گذاری و غیره را اضافه کنید.

پس از کار بر روی کد لبه، درک کاملی از نحوه بهبود تدریجی برنامه های وب خود با ویژگی های جدید مرورگر خواهید داشت، در حالی که بار دانلودی را بر روی زیرمجموعه ای از کاربران خود که اتفاقاً روی مرورگرهای ناسازگار هستند، وارد نمی کنید، و از همه مهمتر ، در حالی که در وهله اول آنها را از برنامه خود مستثنی نمی کنید.

آنچه شما نیاز دارید

مرورگرهایی که در این زمان کاملاً پشتیبانی می شوند عبارتند از:

استفاده از کانال Dev خاص توصیه می شود.

2. پروژه فوگو

برنامه‌های وب پیشرو (PWA) با APIهای مدرن ساخته و تقویت می‌شوند تا قابلیت‌ها، قابلیت اطمینان و نصب‌پذیری پیشرفته‌تری را ارائه دهند، در حالی که با استفاده از هر نوع دستگاهی، به هر کسی در وب، در هر نقطه از جهان دسترسی پیدا می‌کنند.

برخی از این APIها بسیار قدرتمند هستند و در صورت استفاده نادرست، ممکن است مشکلاتی پیش بیاید. درست مثل ماهی فوگو 🐡: وقتی آن را به درستی برش می دهید، یک غذای لذیذ است، اما وقتی آن را اشتباه برش می دهید، می تواند کشنده باشد (اما نگران نباشید، در واقع هیچ چیز در این آزمایشگاه کد شکسته نمی شود).

به همین دلیل است که نام رمز داخلی پروژه قابلیت های وب (که شرکت های درگیر در حال توسعه این API های جدید در آن هستند) Project Fugu است.

قابلیت‌های وب - در حال حاضر - به شرکت‌های بزرگ و کوچک اجازه می‌دهد راه‌حل‌های مبتنی بر مرورگر خالص را بسازند، که اغلب امکان استقرار سریع‌تر با هزینه‌های توسعه کمتر را در مقایسه با رفتن به مسیر خاص پلتفرم فراهم می‌کند.

3. شروع کنید

هر یک از مرورگرها را دانلود کنید، و سپس با پیمایش به about://flags ، پرچم زمان اجرا زیر را تنظیم کنید، که هم در Chrome و هم در Edge کار می‌کند:

  • #enable-experimental-web-platform-features

بعد از اینکه آن را فعال کردید، مرورگر خود را مجددا راه اندازی کنید.

شما از پلتفرم Glitch استفاده خواهید کرد، زیرا به شما امکان می دهد PWA خود را میزبانی کنید و ویرایشگر مناسبی دارد. Glitch همچنین از واردات و صادرات به GitHub پشتیبانی می کند، بنابراین هیچ فروشنده ای وجود ندارد. برای آزمایش برنامه به fugu-paint.glitch.me بروید . این یک برنامه طراحی اولیه است 🎨 که در طول برنامه کد آن را بهبود خواهید داد.

Fugu Greetings خط پایه PWA با یک بوم بزرگ با کلمه “Google” روی آن نقاشی شده است.

پس از بازی با برنامه، برنامه را مجدداً مخلوط کنید تا نسخه خود را ایجاد کنید که می توانید آن را ویرایش کنید. URL ریمیکس شما چیزی شبیه به glitch.com/edit/#!/bouncy-candytuft خواهد بود ("bouncy-candytuft" چیز دیگری برای شما خواهد بود). این ریمیکس به طور مستقیم در سراسر جهان در دسترس است. برای ذخیره کار خود وارد حساب کاربری فعلی خود شوید یا یک حساب کاربری جدید در Glitch ایجاد کنید. با .com کردن .me دکمه "

اکنون آماده ویرایش برنامه خود و بهبود آن هستید. هر زمان که تغییراتی ایجاد می کنید، برنامه مجدداً بارگیری می شود و تغییرات شما مستقیماً قابل مشاهده خواهد بود.

نقص IDE که ویرایش یک سند HTML را نشان می دهد.

کارهای زیر در حالت ایده آل باید به ترتیب تکمیل شوند، اما همانطور که در بالا ذکر شد، اگر به یک دستگاه سازگار دسترسی ندارید، همیشه می توانید یک مرحله را نادیده بگیرید. به یاد داشته باشید، هر کار با 🐟، یک ماهی آب شیرین بی ضرر، یا 🐡، یک ماهی فوگو "با احتیاط کنترل کنید" مشخص شده است، که به شما هشدار می دهد که چقدر یک ویژگی آزمایشی است یا نه.

کنسول موجود در DevTools را بررسی کنید تا ببینید آیا API در دستگاه حاضر پشتیبانی می‌شود یا خیر. ما همچنین از Glitch استفاده می کنیم تا بتوانید همان برنامه را در دستگاه های مختلف به راحتی بررسی کنید، به عنوان مثال در تلفن همراه و رایانه رومیزی خود.

سازگاری API در DevTools به کنسول وارد شده است.

4. 🐟 پشتیبانی Web Share API را اضافه کنید

خلق شگفت‌انگیزترین نقاشی‌ها خسته‌کننده است، اگر کسی نباشد که از آنها قدردانی کند. قابلیتی اضافه کنید که به کاربران شما امکان می دهد نقاشی های خود را به صورت کارت تبریک با جهان به اشتراک بگذارند.

Web Share API از اشتراک‌گذاری فایل‌ها پشتیبانی می‌کند، و همانطور که ممکن است به خاطر داشته باشید، یک File فقط نوع خاصی از Blob است. بنابراین، در فایلی به نام share.mjs ، دکمه اشتراک گذاری و یک تابع راحت را به toBlob() وارد کنید که محتویات یک بوم را به یک حباب تبدیل می کند و عملکرد اشتراک گذاری را مطابق کد زیر اضافه کنید.

اگر این را اجرا کرده اید اما دکمه را نمی بینید، به این دلیل است که مرورگر شما 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. 🐟 پشتیبانی Web Share Target API را اضافه کنید

اکنون کاربران شما می توانند کارت های تبریک ساخته شده با استفاده از برنامه را به اشتراک بگذارند، اما شما همچنین می توانید به کاربران اجازه دهید تصاویر را در برنامه شما به اشتراک بگذارند و آنها را به کارت تبریک تبدیل کنند. برای این کار، می‌توانید از Web Share Target API استفاده کنید.

در مانیفست برنامه کاربردی وب، باید به برنامه بگویید که چه نوع فایل‌هایی را می‌توانید بپذیرید و وقتی یک یا چند فایل به اشتراک گذاشته می‌شود، مرورگر چه URL را باید فراخوانی کند. گزیده زیر از فایل manifest.webmanifest این را نشان می دهد.

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

سپس سرویس‌کار با فایل‌های دریافتی سروکار دارد. URL ./share-target/ در واقع وجود ندارد، برنامه فقط بر روی آن در کنترل کننده fetch عمل می کند و با افزودن یک پارامتر پرس و جو ?share-target ، درخواست را به URL ریشه هدایت می کند:

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 */

  /* ... */
});

هنگامی که برنامه بارگیری می شود، بررسی می کند که آیا این پارامتر پرس و جو تنظیم شده است یا خیر، و در این صورت، تصویر به اشتراک گذاشته شده را روی بوم می کشد و آن را از حافظه پنهان حذف می کند. همه اینها در 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');
  }
};

این تابع پس از شروع اولیه برنامه استفاده می شود.

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

6. 🐟 پشتیبانی تصویر وارداتی را اضافه کنید

کشیدن همه چیز از ابتدا سخت است. یک ویژگی اضافه کنید که به کاربران خود اجازه می دهد یک تصویر محلی را از دستگاه خود در برنامه آپلود کنند.

ابتدا تابع drawImage() canvas را بخوانید. بعد، خودتان را با <​input
type=file>
آشنا کنید <​input
type=file>
عنصر.

با داشتن این دانش، می توانید فایلی به نام import_image_legacy.mjs را ویرایش کرده و قطعه زیر را اضافه کنید. در بالای فایل، دکمه import و یک تابع راحت drawBlob() را وارد می‌کنید که به شما امکان می‌دهد یک حباب را روی بوم بکشید.

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. 🐟 پشتیبانی تصویر صادراتی را اضافه کنید

کاربر شما چگونه فایل ایجاد شده در برنامه را در دستگاه خود ذخیره می کند؟ به طور سنتی، این با <​a
download>
دست می‌آید <​a
download>
عنصر.

در فایل export_image_legacy.mjs محتویات را به صورت زیر اضافه کنید. دکمه صادرات و یک تابع راحت toBlob() را وارد کنید که محتویات بوم را به یک حباب تبدیل می کند.

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. 🐟 پشتیبانی API دسترسی به فایل سیستم را اضافه کنید

اشتراک گذاری اهمیت دارد، اما کاربران شما احتمالاً می خواهند بهترین کار خود را در دستگاه خود ذخیره کنند. یک ویژگی اضافه کنید که به کاربران شما امکان می دهد نقاشی های خود را ذخیره کنند (و دوباره باز کنند).

قبل از این، شما از رویکرد قدیمی <​input type=file> برای وارد کردن فایل‌ها و از رویکرد قدیمی <​a download> برای صادرات فایل‌ها استفاده می‌کردید. اکنون، از File System Access API برای بهبود تجربه استفاده خواهید کرد.

این API امکان باز کردن و ذخیره فایل ها از سیستم فایل سیستم عامل را فراهم می کند. دو فایل import_image.mjs و export_image.mjs را به ترتیب با افزودن مطالب زیر ویرایش کنید. برای بارگیری این فایل‌ها، شکلک‌های 🐡 را از script.mjs حذف کنید.

این خط را جایگزین کنید:

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

... با این خط:

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

در 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);
  }
});

در 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. 🐟 اضافه کردن Contacts Picker API پشتیبانی

کاربران شما ممکن است بخواهند پیامی را به کارت تبریک خود اضافه کنند و شخصاً به شخصی خطاب کنند. یک ویژگی اضافه کنید که به کاربران شما امکان می دهد یک (یا چند) از مخاطبین محلی خود را انتخاب کنند و نام آنها را به پیام اشتراک گذاری اضافه کنند.

در دستگاه‌های Android یا iOS، Contact Picker API به شما امکان می‌دهد مخاطبین را از برنامه مدیریت مخاطبین دستگاه انتخاب کرده و به برنامه برگردانید. فایل contacts.mjs را ویرایش کنید و کد زیر را اضافه کنید.

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. 🐟 پشتیبانی از API کلیپ بورد Async را اضافه کنید

کاربران شما ممکن است بخواهند تصویری را از یک برنامه دیگر در برنامه شما بچسبانند، یا یک طراحی را از برنامه شما در برنامه دیگری کپی کنند. قابلیتی اضافه کنید که به کاربران شما امکان می دهد تصاویر را در داخل و خارج از برنامه شما کپی و جایگذاری کنند. Async Clipboard API از تصاویر PNG پشتیبانی می کند، بنابراین اکنون می توانید داده های تصویر را در کلیپ بورد بخوانید و بنویسید.

فایل clipboard.mjs را پیدا کنید و موارد زیر را اضافه کنید:

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. 🐟 پشتیبانی Badging API را اضافه کنید

هنگامی که کاربران برنامه شما را نصب می کنند، یک نماد در صفحه اصلی آنها ظاهر می شود. می‌توانید از این نماد برای انتقال اطلاعات سرگرم‌کننده، مانند تعداد ضربه‌های قلم‌مو که یک نقاشی معین گرفته است، استفاده کنید.

یک ویژگی اضافه کنید که هر زمان که کاربر یک قلم موی جدید انجام می دهد، نشان را به حساب می آورد. Badging API اجازه می دهد تا یک نشان عددی روی نماد برنامه تنظیم شود. می‌توانید هر زمان که یک رویداد رو به پایین رخ می‌دهد، نشان را به‌روزرسانی کنید (یعنی زمانی که pointerdown رخ می‌دهد)، و وقتی بوم پاک شد، نشان را بازنشانی کنید.

کد زیر را در فایل 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. 🐟 اضافه کردن Screen Wake Lock API پشتیبانی

گاهی اوقات، کاربران شما ممکن است به چند لحظه نیاز داشته باشند تا فقط به یک نقاشی خیره شوند، به اندازه کافی برای الهام گرفتن. قابلیتی را اضافه کنید که صفحه را بیدار نگه می دارد و مانع از ورود محافظ صفحه می شود. Screen Wake Lock API از به خواب رفتن صفحه نمایش کاربر جلوگیری می کند. هنگامی که یک رویداد تغییر دید همانطور که توسط صفحه Visibility تعریف شده است رخ دهد، wake lock به طور خودکار آزاد می شود. بنابراین هنگامی که صفحه به نمایش در می‌آید، wake lock باید مجدداً بدست آید.

فایل wake_lock.mjs را پیدا کنید و محتویات زیر را اضافه کنید. برای آزمایش اینکه آیا این کار می کند، محافظ صفحه نمایش خود را پیکربندی کنید تا بعد از یک دقیقه نمایش داده شود.

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. 🐟 پشتیبانی Periodic Background Sync API را اضافه کنید

شروع با یک بوم خالی می تواند خسته کننده باشد. می‌توانید از Periodic Background Sync API برای مقداردهی اولیه بوم کاربران خود با یک تصویر جدید هر روز، به عنوان مثال، عکس روزانه فوگو Unsplash استفاده کنید.

این به دو فایل نیاز دارد، یک فایل periodic_background_sync.mjs که همگام سازی پس زمینه دوره ای را ثبت می کند و فایل دیگری image_of_the_day.mjs که به دانلود تصویر روز می پردازد.

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

در 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. 🐟 پشتیبانی API تشخیص شکل را اضافه کنید

گاهی اوقات نقاشی های کاربران یا تصاویر پس زمینه استفاده شده ممکن است حاوی اطلاعات مفیدی مانند بارکد باشد. Shape Detection API ، و به طور خاص، API تشخیص بارکد، به شما امکان می دهد این اطلاعات را استخراج کنید. یک ویژگی اضافه کنید که تلاش می کند بارکدها را از نقاشی های کاربران شما شناسایی کند. فایل barcode.mjs را پیدا کرده و محتویات زیر را اضافه کنید. برای آزمایش این ویژگی، کافی است یک تصویر را با بارکد روی بوم بارگذاری یا جایگذاری کنید. می توانید یک نمونه بارکد را از جستجوی تصویر برای کدهای 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. 🐡 اضافه کردن Idle Detection API پشتیبانی

اگر تصور می‌کنید برنامه شما در تنظیمات کیوسک مانند اجرا می‌شود، یک ویژگی مفید این است که بوم را پس از مقدار مشخصی عدم فعالیت، بازنشانی کنید. Idle Detection API به شما امکان می دهد تشخیص دهید که کاربر دیگر با دستگاه خود تعامل ندارد.

فایل idle_detection.mjs را پیدا کنید و در محتویات زیر پیست کنید.

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. 🐡 پشتیبانی API Handling File را اضافه کنید

اگر کاربران شما فقط می‌توانستند روی یک فایل تصویری دوبار کلیک کنند و برنامه شما ظاهر می‌شد، چه؟ File Handling API به شما این امکان را می دهد که این کار را انجام دهید.

شما باید PWA را به عنوان یک کنترل کننده فایل برای تصاویر ثبت کنید. این در مانیفست برنامه کاربردی وب اتفاق می افتد، گزیده زیر از فایل manifest.webmanifest این را نشان می دهد. (این قبلاً بخشی از مانیفست است، نیازی نیست خودتان آن را اضافه کنید.)

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

برای مدیریت واقعی فایل های باز شده، کد زیر را به فایل 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. تبریک می گویم

🎉 ووهو ، شما آن را ساختید!

APIهای مرورگر هیجان انگیز زیادی در زمینه پروژه Fugu در حال توسعه هستند.

برای غواصی عمیق تر، یا فقط برای کسب اطلاعات بیشتر، انتشارات ما را در وب سایت ما دنبال کنید web.dev .

صفحه فرود &ldquo;قابلیت ها&rdquo; بخش وب سایت web.dev.

اما به همین جا ختم نمی شود. برای به‌روزرسانی‌هایی که هنوز منتشر نشده‌اند، می‌توانید به ردیاب Fugu API ما با پیوندهایی به همه پیشنهادهایی که ارسال شده‌اند، در نسخه آزمایشی اولیه یا آزمایشی توسعه‌دهنده هستند، همه پیشنهادهایی که کار روی آنها شروع شده است، و همه چیزهایی که در حال بررسی هستند، اما هنوز شروع نشده‌اند، دسترسی داشته باشید.

وب سایت ردیاب Fugu API

این نرم افزار کد توسط توماس اشتاینر ( @tomayac ) نوشته شده است، من خوشحالم که به سوالات شما پاسخ می دهم و مشتاقانه منتظر خواندن نظرات شما هستم! با تشکر ویژه از Hemanth HM ( @GNUmanth )، کریستین لیبل ( @christianliebel )، سون می ( @Svenmay )، لارس نادسن ( @larsgk )، و جکی هان ( @hanguokai ) که به شکل‌گیری این نرم‌افزار کمک کرده‌اند!