iT邦幫忙

2021 iThome 鐵人賽

DAY 29
0
Modern Web

前端三分鐘 X Progressive Web App 30 天製造解密系列 第 29

Progressive Web App NFC 入門實作 (29)

什麼是 Web NFC?

NFC (Near Field Communication) 近場通訊是高頻 (13.56 MHz) 短距離無線通訊技術,只要距離 5-10 公分內 Web NFC 就能夠讀取和寫入 NFC 標籤,傳輸速率高達 424 kbit/s。

目前 Web NFC 只支援 NDEF,尚不支援 ISO-DEP、NFC-A/B、NFC-F、HCE。

Demo 站台如下,只會示範讀取功能,因為不太確定真的執行寫入悠遊卡會不會壞掉 Orz
https://linyencheng.github.io/pwa-web-nfc/

Web App 會透過 Page Visibility API 來偵測目前網站是否在 "可見" 的狀態,在可見狀態下才能夠執行掃描讀取或寫入。

當用戶成功使用其設備掃描 NFC 標籤時,瀏覽器會使用震動來提示。

  • 如果螢幕關閉或設備被鎖定,則對 NFC 讀取將被中止
  • 對於不可見的網頁,接收和推送 NFC 內容被暫停,並在網頁再次可見時恢復

Web NFC 示意圖(圖片來源: https://web-dev)

使用 Web NFC 的情境包括:

  • 博物館中將裝置跟展覽的 Tag 接觸時,可以顯示有關訊息
  • 商店可以做庫存管理
  • 馬拉松比賽可以拿來讀取跑友的 RFID

怎麼使用 NDEFReader?

  1. 判斷是否支援,接著是取得權限 NDEFReader,只要叫用 scan() 和 write() 就會觸發權限的提示,也可以透過程式觸發。
if ("NDEFReader" in window) {
  // 支援
  const nfcPermissionStatus = await navigator.permissions.query({
    name: "nfc",
  });
  if (nfcPermissionStatus.state === "granted") {
    // 有開啟權限
  } else {
    // 未開啟權限
  }
}
  1. new 一個 NDEFReader 然後就可以叫用 scan(),叫用後當 NFC 標籤接近時,NDEFReadingEvent 會觸發事件。
    • reading 成功後會收到兩個屬性
      • serialNumber: 表示設備的序列號(例如 00-11-22-33-44-55-66),如果沒有,則為空字符串。
      • message: 表示存儲在 NFC 標籤中的 NDEF 消息。
    • 寫入時的 option 也有兩種屬性
      • overwrite: 不可覆寫要記得設定為 false
      • records: 可以寫入多筆資訊
// 讀
async function readTag() {
  try {
    const ndef = new NDEFReader();
    await ndef.scan();

    ndef.addEventListener("readingerror", () => {
      log("讀取錯誤");
    });

    ndef.addEventListener("reading", ({ message, serialNumber }) => {
      log(`> Serial Number: ${serialNumber}`);
      log(`> Records:(${message.records.length})`);
    });
  } catch (error) {
    log("錯誤" + error);
  }
}

// 寫
async function writeTag() {
  try {
    const ndef = new NDEFReader();
    await ndef.write("Hello world!", { overwrite: false });
    await ndef.write({
      records: [
        { recordType: "url", data: "https://w3c.github.io/web-nfc/" },
        { recordType: "url", data: "https://web.dev/nfc/" },
      ],
    });
  } catch (error) {
    log("錯誤" + error);
  }
}
  1. 收到的 message 可能含有多筆 records 這時候可以一筆一筆依照屬性取解析
for (const record of message.records) {
  console.log("Record type:  " + record.recordType);
  console.log("MIME type:    " + record.mediaType);
  console.log("Record id:    " + record.id);
  switch (record.recordType) {
    case "text":
      break;
    case "url":
      break;
    default:
  }
}
  1. 停止操作,透過 AbortController 中的 signal 當成參數送進去 scan()、write() 中可以隨時終止目前的動作。
const abortController = new AbortController();
abortController.signal.onabort = (event) => {};

const ndef = new NDEFReader();
await ndef.scan({ signal: abortController.signal });

await ndef.write("Hello world", { signal: abortController.signal });

function abortAction(event) {
  abortController.abort();
}

abortAction();

上一篇
Progressive Web App 取得 Geolocation API 地理資訊 (28)
下一篇
Progressive Web App 跨平台安裝上架 (30)
系列文
前端三分鐘 X Progressive Web App 30 天製造解密30

尚未有邦友留言

立即登入留言