โต้ตอบกับอุปกรณ์ NFC ใน Chrome สำหรับ Android

อ่านและเขียนแท็ก NFC ได้แล้ว

François Beaufort
François Beaufort

Web NFC คืออะไร

NFC ย่อมาจาก Near Field Communications ซึ่งเป็นเทคโนโลยีไร้สายระยะสั้นที่ทำงานที่ 13.56 MHz ซึ่งช่วยให้สามารถสื่อสารระหว่างอุปกรณ์ ที่มีระยะสัญญาณน้อยกว่า 10 ซม. และอัตราการส่งผ่านสูงสุดถึง 424 kbit/s

NFC บนเว็บทำให้เว็บไซต์สามารถอ่านและเขียนแท็ก NFC เมื่ออยู่ใกล้กับอุปกรณ์ของผู้ใช้ (โดยปกติจะมีขนาด 5-10 ซม. หรือ 2-4 นิ้ว) ขอบเขตปัจจุบันจำกัดอยู่ที่รูปแบบ NFC Data Exchange (NDEF) ซึ่งเป็นรูปแบบข้อความไบนารีขนาดเล็กที่ทำงานได้ในแท็กรูปแบบต่างๆ

โทรศัพท์กำลังเปิดแท็ก NFC เพื่อแลกเปลี่ยนข้อมูล
แผนภาพของการดำเนินการ NFC

กรณีการใช้งานที่แนะนำ

Web NFC จำกัดไว้เฉพาะสำหรับ NDEF เนื่องจากคุณสมบัติด้านความปลอดภัยของการอ่านและการเขียนข้อมูล NDEF นั้นวัดปริมาณได้ง่ายขึ้น ไม่รองรับการดำเนินการ I/O ระดับล่าง (เช่น ISO-DEP, NFC-A/B, NFC-F), โหมดการสื่อสารแบบเพียร์ทูเพียร์ และการจำลองการ์ดตามโฮสต์ (HCE)

ตัวอย่างเว็บไซต์ที่อาจใช้ Web NFC ได้แก่

  • พิพิธภัณฑ์และแกลเลอรีศิลปะสามารถแสดงข้อมูลเพิ่มเติมเกี่ยวกับจอแสดงผล เมื่อผู้ใช้นำอุปกรณ์ไปแตะกับบัตร NFC ใกล้กับการจัดแสดง
  • เว็บไซต์การจัดการพื้นที่โฆษณาจะอ่านหรือเขียนข้อมูลไปยังแท็ก NFC ในคอนเทนเนอร์เพื่ออัปเดตข้อมูลในเนื้อหาได้
  • เว็บไซต์การประชุมสามารถใช้อีเมลนี้เพื่อสแกนป้าย NFC ระหว่างกิจกรรมและตรวจสอบว่ามีการล็อกไว้เพื่อป้องกันไม่ให้มีการเปลี่ยนแปลงข้อมูลที่เขียนไว้ในนั้นอีก
  • โดยเว็บไซต์จะใช้อีเมลนี้เพื่อแชร์ข้อมูลลับเริ่มต้นที่จำเป็นสำหรับสถานการณ์การจัดสรรอุปกรณ์หรือบริการและยังใช้ข้อมูลการกำหนดค่าในโหมดปฏิบัติการได้อีกด้วย
โทรศัพท์สแกนแท็ก NFC หลายรายการ
ภาพการจัดการพื้นที่โฆษณา NFC

สถานะปัจจุบัน

ขั้นตอน สถานะ
1. สร้างข้อความอธิบาย เสร็จสมบูรณ์
2. สร้างฉบับร่างเริ่มต้นของข้อกำหนด เสร็จสมบูรณ์
3. รวบรวมความคิดเห็นและทำซ้ำเกี่ยวกับการออกแบบ เสร็จสมบูรณ์
4. ช่วงทดลองใช้จากต้นทาง เสร็จสมบูรณ์
5. เปิดตัว เสร็จสมบูรณ์

ใช้ Web NFC

การตรวจหาฟีเจอร์

การตรวจหาฟีเจอร์สำหรับฮาร์ดแวร์แตกต่างจากสิ่งที่คุณอาจคุ้นเคย การมี NDEFReader บ่งบอกว่าเบราว์เซอร์รองรับ Web NFC แต่ไม่ได้มีฮาร์ดแวร์ที่จำเป็นอยู่ไหม โดยเฉพาะอย่างยิ่ง หากฮาร์ดแวร์หายไป คำมั่นสัญญาที่ส่งกลับโดยการเรียกบางรายการจะปฏิเสธ ฉันจะให้รายละเอียดเมื่ออธิบายถึง NDEFReader

if ('NDEFReader' in window) { /* Scan and write NFC tags */ }

คำศัพท์

