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

<<:  Day27-TypeScript(TS)的命名空间(Namespace)与模组(Modules)

>>:  [Day27] Scrum失败经验谈 – 危机四伏的Sprint planning会议

Day 20 AWS云端实作起手式最後一弹 整体架构回顾

我们今天来做个小结,快速汇整这一系列实做的内容。 步骤1: 建立2个S3 Bucket 一个S3 B...

DAY2 练习文件内容

以下就是我请学长帮我写的练习专案内容 一、功能列表(Flex 按钮) 1.注册:存取使用者姓名和组别...

Day 19 - To Do List (6) 删除 To Do Event

很快的我们来到做删除的部分, 很快速的,我们先来看一下如果我们要把东西从试算表删除该怎麽做: 我们从...

[烧烤吃到饱-3] 猪对有韩式烤肉吃到饱-台中精武店 #中秋节烤肉精选店家

我对这家店是心存感激的,因为当天我是一个人去吃的,但是店家并没有跟我收「单身税」。 之前有提到,为了...

[Day22] 使用官方提供的工具,吸引用户不断回来使用Action

现在,你有个能针对不同装置进行适当对白的Action了。 但要评量一个Action是否成功,用户的...