PWA'nız için yeni ve yakında kullanıma sunulacak tarayıcı özelliklerini keşfedin: From Fugu With Love

1. Başlamadan önce

Progresif Web Uygulamaları (PWA'lar); HTML, CSS ve JavaScript gibi yaygın web teknolojileri kullanılarak oluşturulmuş, web üzerinden sunulan bir uygulama yazılımı türüdür. Bu uygulamalar, standartlara uygun bir tarayıcı kullanan her platformda çalışacak şekilde tasarlanmıştır.

Bu codelab'de, temel bir PWA ile başlayacak ve daha sonra yeni tarayıcı özelliklerini keşfederek PWA'larınıza süper güçler kazandıracaksınız 🦸.

Bu yeni tarayıcı özelliklerinin çoğu henüz geliştirme aşamasında ve standartlaştırılma sürecindedir. Bu nedenle, bazı durumlarda bunları kullanmak için tarayıcı işaretlerini ayarlamanız gerekir.

Ön koşullar

Bu codelab'i tamamlamak için modern JavaScript'e, özellikle de sözler (promises) ve async/await'e aşina olmanız gerekir. Codelab'in tüm adımları tüm platformlarda desteklenmediğinden, test için elinizin altında ek cihazlar (ör. kodu düzenlediğiniz cihazdan farklı bir işletim sistemi kullanan bir Android telefon veya dizüstü bilgisayar) olması faydalı olur. Gerçek cihazlara alternatif olarak, mevcut cihazınızdan test yapmanıza olanak tanıyan Android simülatörü gibi simülatörleri veya BrowserStack gibi online hizmetleri kullanmayı deneyebilirsiniz. Aksi takdirde, adımlar birbirine bağlı olmadığından herhangi bir adımı atlayabilirsiniz.

Ne oluşturacaksınız?

Tebrik kartı web uygulaması oluşturacak ve yeni ve yakında çıkacak tarayıcı özelliklerinin, uygulamanızı belirli tarayıcılarda gelişmiş bir deneyim sunacak şekilde nasıl geliştirebileceğini (ancak tüm modern tarayıcılarda faydalı olmaya devam edecek şekilde) öğreneceksiniz.

Dosya sistemi erişimi, sistem panosu erişimi, kişi alma, düzenli arka plan senkronizasyonu, ekran uyanık kalma kilidi ve paylaşım özellikleri gibi destek özelliklerini nasıl ekleyeceğinizi öğreneceksiniz.

Bu codelab'i tamamladığınızda, web uygulamalarınızı yeni tarayıcı özellikleriyle nasıl aşamalı olarak geliştireceğinizi, uyumsuz tarayıcılar kullanan kullanıcılarınızın indirme yüküyle karşılaşmamasını ve en önemlisi, bu kullanıcıları uygulamanızın dışında bırakmamanızı sağlayacak şekilde öğreneceksiniz.

İhtiyacınız olanlar

Şu anda tam olarak desteklenen tarayıcılar şunlardır:

Belirli bir geliştirici kanalının kullanılması önerilir.

2. Project Fugu

Progresif Web Uygulamaları (PWA), web'deki herkese, dünyanın her yerinde ve her tür cihazdan ulaşırken gelişmiş özellikler, güvenilirlik ve yüklenebilirlik sunmak için modern API'lerle oluşturulur ve geliştirilir.

Bu API'lerin bazıları çok güçlüdür ve yanlış kullanıldığında sorunlara yol açabilir. Fugu balığı 🐡 gibi: Doğru keserseniz lezzetli bir yiyecek, yanlış keserseniz ölümcül olabilir (Ancak endişelenmeyin, bu codelab'de hiçbir şey gerçekten bozulamaz).