แท็ก NFC เป็นอุปกรณ์ NFC แบบแพสซีฟ ซึ่งหมายความว่าขับเคลื่อนโดยการเหนี่ยวนำแม่เหล็กเมื่ออุปกรณ์ NFC ที่ใช้งานอยู่ (เช่น โทรศัพท์) อยู่ในระยะใกล้ แท็ก NFC มีหลายรูปแบบและหลายรูปแบบ เช่น สติกเกอร์ บัตรเครดิต ข้อมือที่ติดแขน และอื่นๆ

รูปภาพแท็ก NFC แบบโปร่งใส
แท็ก NFC แบบโปร่งใส

ออบเจ็กต์ NDEFReader เป็นจุดแรกเข้าใน Web NFC ที่แสดงฟังก์ชันสำหรับเตรียมการดำเนินการอ่านและ/หรือเขียนที่เสร็จสมบูรณ์เมื่อแท็ก NDEF เข้ามาใกล้กัน NDEF ใน NDEFReader ย่อมาจาก NFC Data Exchange Format ซึ่งเป็นรูปแบบข้อความไบนารีขนาดเล็กที่ฟอรัม NFC เป็นมาตรฐาน

ออบเจ็กต์ NDEFReader มีไว้เพื่อดำเนินการกับข้อความ NDEF ขาเข้าจากแท็ก NFC และเขียนข้อความ NDEF ไปยังแท็ก NFC ภายในช่วง

แท็ก NFC ที่รองรับ NDEF เป็นเหมือนโน้ตโพสต์อิต ทุกคนสามารถอ่านได้ ยกเว้นการเขียนแบบอ่านอย่างเดียว ซึ่งประกอบด้วยข้อความ NDEF เดียว ที่สรุประเบียน NDEF อย่างน้อย 1 รายการ ระเบียน NDEF แต่ละรายการเป็นโครงสร้างไบนารีที่มีเพย์โหลดข้อมูล และข้อมูลประเภทที่เกี่ยวข้อง เว็บ NFC รองรับประเภทระเบียนตามมาตรฐานของฟอรัม NFC ต่อไปนี้ ว่าง, ข้อความ, URL, โปสเตอร์อัจฉริยะ, ประเภท MIME, URL ที่สมบูรณ์, ประเภทภายนอก, ไม่รู้จัก และประเภทในเครื่อง

แผนภาพของข้อความ NDEF
แผนภาพของข้อความ NDEF

สแกนแท็ก NFC

หากต้องการสแกนแท็ก NFC ให้สร้างอินสแตนซ์ NDEFReader ใหม่ก่อน การโทรเรียก scan() จะให้ผลลัพธ์ ผู้ใช้อาจได้รับข้อความแจ้งหากไม่เคยได้รับสิทธิ์ในการเข้าถึงมาก่อน สัญญาจะสิ้นสุดหากเป็นไปตามเงื่อนไขต่อไปนี้ทั้งหมด

  • โดยมีการเรียกเฉพาะการตอบสนองต่อท่าทางสัมผัสของผู้ใช้ เช่น ท่าทางสัมผัสการแตะ หรือการคลิกเมาส์
  • ผู้ใช้อนุญาตให้เว็บไซต์โต้ตอบกับอุปกรณ์ NFC
  • โทรศัพท์ของผู้ใช้รองรับ NFC
  • ผู้ใช้ได้เปิดใช้ NFC ในโทรศัพท์ของตน

เมื่อแก้ปัญหาแล้ว ข้อความ NDEF ที่เข้ามาใหม่จะพร้อมใช้งานโดยการสมัครรับข้อมูลกิจกรรม reading ผ่าน Listener เหตุการณ์ นอกจากนี้ คุณควรสมัครรับข้อมูลเหตุการณ์ readingerror ด้วย เพื่อรับการแจ้งเตือนเมื่อแท็ก NFC ที่ใช้ร่วมกันไม่ได้อยู่ใกล้เคียง

const ndef = new NDEFReader();
ndef.scan().then(() => {
  console.log("Scan started successfully.");
  ndef.onreadingerror = () => {
    console.log("Cannot read data from the NFC tag. Try another one?");
  };
  ndef.onreading = event => {
    console.log("NDEF message read.");
  };
}).catch(error => {
  console.log(`Error! Scan failed to start: ${error}.`);
});

เมื่อแท็ก NFC อยู่ในระยะใกล้ เหตุการณ์ NDEFReadingEvent จะเริ่มทำงาน ซึ่งมีพร็อพเพอร์ตี้ 2 รายการต่อไปนี้

  • serialNumber แสดงหมายเลขซีเรียลของอุปกรณ์ (เช่น 00-11-22-33-44-55-66) หรือสตริงว่างหากไม่มีข้อมูล
  • message แสดงข้อความ NDEF ที่จัดเก็บไว้ในแท็ก NFC

หากต้องการอ่านเนื้อหาของข้อความ NDEF ให้วนซ้ำ message.records และประมวลผลสมาชิก data คนอย่างเหมาะสมตามrecordType สมาชิก data แสดงเป็น DataView เนื่องจากอนุญาตให้จัดการกรณีต่างๆ ที่มีการเข้ารหัสข้อมูลในรูปแบบ UTF-16

