1. Hinweis
Progressive Web-Apps (PWAs) sind eine Art von Anwendungssoftware, die über das Web bereitgestellt wird und mit gängigen Webtechnologien wie HTML, CSS und JavaScript erstellt wird. Sie sind für alle Plattformen gedacht, auf denen ein standardkonformer Browser verwendet wird.
In diesem Codelab beginnen Sie mit einer einfachen PWA und sehen sich dann neue Browserfunktionen an, die Ihrer PWA Superkräfte 🦸 verleihen.
Viele dieser neuen Browserfunktionen sind noch in der Entwicklung und werden noch standardisiert. Daher müssen Sie manchmal Browser-Flags festlegen, um sie verwenden zu können.
Vorbereitung
Für dieses Codelab sollten Sie mit modernem JavaScript vertraut sein, insbesondere mit Promises und async/await. Da nicht alle Schritte des Codelabs auf allen Plattformen unterstützt werden, ist es hilfreich, wenn Sie zum Testen zusätzliche Geräte zur Verfügung haben, z. B. ein Android-Smartphone oder einen Laptop mit einem anderen Betriebssystem als das Gerät, auf dem Sie den Code bearbeiten. Als Alternative zu echten Geräten können Sie Simulatoren wie den Android-Simulator oder Onlinedienste wie BrowserStack verwenden, mit denen Sie von Ihrem aktuellen Gerät aus testen können. Andernfalls können Sie auch einfach einen beliebigen Schritt überspringen, da die Schritte nicht voneinander abhängig sind.
Aufgaben
Sie erstellen eine Web-App für Grußkarten und erfahren, wie neue und zukünftige Browserfunktionen Ihre App verbessern können, sodass sie in bestimmten Browsern eine erweiterte Nutzerfreundlichkeit bietet, aber in allen modernen Browsern nützlich bleibt.
Sie erfahren, wie Sie Unterstützung für Funktionen wie Dateisystemzugriff, Zugriff auf die Systemzwischenablage, Abrufen von Kontakten, regelmäßige Hintergrundsynchronisierung, Aktivierungssperre für den Bildschirm und Freigabefunktionen hinzufügen.
Nachdem Sie das Codelab durchgearbeitet haben, wissen Sie, wie Sie Ihre Web-Apps mit neuen Browserfunktionen verbessern können, ohne dass Nutzer mit inkompatiblen Browsern zusätzliche Downloads durchführen müssen. Vor allem aber werden sie nicht von Ihrer App ausgeschlossen.
Voraussetzungen
Derzeit werden folgende Browser vollständig unterstützt:
Es wird empfohlen, den jeweiligen Dev-Channel zu verwenden.
2. Project Fugu
Progressive Web-Apps (PWAs) werden mit modernen APIs entwickelt und optimiert, um mehr Funktionen, Zuverlässigkeit und Installierbarkeit zu bieten. Damit können Sie alle Nutzer im Web, überall auf der Welt und auf jedem Gerät erreichen.
Einige dieser APIs sind sehr leistungsstark und können bei unsachgemäßer Handhabung zu Problemen führen. Wie beim Kugelfisch 🐡: Wenn man ihn richtig zubereitet, ist er eine Delikatesse, aber wenn man ihn falsch zubereitet, kann er tödlich sein. Aber keine Sorge, in diesem Codelab kann nichts kaputtgehen.
Aus diesem Grund ist der interne Codename des Web Capabilities-Projekts, in dem die beteiligten Unternehmen diese neuen APIs entwickeln, „Project Fugu“.
Webfunktionen ermöglichen es großen und kleinen Unternehmen schon heute, reine browserbasierte Lösungen zu entwickeln. Das führt oft zu einer schnelleren Bereitstellung und geringeren Entwicklungskosten im Vergleich zu plattformspezifischen Lösungen.
3. Jetzt starten
Laden Sie einen der beiden Browser herunter und legen Sie dann das folgende Laufzeit-Flag 🚩 fest, indem Sie zu about://flags
gehen. Das funktioniert sowohl in Chrome als auch in Edge:
#enable-experimental-web-platform-features
Starten Sie den Browser neu, nachdem Sie die Funktion aktiviert haben.
Sie verwenden die Plattform Glitch, da Sie damit Ihre PWA hosten können und sie einen guten Editor bietet. Glitch unterstützt auch den Import und Export nach GitHub, sodass es keine Anbieterbindung gibt. Rufen Sie fugu-paint.glitch.me auf, um die Anwendung auszuprobieren. Es handelt sich um eine einfache Zeichen-App 🎨, die Sie im Laufe des Codelabs verbessern werden.
Nachdem Sie die Anwendung ausprobiert haben, können Sie sie remixen, um eine eigene Kopie zu erstellen, die Sie bearbeiten können. Die URL Ihres Remix sieht etwa so aus: glitch.com/edit/#!/bouncy-candytuft. „bouncy-candytuft“ ist dabei ein Platzhalter. Dieser Remix ist weltweit direkt zugänglich. Melden Sie sich in Ihrem bestehenden Konto an oder erstellen Sie ein neues Konto auf Glitch, um Ihre Arbeit zu speichern. Sie können sich Ihre App ansehen, indem Sie auf die Schaltfläche „🕶 Show“ klicken. Die URL der gehosteten App sieht in etwa so aus: bouncy-candytuft.glitch.me (beachten Sie die .me
anstelle der .com
als Top-Level-Domain).
Jetzt können Sie Ihre App bearbeiten und verbessern. Wenn Sie Änderungen vornehmen, wird die App neu geladen und Ihre Änderungen sind direkt sichtbar.
Die folgenden Aufgaben sollten idealerweise in der angegebenen Reihenfolge ausgeführt werden. Wie oben erwähnt, können Sie jedoch jederzeit einen Schritt überspringen, wenn Sie keinen Zugriff auf ein kompatibles Gerät haben. Jede Aufgabe ist entweder mit 🐟, einem harmlosen Süßwasserfisch, oder mit 🐡, einem Fugu-Fisch, der mit Vorsicht zu genießen ist, gekennzeichnet. So können Sie erkennen, wie experimentell eine Funktion ist.
Prüfen Sie in der Console in den Entwicklertools, ob eine API auf dem aktuellen Gerät unterstützt wird. Wir verwenden auch Glitch, damit Sie dieselbe App ganz einfach auf verschiedenen Geräten testen können, z. B. auf Ihrem Smartphone und Ihrem Computer.
4. 🐟 Unterstützung für die Web Share API hinzufügen
Die schönsten Zeichnungen zu erstellen, ist langweilig, wenn es niemanden gibt, der sie bewundert. Fügen Sie eine Funktion hinzu, mit der Nutzer ihre Zeichnungen in Form von Grußkarten mit anderen teilen können.
Die Web Share API unterstützt die Freigabe von Dateien. Wie Sie sich vielleicht erinnern, ist ein File
nur eine bestimmte Art von Blob
. Importieren Sie daher in der Datei mit dem Namen share.mjs
die Schaltfläche „Teilen“ und die Hilfsfunktion toBlob()
, die den Inhalt eines Canvas in einen Blob konvertiert, und fügen Sie die Freigabefunktion gemäß dem folgenden Code hinzu.
Wenn Sie die Funktion implementiert haben, die Schaltfläche aber nicht sehen, liegt das daran, dass Ihr Browser die Web Share API nicht unterstützt.
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. 🐟 Unterstützung für die Web Share Target API hinzufügen
Ihre Nutzer können jetzt mit der App erstellte Grußkarten teilen. Sie können aber auch zulassen, dass Nutzer Bilder für Ihre App freigeben und in Grußkarten umwandeln. Dazu können Sie die Web Share Target API verwenden.
Im Web-App-Manifest müssen Sie der App mitteilen, welche Dateitypen sie akzeptieren kann und welche URL der Browser aufrufen soll, wenn eine oder mehrere Dateien geteilt werden. Der folgende Auszug aus der Datei manifest.webmanifest
zeigt dies.
{
"share_target": {
"action": "./share-target/",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"files": [
{
"name": "image",
"accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
}
]
}
}
}
Der Service Worker verarbeitet dann die empfangenen Dateien. Die URL ./share-target/
ist nicht vorhanden. Die App reagiert nur im fetch
-Handler darauf und leitet die Anfrage zur Stamm-URL weiter, indem sie den Abfrageparameter ?share-target
hinzufügt:
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 */
/* ... */
});
Wenn die App geladen wird, wird geprüft, ob dieser Abfrageparameter festgelegt ist. Wenn ja, wird das freigegebene Bild auf das Canvas gezeichnet und aus dem Cache gelöscht. Das alles passiert in 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');
}
};
Diese Funktion wird dann bei der Initialisierung der App verwendet.
if (location.search.includes('share-target')) {
restoreImageFromShare();
} else {
drawDefaultImage();
}
6. 🐟 Unterstützung für den Import von Bildern hinzufügen
Es ist schwierig, alles von Grund auf neu zu zeichnen. Fügen Sie eine Funktion hinzu, mit der Nutzer ein lokales Bild von ihrem Gerät in die App hochladen können.
Machen Sie sich zuerst mit der Funktion drawImage()
des Canvas vertraut. Machen Sie sich als Nächstes mit dem Element <input
vertraut.
type=file>
Mit diesem Wissen können Sie dann die Datei import_image_legacy.mjs
bearbeiten und das folgende Snippet hinzufügen. Oben in der Datei importieren Sie die Import-Schaltfläche und eine Hilfsfunktion drawBlob()
, mit der Sie einen Blob auf das Canvas zeichnen können.
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. 🐟 Unterstützung für den Export von Bildern hinzufügen
Wie speichert der Nutzer eine in der App erstellte Datei auf seinem Gerät? Bisher wurde dies in der Regel mit einem <a
-Element erreicht.
download>
Fügen Sie in der Datei export_image_legacy.mjs
den Inhalt wie unten beschrieben hinzu. Importieren Sie die Export-Schaltfläche und die toBlob()
-Hilfsfunktion, mit der der Canvas-Inhalt in ein Blob konvertiert wird.
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. 🐟 Unterstützung für die File System Access API hinzufügen
Teilen ist zwar schön, aber Ihre Nutzer möchten ihre besten Arbeiten wahrscheinlich auf ihren eigenen Geräten speichern. Fügen Sie eine Funktion hinzu, mit der Nutzer ihre Zeichnungen speichern und wieder öffnen können.
Bisher haben Sie für den Import von Dateien den Legacy-Ansatz <input type=file>
und für den Export von Dateien den Legacy-Ansatz <a download>
verwendet. Jetzt verwenden Sie die File System Access API, um die Nutzerfreundlichkeit zu verbessern.
Diese API ermöglicht das Öffnen und Speichern von Dateien aus dem Dateisystem des Betriebssystems. Bearbeiten Sie die beiden Dateien import_image.mjs
und export_image.mjs
, indem Sie den folgenden Inhalt hinzufügen. Damit diese Dateien geladen werden, müssen Sie die 🐡-Emojis aus script.mjs
entfernen.
Ersetzen Sie diese Zeile:
// Remove all the emojis for this feature test to succeed.
if ('show🐡Open🐡File🐡Picker' in window) {
/* ... */
}
… durch diese Zeile:
if ('showOpenFilePicker' in window) {
/* ... */
}
In 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);
}
});
In 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. 🐟 Unterstützung für die Contacts Picker API hinzufügen
Ihre Nutzer möchten möglicherweise eine Nachricht auf ihre Grußkarte schreiben und jemanden persönlich ansprechen. Fügen Sie eine Funktion hinzu, mit der Nutzer einen oder mehrere ihrer lokalen Kontakte auswählen und deren Namen der Freigabenachricht hinzufügen können.
Mit der Contact Picker API können Sie auf einem Android- oder iOS-Gerät Kontakte aus der Kontaktverwaltungs-App des Geräts auswählen und an die Anwendung zurückgeben. Bearbeiten Sie die Datei contacts.mjs
und fügen Sie den folgenden Code hinzu.
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. 🐟 Unterstützung für die Async Clipboard API hinzufügen
Nutzer möchten möglicherweise ein Bild aus einer anderen App in Ihre App einfügen oder eine Zeichnung aus Ihrer App in eine andere App kopieren. Fügen Sie eine Funktion hinzu, mit der Nutzer Bilder in Ihre App und aus Ihrer App kopieren und einfügen können. Die Async Clipboard API unterstützt PNG-Bilder. Sie können also jetzt Bilddaten in die Zwischenablage lesen und schreiben.
Suchen Sie die Datei clipboard.mjs
und fügen Sie Folgendes hinzu:
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. 🐟 Unterstützung für die Badging API hinzufügen
Wenn Nutzer Ihre App installieren, wird auf ihrem Startbildschirm ein Symbol angezeigt. Mit diesem Symbol können Sie interessante Informationen wie die Anzahl der Pinselstriche einer bestimmten Zeichnung angeben.
Fügen Sie eine Funktion hinzu, die das Badge jedes Mal hochzählt, wenn der Nutzer einen neuen Pinselstrich macht. Mit der Badging API kann ein numerisches Badge für das App-Symbol festgelegt werden. Sie können das Badge immer dann aktualisieren, wenn ein pointerdown
-Ereignis eintritt (d. h. wenn ein Pinselstrich erfolgt), und das Badge zurücksetzen, wenn die Leinwand gelöscht wird.
Fügen Sie den folgenden Code in die Datei badge.mjs
ein:
import { canvas, clearButton } from './script.mjs';
let strokes = 0;
canvas.addEventListener('pointerdown', () => {
navigator.setAppBadge(++strokes);
});
clearButton.addEventListener('click', () => {
strokes = 0;
navigator.setAppBadge(strokes);
});
12. 🐟 Unterstützung der Screen Wake Lock API hinzufügen
Manchmal brauchen Ihre Nutzer ein paar Momente, um sich eine Zeichnung anzusehen, bis die Inspiration kommt. Fügen Sie eine Funktion hinzu, die das Display aktiviert und verhindert, dass der Bildschirmschoner gestartet wird. Die Screen Wake Lock API verhindert, dass der Bildschirm des Nutzers in den Ruhemodus wechselt. Das Wake Lock wird automatisch freigegeben, wenn ein Ereignis vom Typ „Sichtbarkeitsänderung“ gemäß Page Visibility eintritt. Daher muss das Wake Lock neu angefordert werden, wenn die Seite wieder sichtbar wird.
Suchen Sie die Datei wake_lock.mjs
und fügen Sie den folgenden Inhalt hinzu. Um zu testen, ob das funktioniert, konfigurieren Sie den Bildschirmschoner so, dass er nach einer Minute angezeigt wird.
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. 🐟 Unterstützung für die Periodic Background Sync API hinzufügen
Mit einer leeren Leinwand zu beginnen, kann langweilig sein. Mit der Periodic Background Sync API können Sie das Canvas Ihrer Nutzer beispielsweise jeden Tag mit einem neuen Bild initialisieren, z. B. mit dem täglichen Kugelfischfoto von Unsplash.
Dazu sind zwei Dateien erforderlich: eine Datei periodic_background_sync.mjs
, in der die regelmäßige Hintergrundsynchronisierung registriert wird, und eine weitere Datei image_of_the_day.mjs
, in der das Herunterladen des Bilds des Tages erfolgt.
In 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());
});
In 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. 🐟 Unterstützung für die Shape Detection API hinzufügen
Manchmal enthalten die Zeichnungen oder die verwendeten Hintergrundbilder Ihrer Nutzer nützliche Informationen wie z. B. Barcodes. Mit der Shape Detection API und insbesondere der Barcode Detection API können Sie diese Informationen extrahieren. Fügen Sie eine Funktion hinzu, die versucht, Barcodes aus den Zeichnungen Ihrer Nutzer zu erkennen. Suchen Sie die Datei barcode.mjs
und fügen Sie den folgenden Inhalt hinzu. Um diese Funktion zu testen, laden Sie einfach ein Bild mit einem Barcode auf die Arbeitsfläche oder fügen Sie es dort ein. Sie können einen Beispielbarcode aus einer Bildersuche nach QR‑Codes kopieren.
/* 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. 🐡 Unterstützung für die Idle Detection API hinzufügen
Wenn Sie sich vorstellen, dass Ihre App in einer Art Kiosk ausgeführt wird, wäre es nützlich, den Canvas nach einer bestimmten Zeit der Inaktivität zurückzusetzen. Mit der Idle Detection API können Sie erkennen, wann ein Nutzer nicht mehr mit seinem Gerät interagiert.
Suchen Sie die Datei idle_detection.mjs
und fügen Sie den folgenden Inhalt ein.
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. 🐡 Unterstützung für die File Handling API hinzufügen
Was wäre, wenn Ihre Nutzer einfach auf eine Bilddatei doppelklicken könnten und Ihre App würde geöffnet? Mit der File Handling API ist genau das möglich.
Sie müssen die PWA als Dateihandler für Bilder registrieren. Dies geschieht im Web-App-Manifest. Der folgende Auszug aus der Datei manifest.webmanifest
zeigt dies. (Dies ist bereits Teil des Manifests und muss nicht manuell hinzugefügt werden.)
{
"file_handlers": [
{
"action": "./",
"accept": {
"image/*": [".jpg", ".jpeg", ".png", ".webp", ".svg"]
}
}
]
}
Fügen Sie den folgenden Code in die Datei file-handling.mjs
ein, um geöffnete Dateien zu verarbeiten:
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. Glückwunsch
🎉 Juhu, du hast es geschafft!
Im Rahmen von Project Fugu 🐡 werden so viele spannende Browser-APIs entwickelt, dass dieses Codelab nur einen kleinen Einblick geben kann.
Weitere Informationen finden Sie in unseren Publikationen auf unserer Website web.dev.
Aber das ist noch nicht alles. Für Updates, die noch nicht veröffentlicht wurden, können Sie auf unseren Fugu API-Tracker zugreifen. Dort finden Sie Links zu allen Vorschlägen, die veröffentlicht wurden, sich in einem Origin- oder Dev-Trial befinden, an denen gearbeitet wird und die in Erwägung gezogen werden, aber noch nicht begonnen haben.
Dieses Codelab wurde von Thomas Steiner (@tomayac) geschrieben. Ich beantworte gerne Ihre Fragen und freue mich auf Ihr Feedback. Besonderer Dank gilt Hemanth H.M (@GNUmanth), Christian Liebel (@christianliebel), Sven May (@Svenmay), Lars Knudsen (@larsgk) und Jackie Han (@hanguokai), die dieses Codelab mitgestaltet haben.