Bu nedenle, Web Özellikleri projesinin (ilgili şirketlerin bu yeni API'leri geliştirdiği) dahili kod adı Project Fugu'dur.

Web özellikleri, büyük ve küçük işletmelerin tamamen tarayıcı tabanlı çözümler oluşturmasına olanak tanır. Bu çözümler, platforma özel bir yol izlemeye kıyasla genellikle daha düşük geliştirme maliyetleriyle daha hızlı dağıtım sağlar.

3. Başlayın

Tarayıcılardan birini indirin ve about://flags seçeneğine giderek hem Chrome hem de Edge'de çalışan aşağıdaki çalışma zamanı işaretini 🚩 ayarlayın:

  • #enable-experimental-web-platform-features

Etkinleştirdikten sonra tarayıcınızı yeniden başlatın.

PWA'nızı barındırmanıza olanak tanıdığı ve iyi bir düzenleyicisi olduğu için Glitch platformunu kullanacaksınız. Glitch, GitHub'a içe ve dışa aktarmayı da desteklediğinden satıcıya bağımlılık söz konusu değildir. Uygulamayı denemek için fugu-paint.glitch.me adresine gidin. Bu, codelab sırasında geliştireceğiniz temel bir çizim uygulamasıdır 🎨.

Üzerinde "Google" kelimesinin yazılı olduğu büyük bir tuvalin bulunduğu Fugu Greetings temel PWA'sı.

Uygulamayla oynadıktan sonra, düzenleyebileceğiniz kendi kopyanızı oluşturmak için uygulamayı remiksleyin. Remiks URL'niz glitch.com/edit/#!/bouncy-candytuft gibi görünür ("bouncy-candytuft" sizin için farklı bir şey olacaktır). Bu remikse dünya genelinde doğrudan erişilebilir. Çalışmanızı kaydetmek için mevcut hesabınızda oturum açın veya Glitch'te yeni bir hesap oluşturun. "🕶 Göster" düğmesini tıklayarak uygulamanızı görebilirsiniz. Barındırılan uygulamanın URL'si bouncy-candytuft.glitch.me gibi bir şey olur (üst düzey alan olarak .com yerine .me kullanıldığına dikkat edin).

Artık uygulamanızı düzenleyip iyileştirmeye hazırsınız. Değişiklik yaptığınızda uygulama yeniden yüklenir ve değişiklikleriniz doğrudan görünür hale gelir.

Glitch IDE'de bir HTML dokümanının düzenlenmesi gösteriliyor.

Aşağıdaki görevlerin ideal olarak sırayla tamamlanması gerekir. Ancak yukarıda belirtildiği gibi, uyumlu bir cihaza erişiminiz yoksa adımları atlayabilirsiniz. Her görev, özelliğin ne kadar deneysel olduğunu belirtmek için 🐟 (zararsız bir tatlı su balığı) veya 🐡 ("dikkatli kullanın" anlamına gelen bir fugu balığı) ile işaretlenir.

Bir API'nin mevcut cihazda desteklenip desteklenmediğini görmek için Geliştirici Araçları'ndaki Konsol'u kontrol edin. Aynı uygulamayı farklı cihazlarda (ör. cep telefonunuz ve masaüstü bilgisayarınız) kolayca kontrol edebilmeniz için Glitch'i de kullanırız.

API uyumluluğu, Geliştirici Araçları'ndaki Konsol'a kaydedilir.

4. 🐟 Web Paylaşımı API'si desteği ekleme

En muhteşem çizimleri oluşturmak, takdir edecek kimse yoksa sıkıcıdır. Kullanıcılarınızın çizimlerini tebrik kartı şeklinde dünyayla paylaşmasına olanak tanıyan bir özellik ekleyin.

Web Share API, dosya paylaşımını destekler. File ise hatırlayacağınız gibi yalnızca belirli bir Blob türüdür. Bu nedenle, share.mjs adlı dosyada paylaşım düğmesini ve tuvalin içeriğini blob'a dönüştüren toBlob() adlı kolaylık işlevini içe aktarın ve paylaşım işlevini aşağıdaki koda göre ekleyin.

Bu özelliği uyguladıysanız ancak düğmeyi görmüyorsanız bunun nedeni tarayıcınızın Web Share API'yi uygulamamasıdır.

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 desteği ekleme

Kullanıcılarınız artık uygulamayı kullanarak oluşturdukları tebrik kartlarını paylaşabilir. Ayrıca, kullanıcıların resimleri uygulamanızda paylaşmasına ve bunları tebrik kartlarına dönüştürmesine de izin verebilirsiniz. Bunun için Web Share Target API'yi kullanabilirsiniz.

Web Uygulaması Manifesti'nde, uygulamaya hangi tür dosyaları kabul edebileceğinizi ve bir veya daha fazla dosya paylaşıldığında tarayıcının hangi URL'yi çağırması gerektiğini söylemeniz gerekir. manifest.webmanifest dosyasının aşağıdaki alıntısında bu durum gösterilmektedir.

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

Ardından hizmet çalışanı, alınan dosyalarla ilgilenir. ./share-target/ URL'si aslında mevcut değil. Uygulama, yalnızca fetch işleyicisinde bu URL'ye göre hareket ediyor ve ?share-target sorgu parametresini ekleyerek isteği kök URL'ye yönlendiriyor:

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

  /* ... */
});

Uygulama yüklendiğinde bu sorgu parametresinin ayarlanıp ayarlanmadığını kontrol eder. Ayarlanmışsa paylaşılan resmi tuvale çizer ve önbellekten siler. Tüm bunlar script.mjs içinde gerçekleşir:

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

Bu işlev daha sonra uygulama başlatıldığında kullanılır.

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