ndef.onreading = event => {
  const message = event.message;
  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":
        // TODO: Read text record with record data, lang, and encoding.
        break;
      case "url":
        // TODO: Read URL record with record data.
        break;
      default:
        // TODO: Handle other records with record data.
    }
  }
};

เขียนแท็ก NFC

หากต้องการเขียนแท็ก NFC ให้สร้างอินสแตนซ์ NDEFReader ใหม่ก่อน การโทรติดต่อ write() ให้คำมั่นสัญญา ผู้ใช้อาจได้รับข้อความแจ้งหากก่อนหน้านี้ไม่ได้รับสิทธิ์การเข้าถึง ณ จุดนี้ ข้อความ NDEF มีสถานะเป็น "เตรียมพร้อมแล้ว" และสัญญาว่าจะได้รับการแก้ไขหากเป็นไปตามเงื่อนไขต่อไปนี้ทั้งหมด

  • โดยมีการเรียกเฉพาะการตอบสนองต่อท่าทางสัมผัสของผู้ใช้ เช่น ท่าทางสัมผัสการแตะ หรือการคลิกเมาส์
  • ผู้ใช้อนุญาตให้เว็บไซต์โต้ตอบกับอุปกรณ์ NFC
  • โทรศัพท์ของผู้ใช้รองรับ NFC
  • ผู้ใช้ได้เปิดใช้ NFC ในโทรศัพท์ของตน
  • ผู้ใช้ได้แตะแท็ก NFC และเขียนข้อความ NDEF สำเร็จแล้ว

หากต้องการเขียนข้อความไปยังแท็ก NFC ให้ส่งสตริงไปยังเมธอด write()

const ndef = new NDEFReader();
ndef.write(
  "Hello World"
).then(() => {
  console.log("Message written.");
}).catch(error => {
  console.log(`Write failed :-( try again: ${error}.`);
});

หากต้องการเขียนระเบียน URL ไปยังแท็ก NFC ให้ส่งพจนานุกรมที่แสดงถึงข้อความ NDEF ไปยัง write() ในตัวอย่างด้านล่าง ข้อความ NDEF เป็นพจนานุกรมที่มีคีย์ records ค่าของระเบียนคืออาร์เรย์ของระเบียน ในกรณีนี้ ระเบียน URL ที่กำหนดเป็นออบเจ็กต์ที่ตั้งค่าคีย์ recordType เป็น "url" และชุดคีย์ data เป็นสตริง URL

const ndef = new NDEFReader();
ndef.write({
  records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }]
}).then(() => {
  console.log("Message written.");
}).catch(error => {
  console.log(`Write failed :-( try again: ${error}.`);
});

หรือจะเขียนระเบียนหลายรายการลงในแท็ก NFC ก็ได้

const ndef = new NDEFReader();
ndef.write({ records: [
    { recordType: "url", data: "https://w3c.github.io/web-nfc/" },
    { recordType: "url", data: "https://web.dev/nfc/" }
]}).then(() => {
  console.log("Message written.");
}).catch(error => {
  console.log(`Write failed :-( try again: ${error}.`);
});

หากแท็ก NFC มีข้อความ NDEF ที่ไม่ได้มีไว้เพื่อเขียนทับ ให้ตั้งค่าพร็อพเพอร์ตี้ overwrite เป็น false ในตัวเลือกที่ส่งไปยังเมธอด write() ในกรณีดังกล่าว สัญญาที่แสดงผลจะปฏิเสธหากมีข้อความ NDEF เก็บไว้ในแท็ก NFC แล้ว

const ndef = new NDEFReader();
ndef.write("Writing data on an empty NFC tag is fun!", { overwrite: false })
.then(() => {
  console.log("Message written.");
}).catch(error => {
  console.log(`Write failed :-( try again: ${error}.`);
});

กำหนดให้แท็ก NFC เป็นแบบอ่านอย่างเดียว

คุณสามารถทำให้แท็ก NFC เป็นแบบอ่านอย่างเดียวอย่างถาวรได้เพื่อป้องกันไม่ให้ผู้ใช้ที่เป็นอันตรายเขียนทับเนื้อหาของแท็ก NFC การดำเนินการนี้เป็นกระบวนการทางเดียว และย้อนกลับไม่ได้ เมื่อทำให้แท็ก NFC เป็นแบบอ่านอย่างเดียวแล้ว จะไม่สามารถเขียนแท็กนั้นได้อีก

หากต้องการกำหนดให้แท็ก NFC เป็นแบบอ่านอย่างเดียว ให้สร้างอินสแตนซ์ NDEFReader ใหม่ก่อน การโทรติดต่อ makeReadOnly() ให้คำมั่นสัญญา ผู้ใช้อาจได้รับข้อความแจ้งหากก่อนหน้านี้ไม่ได้รับสิทธิ์การเข้าถึง สัญญาจะสิ้นสุดหากเป็นไปตามเงื่อนไขต่อไปนี้ทั้งหมด

  • โดยมีการเรียกเฉพาะการตอบสนองต่อท่าทางสัมผัสของผู้ใช้ เช่น ท่าทางสัมผัสการแตะ หรือการคลิกเมาส์
  • ผู้ใช้อนุญาตให้เว็บไซต์โต้ตอบกับอุปกรณ์ NFC
  • โทรศัพท์ของผู้ใช้รองรับ NFC
  • ผู้ใช้ได้เปิดใช้ NFC ในโทรศัพท์ของตน
  • ผู้ใช้แตะแท็ก NFC และแท็ก NFC มีสถานะเป็นอ่านอย่างเดียวเรียบร้อยแล้ว