6. 🐟 Görüntü İçe Aktarma Desteği Ekleme

Her şeyi sıfırdan çizmek zordur. Kullanıcılarınızın cihazlarından uygulamaya yerel resim yüklemelerine olanak tanıyan bir özellik ekleyin.

Öncelikle, tuvalin drawImage() işlevi hakkında bilgi edinin. Ardından, <​input
type=file>
öğesi hakkında bilgi edinin.

Bu bilgileri kullanarak import_image_legacy.mjs adlı dosyayı düzenleyebilir ve aşağıdaki snippet'i ekleyebilirsiniz. İçe aktardığınız dosyanın üst kısmında, içe aktarma düğmesi ve tuval üzerine bir blob çizmenize olanak tanıyan kolaylık işlevi drawBlob() bulunur.

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. 🐟 Dışa aktarılan resim desteği ekleme

Kullanıcılarınız, uygulamada oluşturulan bir dosyayı cihazlarına nasıl kaydedecek? Geleneksel olarak bu, <​a
download>
öğesiyle sağlanır.

export_image_legacy.mjs dosyasında içeriği aşağıdaki gibi ekleyin. Dışa aktarma düğmesini ve tuval içeriklerini blob'a dönüştüren bir toBlob() kolaylık işlevini içe aktarın.

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. 🐟 File System Access API desteği ekleme

Paylaşmak güzeldir ancak kullanıcılarınız en iyi çalışmalarını kendi cihazlarına kaydetmek isteyebilir. Kullanıcılarınızın çizimlerini kaydetmesine (ve yeniden açmasına) olanak tanıyan bir özellik ekleyin.

Daha önce, dosyaları içe aktarmak için <​input type=file> eski bir yaklaşım, dosyaları dışa aktarmak için ise <​a download> eski bir yaklaşım kullanıyordunuz. Şimdi deneyimi iyileştirmek için File System Access API'yi kullanacaksınız.

Bu API, işletim sisteminin dosya sistemindeki dosyaların açılmasına ve kaydedilmesine olanak tanır. Aşağıdaki içerikleri ekleyerek import_image.mjs ve export_image.mjs dosyalarını düzenleyin. Bu dosyaların yüklenmesi için 🐡 emojilerini script.mjs bölümünden kaldırın.

Şu satırı değiştirin:

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

...şu satırla:

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

import_image.mjs içinde:

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 içinde:

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. 🐟 Kişi Seçici API desteği eklendi

Kullanıcılarınız, tebrik kartlarına mesaj eklemek ve birine kişisel olarak hitap etmek isteyebilir. Kullanıcılarınızın yerel kişilerinden birini (veya birden çok kişiyi) seçip adlarını paylaşım mesajına eklemelerine olanak tanıyan bir özellik ekleyin.

Android veya iOS cihazlarda Contact Picker API, cihazın Kişiler Yöneticisi uygulamasından kişileri seçmenize ve bunları uygulamaya döndürmenize olanak tanır. contacts.mjs dosyasını düzenleyerek aşağıdaki kodu ekleyin.

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. 🐟 Async Clipboard API desteği ekleme

Kullanıcılarınız başka bir uygulamadaki resmi uygulamanıza yapıştırmak veya uygulamanızdaki çizimi başka bir uygulamaya kopyalamak isteyebilir. Kullanıcılarınızın resimleri uygulamanıza ve uygulamanızdan kopyalayıp yapıştırmasına olanak tanıyan bir özellik ekleyin. Async Clipboard API, PNG resimlerini destekler. Bu sayede artık resim verilerini panoya okuyup yazabilirsiniz.

clipboard.mjs dosyasını bulun ve aşağıdakileri ekleyin:

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 desteği ekleme

Kullanıcılarınız uygulamanızı yüklediğinde ana ekranlarında bir simge görünür. Bu simgeyi, belirli bir çizimin kaç fırça darbesiyle yapıldığı gibi eğlenceli bilgileri aktarmak için kullanabilirsiniz.

Kullanıcınız her yeni fırça darbesi yaptığında rozeti sayan bir özellik ekleyin. Badging API, uygulama simgesine sayısal bir rozet ayarlanmasına olanak tanır. pointerdown etkinliği her gerçekleştiğinde (ör. fırça darbesi olduğunda) rozeti güncelleyebilir ve tuval temizlendiğinde rozeti sıfırlayabilirsiniz.

Aşağıdaki kodu badge.mjs dosyasına yerleştirin:

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 desteği eklendi

Kullanıcılarınızın bazen ilham almak için bir çizime uzun süre bakması gerekebilir. Ekranın uyanık kalmasını sağlayan ve ekran koruyucunun etkinleşmesini engelleyen bir özellik ekleyin. Screen Wake Lock API, kullanıcının ekranının uyku moduna geçmesini engeller. Sayfa görünürlüğü ile tanımlanan bir görünürlük değişikliği etkinliği gerçekleştiğinde uyanık kalma kilidi otomatik olarak serbest bırakılır. Bu nedenle, sayfa tekrar göründüğünde uyandırma kilidinin yeniden alınması gerekir.

Dosyayı bulun wake_lock.mjs ve aşağıdaki içerikleri ekleyin. Bu özelliğin çalışıp çalışmadığını test etmek için ekran koruyucunuzu bir dakika sonra gösterilecek şekilde yapılandırın.

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. 🐟 Periyodik Arka Plan Senkronizasyonu API'si desteği ekleme

Boş bir tuvalle başlamak sıkıcı olabilir. Kullanıcılarınızın tuvalini her gün yeni bir resimle başlatmak için Periodic Background Sync API'yi kullanabilirsiniz. Örneğin, Unsplash'in günlük fugu fotoğrafı.

Bu işlem için iki dosya gerekir: Periyodik Arka Plan Senkronizasyonu'nu kaydeden bir dosya periodic_background_sync.mjs ve günün resmini indiren başka bir dosya image_of_the_day.mjs.

periodic_background_sync.mjs içinde:

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 içinde:

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. 🐟 Shape Detection API desteği ekleme

Bazen kullanıcılarınızın çizimleri veya kullanılan arka plan resimleri, örneğin barkodlar gibi faydalı bilgiler içerebilir. Shape Detection API ve özellikle de Barcode Detection API, bu bilgileri ayıklamanıza olanak tanır. Kullanıcılarınızın çizimlerindeki barkodları algılamaya çalışan bir özellik ekleyin. Dosyayı barcode.mjs bulun ve aşağıdaki içeriği ekleyin. Bu özelliği test etmek için barkodlu bir resmi tuvale yüklemeniz veya yapıştırmanız yeterlidir. QR kodları için resim aramasından örnek bir barkodu kopyalayabilirsiniz.

/* 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 desteği eklendi

Uygulamanızın kiosk benzeri bir kurulumda çalıştığını düşünüyorsanız belirli bir süre işlem yapılmadığında tuvali sıfırlamak faydalı bir özellik olabilir. Boşta Kalma Algılama API'si, kullanıcının cihazıyla etkileşimde bulunmadığı zamanları algılamanıza olanak tanır.

Dosyayı idle_detection.mjs bulun ve aşağıdaki içerikleri yapıştırın.

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. 🐡 File Handling API desteği ekleme

Kullanıcılarınızın bir resim dosyasını çift tıkladığında uygulamanızın açıldığını düşünün. File Handling API tam da bunu yapmanızı sağlar.

PWA'yı resimler için dosya işleyici olarak kaydetmeniz gerekir. Bu durum, web uygulaması manifestinde gerçekleşir. Aşağıdaki manifest.webmanifest dosyasının alıntısı bunu gösterir. (Bu, manifestin bir parçasıdır. Kendiniz eklemenize gerek yoktur.)

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

Açılan dosyaları gerçekten işlemek için aşağıdaki kodu file-handling.mjs dosyasına ekleyin:

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. Tebrikler

🎉 Yaşasın, başardınız!

Project Fugu 🐡 kapsamında geliştirilen çok sayıda heyecan verici tarayıcı API'si olduğundan bu codelab, konunun ancak yüzeyini çizebilir.

Daha ayrıntılı bilgi edinmek veya sadece daha fazla şey öğrenmek için web.dev sitemizdeki yayınlarımızı takip edin.

web.dev sitesinin &quot;Özellikler&quot; bölümünün açılış sayfası.

Ancak bu özellikler bunlarla sınırlı değil. Henüz duyurulmamış güncellemeler için, gönderilmiş, kaynak denemesi veya geliştirici denemesi aşamasında olan tüm tekliflerin bağlantılarını, üzerinde çalışılmaya başlanan tüm teklifleri ve henüz başlanmamış ancak değerlendirilen her şeyi içeren Fugu API izleyicimize erişebilirsiniz.

Fugu API izleyici web sitesi

Bu codelab, Thomas Steiner (@tomayac) tarafından yazılmıştır. Sorularınızı yanıtlamaktan memnuniyet duyarım ve geri bildirimlerinizi bekliyorum. Bu codelab'in şekillenmesine yardımcı olan Hemanth H.M (@GNUmanth), Christian Liebel (@christianliebel), Sven May (@Svenmay), Lars Knudsen (@larsgk) ve Jackie Han'e (@hanguokai) özel teşekkürlerimizi sunarız.