const ndef = new NDEFReader();
ndef.makeReadOnly()
.then(() => {
  console.log("NFC tag has been made permanently read-only.");
}).catch(error => {
  console.log(`Operation failed: ${error}`);
});

ต่อไปนี้เป็นวิธีสร้างแท็ก NFC เป็นแบบอ่านอย่างเดียวอย่างถาวรหลังจากเขียนแท็ก

const ndef = new NDEFReader();
try {
  await ndef.write("Hello world");
  console.log("Message written.");
  await ndef.makeReadOnly();
  console.log("NFC tag has been made permanently read-only after writing to it.");
} catch (error) {
  console.log(`Operation failed: ${error}`);
}

เนื่องจาก makeReadOnly() พร้อมใช้งานใน Android ใน Chrome 100 ขึ้นไป โปรดตรวจสอบว่าฟีเจอร์ต่อไปนี้รองรับฟีเจอร์นี้หรือไม่

if ("NDEFReader" in window && "makeReadOnly" in NDEFReader.prototype) {
  // makeReadOnly() is supported.
}

ความปลอดภัยและสิทธิ์

ทีม Chrome ได้ออกแบบและใช้งาน Web NFC โดยใช้หลักการสำคัญที่ระบุไว้ในการควบคุมการเข้าถึงฟีเจอร์แพลตฟอร์มเว็บที่มีประสิทธิภาพ ซึ่งได้แก่ การควบคุมผู้ใช้ ความโปร่งใส และการยศาสตร์

เนื่องจาก NFC ขยายโดเมนของข้อมูลที่อาจมีในเว็บไซต์ที่เป็นอันตราย ความพร้อมใช้งานของ NFC จึงมีจำกัดเพื่อเพิ่มการรับรู้และควบคุมการใช้ NFC ของผู้ใช้ให้สูงสุด

ภาพหน้าจอข้อความแจ้ง NFC ของเว็บบนเว็บไซต์
ข้อความแจ้งผู้ใช้ NFC ในเว็บ

Web NFC ใช้งานได้กับเฟรมระดับบนสุดและบริบทการท่องเว็บอย่างปลอดภัยเท่านั้น (HTTPS เท่านั้น) ต้นทางต้องขอสิทธิ์ "nfc" ก่อนขณะจัดการท่าทางสัมผัสของผู้ใช้ (เช่น การคลิกปุ่ม) เมธอด NDEFReader scan(), write() และ makeReadOnly() จะทริกเกอร์ข้อความแจ้งผู้ใช้ หากยังไม่ได้รับสิทธิ์เข้าถึงก่อนหน้านี้

  document.querySelector("#scanButton").onclick = async () => {
    const ndef = new NDEFReader();
    // Prompt user to allow website to interact with NFC devices.
    await ndef.scan();
    ndef.onreading = event => {
      // TODO: Handle incoming NDEF messages.
    };
  };

ชุดข้อความแจ้งสิทธิ์ที่เริ่มต้นโดยผู้ใช้และการย้ายอุปกรณ์จริงในการนำอุปกรณ์ไปไว้เหนือแท็ก NFC เป้าหมายจะสะท้อนรูปแบบตัวเลือกที่พบในไฟล์อื่นและ API การเข้าถึงอุปกรณ์

ในการสแกนหรือเขียน หน้าเว็บจะต้องมองเห็นได้เมื่อผู้ใช้แตะแท็ก NFC ด้วยอุปกรณ์ของตน เบราว์เซอร์จะใช้การตอบสนองแบบรู้สึกได้ เพื่อระบุการแตะ การเข้าถึงวิทยุ NFC จะถูกบล็อกหากจอแสดงผลปิดอยู่หรืออุปกรณ์ล็อกอยู่ สำหรับหน้าเว็บที่ไม่แสดง การรับและการพุชเนื้อหา NFC จะถูกระงับ และจะกลับมาทำงานอีกครั้งเมื่อหน้าเว็บกลับมาแสดงอีกครั้ง

API ระดับการเข้าถึงหน้าเว็บช่วยให้คุณติดตามได้เมื่อระดับการเข้าถึงเอกสารมีการเปลี่ยนแปลง

document.onvisibilitychange = event => {
  if (document.hidden) {
    // All NFC operations are automatically suspended when document is hidden.
  } else {
    // All NFC operations are resumed, if needed.
  }
};

ตำราอาหาร

นี่คือตัวอย่างโค้ดบางส่วนที่จะช่วยคุณเริ่มต้น

ตรวจสอบสิทธิ์

Permissions API อนุญาตให้ตรวจสอบว่าได้รับสิทธิ์ "nfc" หรือไม่ ตัวอย่างนี้แสดงวิธีสแกนแท็ก NFC โดยไม่ต้องมีการโต้ตอบของผู้ใช้หากได้รับสิทธิ์เข้าถึงก่อนหน้านี้แล้ว หรือแสดงปุ่มหากไม่มีการโต้ตอบ โปรดทราบว่ากลไกเดียวกับที่ใช้ในการเขียนแท็ก NFC เพราะใช้สิทธิ์เดียวกันขั้นสูง

const ndef = new NDEFReader();

async function startScanning() {
  await ndef.scan();
  ndef.onreading = event => {
    /* handle NDEF messages */
  };
}

const nfcPermissionStatus = await navigator.permissions.query({ name: "nfc" });
if (nfcPermissionStatus.state === "granted") {
  // NFC access was previously granted, so we can start NFC scanning now.
  startScanning();
} else {
  // Show a "scan" button.
  document.querySelector("#scanButton").style.display = "block";
  document.querySelector("#scanButton").onclick = event => {
    // Prompt user to allow UA to send and receive info when they tap NFC devices.
    startScanning();
  };
}

ล้มเลิกการดำเนินการ NFC

การใช้ AbortController แบบเดิมจะช่วยให้ล้มเลิกการดำเนินการ NFC ได้ง่าย ตัวอย่างด้านล่างแสดงวิธีส่ง signal ของ AbortController ผ่านตัวเลือกเมธอด NDEFReader scan(), makeReadOnly(), write() และล้มเลิกการดำเนินการของ NFC ทั้ง 2 รายการพร้อมกัน

const abortController = new AbortController();
abortController.signal.onabort = event => {
  // All NFC operations have been aborted.
};

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

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

document.querySelector("#abortButton").onclick = event => {
  abortController.abort();
};

อ่านหลังจากเขียน

การใช้ write() ตามด้วย scan() กับ AbortController แบบพื้นฐานจะทำให้อ่านแท็ก NFC ได้หลังจากเขียนข้อความไปยังแท็ก NFC ตัวอย่างด้านล่างแสดงวิธีเขียนข้อความไปยังแท็ก NFC และอ่านข้อความใหม่ในแท็ก NFC อุปกรณ์หยุดสแกนหลังจากผ่านไป 3 วินาที

// Waiting for user to tap NFC tag to write to it...
const ndef = new NDEFReader();
await ndef.write("Hello world");
// Success! Message has been written.

// Now scanning for 3 seconds...
const abortController = new AbortController();
await ndef.scan({ signal: abortController.signal });
const message = await new Promise((resolve) => {
  ndef.onreading = (event) => resolve(event.message);
});
// Success! Message has been read.

await new Promise((r) => setTimeout(r, 3000));
abortController.abort();
// Scanning is now stopped.

อ่านและเขียนบันทึกข้อความ

ถอดรหัสบันทึกข้อความ data ได้ด้วยอินสแตนซ์ TextDecoder ที่มีพร็อพเพอร์ตี้ encoding ของระเบียน โปรดทราบว่าภาษาของระเบียนข้อความจะใช้ได้ผ่านพร็อพเพอร์ตี้ lang

function readTextRecord(record) {
  console.assert(record.recordType === "text");
  const textDecoder = new TextDecoder(record.encoding);
  console.log(`Text: ${textDecoder.decode(record.data)} (${record.lang})`);
}

หากต้องการเขียนระเบียนข้อความแบบง่าย ให้ส่งสตริงไปยังเมธอด write() ของ NDEFReader

const ndef = new NDEFReader();
await ndef.write("Hello World");

ระเบียนข้อความจะเป็น UTF-8 โดยค่าเริ่มต้น และจะถือว่าเป็นภาษาของเอกสารปัจจุบัน แต่สามารถระบุพร็อพเพอร์ตี้ทั้ง 2 รายการ (encoding และ lang) ได้โดยใช้ไวยากรณ์แบบเต็มสำหรับการสร้างระเบียน NDEF ที่กำหนดเอง

function a2utf16(string) {
  let result = new Uint16Array(string.length);
  for (let i = 0; i < string.length; i++) {
    result[i] = string.codePointAt(i);
  }
  return result;
}

const textRecord = {
  recordType: "text",
  lang: "fr",
  encoding: "utf-16",
  data: a2utf16("Bonjour, François !")
};

const ndef = new NDEFReader();
await ndef.write({ records: [textRecord] });

อ่านและเขียนระเบียน URL

ใช้ TextDecoder เพื่อถอดรหัส data ของระเบียน

function readUrlRecord(record) {
  console.assert(record.recordType === "url");
  const textDecoder = new TextDecoder();
  console.log(`URL: ${textDecoder.decode(record.data)}`);
}

หากต้องการเขียนระเบียน URL ให้ส่งพจนานุกรมข้อความ NDEF ไปยังเมธอด NDEFReader write() ระเบียน URL ที่อยู่ในข้อความ NDEF กำหนดเป็นออบเจ็กต์ที่มีการตั้งค่าคีย์ recordType เป็น "url" และชุดคีย์ data เป็นสตริง URL

const urlRecord = {
  recordType: "url",
  data:"https://w3c.github.io/web-nfc/"
};

const ndef = new NDEFReader();
await ndef.write({ records: [urlRecord] });

อ่านและเขียนระเบียนประเภท MIME

พร็อพเพอร์ตี้ mediaType ของระเบียนประเภท MIME แสดงถึงประเภท MIME ของเพย์โหลดระเบียน NDEF เพื่อให้ถอดรหัส data ได้อย่างถูกต้อง เช่น ใช้ JSON.parse เพื่อถอดรหัสข้อความ JSON และองค์ประกอบรูปภาพเพื่อถอดรหัสข้อมูลรูปภาพ

function readMimeRecord(record) {
  console.assert(record.recordType === "mime");
  if (record.mediaType === "application/json") {
    const textDecoder = new TextDecoder();
    console.log(`JSON: ${JSON.parse(decoder.decode(record.data))}`);
  }
  else if (record.mediaType.startsWith('image/')) {
    const blob = new Blob([record.data], { type: record.mediaType });
    const img = new Image();
    img.src = URL.createObjectURL(blob);
    document.body.appendChild(img);
  }
  else {
    // TODO: Handle other MIME types.
  }
}

หากต้องการเขียนระเบียนประเภท MIME ให้ส่งพจนานุกรมข้อความ NDEF ไปยังเมธอด NDEFReader write() ระเบียนประเภท MIME ที่มีอยู่ในข้อความ NDEF คือออบเจ็กต์ที่ตั้งค่าคีย์ recordType เป็น "mime" คีย์ mediaType ที่ตั้งค่าเป็นประเภท MIME จริงของเนื้อหา และชุดคีย์ data เป็นออบเจ็กต์ซึ่งอาจเป็น ArrayBuffer หรือมอบมุมมองให้กับ ArrayBuffer (เช่น Uint8Array, DataView)

const encoder = new TextEncoder();
const data = {
  firstname: "François",
  lastname: "Beaufort"
};
const jsonRecord = {
  recordType: "mime",
  mediaType: "application/json",
  data: encoder.encode(JSON.stringify(data))
};

const imageRecord = {
  recordType: "mime",
  mediaType: "image/png",
  data: await (await fetch("icon1.png")).arrayBuffer()
};

const ndef = new NDEFReader();
await ndef.write({ records: [jsonRecord, imageRecord] });

อ่านและเขียนระเบียน URL แบบสัมบูรณ์

ระเบียน URL สัมบูรณ์ data ถอดรหัสได้ด้วย TextDecoder แบบง่าย

function readAbsoluteUrlRecord(record) {
  console.assert(record.recordType === "absolute-url");
  const textDecoder = new TextDecoder();
  console.log(`Absolute URL: ${textDecoder.decode(record.data)}`);
}

หากต้องการเขียนระเบียน URL แบบสัมบูรณ์ ให้ส่งพจนานุกรมข้อความ NDEF ไปยังเมธอด write() ของ NDEFReader ระเบียน URL สัมบูรณ์ที่อยู่ในข้อความ NDEF ได้รับการกำหนดเป็นออบเจ็กต์ที่ตั้งค่าคีย์ recordType เป็น "absolute-url" และชุดคีย์ data เป็นสตริง URL

const absoluteUrlRecord = {
  recordType: "absolute-url",
  data:"https://w3c.github.io/web-nfc/"
};

const ndef = new NDEFReader();
await ndef.write({ records: [absoluteUrlRecord] });

อ่านและเขียนระเบียนโปสเตอร์อัจฉริยะ

ระเบียนโปสเตอร์อัจฉริยะ (ใช้ในโฆษณานิตยสาร ใบปลิว บิลบอร์ด ฯลฯ) อธิบายเนื้อหาเว็บบางรายการว่าเป็นระเบียน NDEF ที่มีข้อความ NDEF เป็นเพย์โหลด เรียก record.toRecords() เพื่อเปลี่ยน data เป็นรายการระเบียนที่มีอยู่ในระเบียนโปสเตอร์อัจฉริยะ โดยควรมีระเบียน URL, ระเบียนข้อความสำหรับชื่อ, ระเบียนประเภท MIME สำหรับรูปภาพ และระเบียนประเภทภายในที่กำหนดเองบางรายการ เช่น ":t", ":act" และ ":s" ตามลำดับสำหรับประเภท การดำเนินการ และขนาดของระเบียนโปสเตอร์อัจฉริยะ

ระเบียนประเภทในเครื่องจะไม่ซ้ำกันภายในบริบทภายในของระเบียน NDEF ที่มีเท่านั้น ใช้หมวดหมู่เหล่านี้เมื่อความหมายของประเภทไม่มีความสำคัญนอกเหนือจากบริบทในท้องถิ่นของระเบียนที่มี และเมื่อการใช้พื้นที่เก็บข้อมูลเป็นข้อจำกัดที่จริงจัง ชื่อระเบียนประเภทในเครื่องจะขึ้นต้นด้วย : ใน Web NFC เสมอ (เช่น ":t", ":s", ":act") เป็นต้น เพื่อแยกระเบียนข้อความออกจากระเบียนข้อความประเภทในเครื่อง

function readSmartPosterRecord(smartPosterRecord) {
  console.assert(record.recordType === "smart-poster");
  let action, text, url;

  for (const record of smartPosterRecord.toRecords()) {
    if (record.recordType == "text") {
      const decoder = new TextDecoder(record.encoding);
      text = decoder.decode(record.data);
    } else if (record.recordType == "url") {
      const decoder = new TextDecoder();
      url = decoder.decode(record.data);
    } else if (record.recordType == ":act") {
      action = record.data.getUint8(0);
    } else {
      // TODO: Handle other type of records such as `:t`, `:s`.
    }
  }

  switch (action) {
    case 0:
      // Do the action
      break;
    case 1:
      // Save for later
      break;
    case 2:
      // Open for editing
      break;
  }
}

หากต้องการเขียนระเบียนโปสเตอร์อัจฉริยะ ให้ส่งข้อความ NDEF ไปยังเมธอด write() ของ NDEFReader ระเบียนโปสเตอร์อัจฉริยะที่มีอยู่ในข้อความ NDEF นั้นกำหนดเป็นออบเจ็กต์ที่ตั้งค่าคีย์ recordType เป็น "smart-poster" และตั้งค่าคีย์ data เป็นออบเจ็กต์ที่แสดงถึงข้อความ NDEF ที่อยู่ในระเบียน Smart Post (อีกครั้ง)

const encoder = new TextEncoder();
const smartPosterRecord = {
  recordType: "smart-poster",
  data: {
    records: [
      {
        recordType: "url", // URL record for smart poster content
        data: "https://my.org/content/19911"
      },
      {
        recordType: "text", // title record for smart poster content
        data: "Funny dance"
      },
      {
        recordType: ":t", // type record, a local type to smart poster
        data: encoder.encode("image/gif") // MIME type of smart poster content
      },
      {
        recordType: ":s", // size record, a local type to smart poster
        data: new Uint32Array([4096]) // byte size of smart poster content
      },
      {
        recordType: ":act", // action record, a local type to smart poster
        // do the action, in this case open in the browser
        data: new Uint8Array([0])
      },
      {
        recordType: "mime", // icon record, a MIME type record
        mediaType: "image/png",
        data: await (await fetch("icon1.png")).arrayBuffer()
      },
      {
        recordType: "mime", // another icon record
        mediaType: "image/jpg",
        data: await (await fetch("icon2.jpg")).arrayBuffer()
      }
    ]
  }
};

const ndef = new NDEFReader();
await ndef.write({ records: [smartPosterRecord] });

อ่านและเขียนระเบียนประเภทภายนอก

หากต้องการสร้างระเบียนที่แอปพลิเคชันกำหนด ให้ใช้ระเบียนประเภทภายนอก ซึ่งอาจมีข้อความ NDEF เป็นเพย์โหลดที่เข้าถึงได้ด้วย toRecords() ชื่อของพวกเขามีชื่อโดเมนขององค์กรที่ออก โคลอน และชื่อประเภทที่มีความยาวอย่างน้อย 1 อักขระ เช่น "example.com:foo"

function readExternalTypeRecord(externalTypeRecord) {
  for (const record of externalTypeRecord.toRecords()) {
    if (record.recordType == "text") {
      const decoder = new TextDecoder(record.encoding);
      console.log(`Text: ${textDecoder.decode(record.data)} (${record.lang})`);
    } else if (record.recordType == "url") {
      const decoder = new TextDecoder();
      console.log(`URL: ${decoder.decode(record.data)}`);
    } else {
      // TODO: Handle other type of records.
    }
  }
}

หากต้องการเขียนระเบียนประเภทภายนอก ให้ส่งพจนานุกรมข้อความ NDEF ไปยังเมธอด write() ของ NDEFReader ระเบียนประเภทภายนอกที่มีอยู่ในข้อความ NDEF คือออบเจ็กต์ที่มีการตั้งค่าคีย์ recordType เป็นชื่อของประเภทภายนอก และชุดคีย์ data เป็นออบเจ็กต์ที่แสดงถึงข้อความ NDEF ที่อยู่ในระเบียนประเภทภายนอก โปรดทราบว่าคีย์ data อาจเป็น ArrayBuffer หรือให้มุมมองแก่ ArrayBuffer ได้ด้วย (เช่น Uint8Array, DataView)

const externalTypeRecord = {
  recordType: "example.game:a",
  data: {
    records: [
      {
        recordType: "url",
        data: "https://example.game/42"
      },
      {
        recordType: "text",
        data: "Game context given here"
      },
      {
        recordType: "mime",
        mediaType: "image/png",
        data: await (await fetch("image.png")).arrayBuffer()
      }
    ]
  }
};

const ndef = new NDEFReader();
ndef.write({ records: [externalTypeRecord] });

อ่านและเขียนระเบียนเปล่า

ระเบียนเปล่าไม่มีเพย์โหลด

หากต้องการเขียนระเบียนเปล่า ให้ส่งพจนานุกรมข้อความ NDEF ไปยังเมธอด NDEFReader write() ระเบียนว่างที่อยู่ในข้อความ NDEF ได้รับการกำหนดเป็นออบเจ็กต์ที่มีการตั้งค่าคีย์ recordType เป็น "empty"

const emptyRecord = {
  recordType: "empty"
};

const ndef = new NDEFReader();
await ndef.write({ records: [emptyRecord] });

การสนับสนุนเบราว์เซอร์

เว็บ NFC ใช้งานได้บน Android ใน Chrome 89

เคล็ดลับสำหรับนักพัฒนา

นี่คือรายการสิ่งที่ฉันอยากทราบตอนที่เริ่มเล่น Web NFC

  • โดย Android จะจัดการแท็ก NFC ที่ระดับระบบปฏิบัติการก่อนที่ Web NFC จะทำงาน
  • คุณจะเห็นไอคอน NFC ใน material.io
  • ใช้ระเบียน NDEF id เพื่อระบุระเบียนได้อย่างง่ายดายเมื่อจำเป็น
  • แท็ก NFC ที่ไม่ได้จัดรูปแบบซึ่งรองรับ NDEF จะมีระเบียนประเภทว่างอยู่รายการเดียว
  • การเขียนระเบียนแอปพลิเคชัน Android นั้นง่ายดายดังที่แสดงด้านล่าง
const encoder = new TextEncoder();
const aarRecord = {
  recordType: "android.com:pkg",
  data: encoder.encode("com.example.myapp")
};

const ndef = new NDEFReader();
await ndef.write({ records: [aarRecord] });

เดโม

ทดลองใช้ตัวอย่างอย่างเป็นทางการ และดูการสาธิต Web NFC เจ๋งๆ:

การสาธิตบัตร NFC บนเว็บที่งาน Chrome Dev Summit 2019

ความคิดเห็น

Web NFC Community Group และทีม Chrome ต้องการทราบความคิดเห็นและประสบการณ์การใช้งาน Web NFC ของคุณ

บอกให้เราทราบเกี่ยวกับการออกแบบ API

มีบางอย่างเกี่ยวกับ API ที่ทำงานไม่ได้ตามที่คาดไว้ไหม หรือมีวิธีหรือคุณสมบัติที่ขาดหายไปที่คุณจำเป็นต้องใช้ในการนำแนวคิดของคุณไปปรับใช้

โปรดแจ้งปัญหาเกี่ยวกับข้อมูลจำเพาะในที่เก็บ GitHub ของเว็บ NFC หรือแสดงความคิดเห็นเกี่ยวกับปัญหาที่มีอยู่

รายงานปัญหาเกี่ยวกับการใช้งาน

คุณพบข้อบกพร่องในการใช้งาน Chrome หรือไม่ หรือการใช้งานแตกต่างจาก ข้อกำหนดหรือไม่

รายงานข้อบกพร่องที่ https://new.crbug.com อย่าลืมระบุรายละเอียดให้มากที่สุดเท่าที่จะทำได้ ระบุวิธีการง่ายๆ ในการสร้างข้อบกพร่องอีกครั้ง และตั้งค่าคอมโพเนนต์เป็น Blink>NFC Glitch เหมาะสำหรับการแชร์ การเรียกซ้ำที่ง่ายและรวดเร็ว

แสดงการสนับสนุน

คุณวางแผนที่จะใช้ Web NFC หรือไม่ การสนับสนุนสาธารณะของคุณช่วยให้ทีม Chrome จัดลำดับความสำคัญของคุณลักษณะต่างๆ และแสดงให้ผู้ให้บริการเบราว์เซอร์รายอื่นๆ เห็นว่าการสนับสนุนนั้นสำคัญเพียงใด

ส่งทวีตไปที่ @ChromiumDev โดยใช้แฮชแท็ก #WebNFC และแจ้งให้เราทราบว่าคุณใช้แฮชแท็กนี้ที่ไหนและอย่างไร

ลิงก์ที่มีประโยชน์

ข้อความแสดงการยอมรับ

ขอขอบคุณอย่างยิ่งสำหรับทีมงาน Intel ที่นำ Web NFC มาใช้งาน Google Chrome อาศัยชุมชนคณะกรรมการที่ทำงานร่วมกันเพื่อผลักดันโครงการ Chromium ให้ก้าวไปข้างหน้า ไม่ใช่ทุกคนของ Chromium ที่จะเป็น Googler และผู้มีส่วนร่วม เหล่านี้สมควรได้รับการยกย่องชมเชยเป็นพิเศษ!