Excalidraw และ Fugu: ปรับปรุงเส้นทางของผู้ใช้หลัก

เทคโนโลยีใดๆ ที่ก้าวหน้ามากพอไม่สามารถแยกความแตกต่างจากเวทมนตร์ได้เลย เว้นแต่คุณจะเข้าใจ ฉันชื่อ Thomas Steiner ฉันทำงานที่ฝ่ายนักพัฒนาซอฟต์แวร์สัมพันธ์ที่ Google และในการเขียนสรุปการพูดคุยในงาน Google I/O นี้ ผมจะพูดถึง Fugu API ใหม่บางส่วน รวมถึงวิธีปรับปรุงเส้นทางของผู้ใช้หลักใน Excalidraw PWA เพื่อให้คุณได้แรงบันดาลใจจากแนวคิดเหล่านี้และนำไปใช้กับแอปของคุณเอง

ฉันมาที่ Excalidraw ได้อย่างไร

ขอเริ่มที่เรื่องเล่าก่อน ในวันที่ 1 มกราคม 2020 Christopher Chedeau วิศวกรซอฟต์แวร์ของ Facebook ได้ทวีตเกี่ยวกับแอปวาดภาพขนาดเล็กที่เขาได้เริ่มเข้ามาพัฒนา ด้วยเครื่องมือนี้ คุณจะวาดกล่องและลูกศรที่ทำให้รู้สึกเหมือนการ์ตูนหรือวาดด้วยมือได้ ในวันถัดไป คุณยังสามารถวาดจุดไข่ปลาและข้อความ ตลอดจนเลือกวัตถุและย้ายตำแหน่งไปรอบๆ เมื่อวันที่ 3 มกราคม แอปนั้นชื่อ Excalidraw แล้ว การซื้อชื่อโดเมนก็เป็นหนึ่งในกิจกรรมแรกๆ ของคริสโตเฟอร์ด้วย ตอนนี้คุณก็สามารถใช้สีและส่งออกภาพวาดทั้งภาพเป็น PNG ได้แล้ว

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

เมื่อวันที่ 15 มกราคม คริสโตเฟอร์เขียนบล็อกโพสต์ที่ดึงดูดความสนใจของเราอย่างมากใน Twitter รวมถึงของฉันด้วย โพสต์นี้เริ่มต้นด้วยสถิติที่น่าประทับใจ:

  • ผู้ใช้ที่ใช้งานอยู่ที่ไม่ซ้ำกัน 1.2 หมื่นคน
  • 1.5 พันดาวบน GitHub
  • ผู้ร่วมให้ข้อมูล 26 ราย

สำหรับโครงการที่เริ่มต้นเมื่อ 2 สัปดาห์ที่แล้ว ก็ถือว่าไม่เลวเลยทีเดียว แต่สิ่งที่ดึงดูดใจผมได้มาก คือสิ่งที่ผมสนใจจริงๆ ในโพสต์ คริสโตเฟอร์เขียนว่าเขาได้ลองสิ่งใหม่ๆ ในครั้งนี้: การเปิดโอกาสให้ทุกคนเข้าถึงแอปแบบพุลคำขอแบบมีเงื่อนไขโดยไม่มีเงื่อนไข วันเดียวกับที่อ่านบล็อกโพสต์ ฉันมีคำขอแบบพุลที่เพิ่มการรองรับ File System Access API ให้กับ Excalidraw เพื่อแก้ไขคำขอฟีเจอร์ที่ผู้อื่นยื่นไป

ภาพหน้าจอของทวีตที่ฉันประกาศ PR

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

ปัจจุบัน Excalidraw เป็น Progressive Web App แบบติดตั้งอย่างสมบูรณ์ที่มีการรองรับแบบออฟไลน์ โหมดมืดที่น่าทึ่ง และความสามารถในการเปิดและบันทึกไฟล์ด้วย File System Access API

ภาพหน้าจอของ Excalidraw PWA ที่มีสถานะปัจจุบัน

Lipis อธิบายเหตุผลที่เขาทุ่มเทเวลามากมายให้แก่ Excalidraw

นี่จึงเป็นจุดจบของเรื่องราว "วิธีที่ฉันมาพบกับ Excalidraw" แต่ก่อนที่จะเจาะลึกในฟีเจอร์เจ๋งๆ ของ Excalidraw ผมยินดีอย่างยิ่งที่ได้แนะนำ Panayiotis Panayiotis Lipiridis ในอินเทอร์เน็ตหรือที่เรียกกันสั้นๆ ว่า lipis เป็นผู้มีส่วนร่วมอย่างล้นหลามที่สุดสำหรับ Excalidraw ฉันถาม Lipis ว่าอะไรเป็นแรงจูงใจให้เขาทุ่มเทเวลามากมายให้แก่ Excalidraw

เช่นเดียวกับคนอื่นๆ ผมได้รู้เกี่ยวกับโครงการนี้จากทวีตของคริสโตเฟอร์ ผลงานแรกของฉันคือการเพิ่ม Open Color library ซึ่งเป็นสีที่ยังคงเป็นส่วนหนึ่งของ Excalidraw ในปัจจุบัน เมื่อโปรเจ็กต์เติบโตขึ้นและเราได้รับคําขอจํานวนมาก การมีส่วนร่วมครั้งใหญ่ครั้งต่อไปของฉันคือการสร้างแบ็กเอนด์สำหรับจัดเก็บภาพวาดเพื่อให้ผู้ใช้สามารถแชร์ภาพวาดได้ แต่สิ่งที่กระตุ้นให้ฉันมีส่วนร่วมคือผู้ที่ได้ลองใช้ Excalidraw กำลังหาข้อแก้ตัวที่จะใช้อีกครั้ง

ฉันเห็นด้วยกับ Lipis จริงๆ ผู้ที่ลองใช้ Excalidraw ต้องการค้นหาข้อแก้ตัวที่จะใช้อีกครั้ง

การทำงานของ Excalidraw

ตอนนี้ผมจะพาไปดูวิธีใช้ Excalidraw ในทางปฏิบัติ ฉันไม่ใช่ศิลปินที่ดี แต่โลโก้ Google I/O นั้นใช้ง่ายมาก ฉันขอลองเลย กล่องคือ "i" เส้นอาจเป็นเครื่องหมายทับ และ "o" คือวงกลม และกดแป้น shift ค้างไว้ เพื่อให้รูปภาพวงกลมสมบูรณ์แบบ ผมจะลากเครื่องหมายทับไปเล็กน้อย เพื่อให้ดูดีขึ้น มาเพิ่มสีให้กับ "i" และ "o" กัน สีน้ำเงินก็ดี หรืออาจเป็น รูปแบบการเติมสีอื่น เข้าใจทุกอย่างจริงๆ หรือแบบครอสแฮทช์ ไม่นะ แฮชัวร์ดูดีมากเลย อาจจะยังไม่สมบูรณ์แบบ แต่นั่นคือแนวคิดของ Excalidraw ดังนั้นผมขอบันทึกไว้ก่อน

คลิกไอคอนบันทึกแล้วป้อนชื่อไฟล์ในกล่องโต้ตอบบันทึกไฟล์ ใน Chrome ซึ่งเป็นเบราว์เซอร์ที่รองรับ File System Access API ซึ่งไม่ใช่การดาวน์โหลด แต่เป็นการบันทึกจริงๆ โดยที่ผมเลือกตำแหน่งและชื่อไฟล์ได้ และถ้าแก้ไขแล้ว ก็จะบันทึกไปที่ไฟล์เดียวกันได้

ขอเปลี่ยนโลโก้และทำให้ "i" เป็นสีแดง หากคลิกบันทึกอีกครั้ง การแก้ไขก็จะบันทึกไปยังไฟล์เดิม ขอยืนยันว่าจะล้างผ้าใบแล้วเปิดไฟล์อีกครั้ง คุณจะเห็นว่าโลโก้สีแดงน้ำเงินที่แก้ไขแล้ว กลับมาปรากฏอีกครั้ง

การทำงานกับไฟล์

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

การเปิดไฟล์

แล้วความลับคืออะไร การเปิดและบันทึกจะทำงานในเบราว์เซอร์ต่างๆ ที่อาจหรือไม่รองรับ File System Access API ได้อย่างไร การเปิดไฟล์ใน Excalidraw จะเกิดขึ้นในฟังก์ชันที่ชื่อว่า loadFromJSON)() ซึ่งจะเรียกฟังก์ชันที่เรียกว่า fileOpen()

export const loadFromJSON = async (localAppState: AppState) => {
  const blob = await fileOpen({
    description: 'Excalidraw files',
    extensions: ['.json', '.excalidraw', '.png', '.svg'],
    mimeTypes: ['application/json', 'image/png', 'image/svg+xml'],
  });
  return loadFromBlob(blob, localAppState);
};

ฟังก์ชัน fileOpen() ที่มาจากไลบรารีขนาดเล็กที่ฉันเขียนไว้ชื่อ browser-fs-access ที่เราใช้ใน Excalidraw ไลบรารีนี้ให้สิทธิ์เข้าถึงระบบไฟล์ผ่าน File System Access API ที่มีไฟล์สำรองแบบเดิม ดังนั้นจึงใช้ในเบราว์เซอร์ใดก็ได้

ก่อนอื่นฉันขอแสดงการใช้งาน API ที่ได้รับการสนับสนุน หลังจากต่อรองประเภท MIME และนามสกุลไฟล์ที่ยอมรับแล้ว ส่วนส่วนกลางคือการเรียกใช้ฟังก์ชัน showOpenFilePicker() ของ File System Access API ฟังก์ชันนี้จะแสดงผลอาร์เรย์ของไฟล์หรือไฟล์เดียว โดยจะขึ้นอยู่กับว่ามีการเลือกหลายไฟล์หรือไม่ ที่เหลือก็แค่วางแฮนเดิลไฟล์บนออบเจ็กต์ไฟล์เพื่อให้ดึงกลับมาได้ใหม่

export default async (options = {}) => {
  const accept = {};
  // Not shown: deal with extensions and MIME types.
  const handleOrHandles = await window.showOpenFilePicker({
    types: [
      {
        description: options.description || '',
        accept: accept,
      },
    ],
    multiple: options.multiple || false,
  });
  const files = await Promise.all(handleOrHandles.map(getFileWithHandle));
  if (options.multiple) return files;
  return files[0];
  const getFileWithHandle = async (handle) => {
    const file = await handle.getFile();
    file.handle = handle;
    return file;
  };
};

การใช้งานวิดีโอสำรองขึ้นอยู่กับองค์ประกอบ input ประเภท "file" หลังจากเจรจาเกี่ยวกับประเภทและส่วนขยาย MIME ที่จะยอมรับแล้ว ขั้นตอนต่อไปคือการคลิกองค์ประกอบอินพุตโดยใช้โปรแกรมเพื่อให้กล่องโต้ตอบการเปิดไฟล์ปรากฏขึ้น การเปลี่ยนแปลง กล่าวคือ เมื่อผู้ใช้เลือกไฟล์ไว้ 1 ไฟล์หรือหลายไฟล์ สัญญาก็จะสิ้นสุดลง

export default async (options = {}) => {
  return new Promise((resolve) => {
    const input = document.createElement('input');
    input.type = 'file';
    const accept = [
      ...(options.mimeTypes ? options.mimeTypes : []),
      options.extensions ? options.extensions : [],
    ].join();
    input.multiple = options.multiple || false;
    input.accept = accept || '*/*';
    input.addEventListener('change', () => {
      resolve(input.multiple ? Array.from(input.files) : input.files[0]);
    });
    input.click();
  });
};

กำลังบันทึกไฟล์

ตอนนี้จะบันทึก ใน Excalidraw การบันทึกจะเกิดขึ้นในฟังก์ชัน saveAsJSON() ขั้นแรกจะเรียงอันดับอาร์เรย์องค์ประกอบ Excalidraw เป็น JSON แล้วแปลง JSON เป็น Blob จากนั้นจึงเรียกใช้ฟังก์ชันชื่อ fileSave() ฟังก์ชันนี้ยังมีไลบรารี Browser-fs-access อีกด้วย

export const saveAsJSON = async (
  elements: readonly ExcalidrawElement[],
  appState: AppState,
) => {
  const serialized = serializeAsJSON(elements, appState);
  const blob = new Blob([serialized], {
    type: 'application/vnd.excalidraw+json',
  });
  const fileHandle = await fileSave(
    blob,
    {
      fileName: appState.name,
      description: 'Excalidraw file',
      extensions: ['.excalidraw'],
    },
    appState.fileHandle,
  );
  return { fileHandle };
};

ขอทบทวนการใช้งานสำหรับเบราว์เซอร์ที่รองรับ File System Access API ก่อน โดย 2 บรรทัดแรกอาจดูเกี่ยวข้องกันเล็กน้อย แต่ทั้งหมดที่มีเพียงการเจรจาเกี่ยวกับประเภท MIME และส่วนขยายไฟล์ เมื่อฉันบันทึกไว้ก่อนหน้านี้และมีแฮนเดิลไฟล์อยู่แล้ว ไม่จำเป็นต้องแสดงกล่องโต้ตอบการบันทึก แต่ถ้าเป็นการบันทึกครั้งแรก กล่องโต้ตอบของไฟล์จะปรากฏขึ้นและแอปจะรับการจัดการไฟล์กลับคืนมาเพื่อใช้ในอนาคต ส่วนที่เหลือจะเป็นเพียงการเขียนลงในไฟล์ ซึ่งเกิดขึ้นผ่านสตรีมที่เขียนได้

export default async (blob, options = {}, handle = null) => {
  options.fileName = options.fileName || 'Untitled';
  const accept = {};
  // Not shown: deal with extensions and MIME types.
  handle =
    handle ||
    (await window.showSaveFilePicker({
      suggestedName: options.fileName,
      types: [
        {
          description: options.description || '',
          accept: accept,
        },
      ],
    }));
  const writable = await handle.createWritable();
  await writable.write(blob);
  await writable.close();
  return handle;
};

ฟีเจอร์ "บันทึกเป็น"

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

การใช้งานสำหรับเบราว์เซอร์ที่ไม่รองรับ File System Access API นั้นสั้น เนื่องจากวิธีการคือสร้างองค์ประกอบ Anchor ที่มีแอตทริบิวต์ download ที่มีค่าเป็นชื่อไฟล์ที่ต้องการและใช้ blob URL เป็นค่าแอตทริบิวต์ href

export default async (blob, options = {}) => {
  const a = document.createElement('a');
  a.download = options.fileName || 'Untitled';
  a.href = URL.createObjectURL(blob);
  a.addEventListener('click', () => {
    setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
  });
  a.click();
};

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

ลากและวาง

การผสานรวมระบบที่ฉันชอบบนเดสก์ท็อปคือการลากและวาง ใน Excalidraw เมื่อฉันวางไฟล์ .excalidraw ลงในแอปพลิเคชัน ไฟล์จะเปิดขึ้นทันทีและฉันเริ่มแก้ไขได้ ในเบราว์เซอร์ที่รองรับ File System Access API ผมก็จะบันทึกการเปลี่ยนแปลงได้ทันที ไม่จำเป็นต้องผ่านกล่องโต้ตอบการบันทึกไฟล์เนื่องจากได้แฮนเดิลไฟล์ที่จำเป็นมาจากการลากและวาง

ข้อมูลลับสำหรับการดำเนินการนี้คือการเรียกใช้ getAsFileSystemHandle() ในรายการการโอนข้อมูลเมื่อมีการรองรับ File System Access API จากนั้นจะส่งแฮนเดิลไฟล์นี้ไปยัง loadFromBlob() ซึ่งคุณอาจจำได้จาก 2-3 ย่อหน้าด้านบน คุณสามารถดำเนินการกับไฟล์ได้หลายอย่าง เช่น การเปิด การบันทึก การบันทึกมากเกินไป ลาก วาง เพื่อนร่วมงานของฉัน Pete และฉันได้บันทึกเคล็ดลับทั้งหมดเหล่านี้และเรื่องอื่นๆ ไว้ในบทความของเราเพื่อให้คุณติดตามได้เผื่อว่าเรื่องพวกนี้เร็วเกินไป

const file = event.dataTransfer?.files[0];
if (file?.type === 'application/json' || file?.name.endsWith('.excalidraw')) {
  this.setState({ isLoading: true });
  // Provided by browser-fs-access.
  if (supported) {
    try {
      const item = event.dataTransfer.items[0];
      file as any.handle = await item as any
        .getAsFileSystemHandle();
    } catch (error) {
      console.warn(error.name, error.message);
    }
  }
  loadFromBlob(file, this.state).then(({ elements, appState }) =>
    // Load from blob
  ).catch((error) => {
    this.setState({ isLoading: false, errorMessage: error.message });
  });
}

การแชร์ไฟล์

ปัจจุบันมีการผสานรวมระบบอีกอย่างหนึ่งใน Android, ChromeOS และ Windows คือการใช้ Web Share Target API ฉันอยู่ในแอป Files ในโฟลเดอร์Downloads ฉันเห็น 2 ไฟล์ โดยไฟล์หนึ่งไม่มีชื่อ untitled และการประทับเวลา ในการตรวจสอบว่า มีข้อมูลอะไรบ้าง ผมจะคลิกที่จุด 3 จุด จากนั้นก็แชร์ และตัวเลือกหนึ่งที่ปรากฏขึ้นคือ Excalidraw เมื่อแตะไอคอน ฉันจะเห็นว่าไฟล์มีโลโก้ I/O อีกครั้ง

Lipis ใน Electron เวอร์ชันที่เลิกใช้งานแล้ว

สิ่งหนึ่งที่คุณสามารถทำได้กับไฟล์ที่ผมยังไม่ได้พูดถึงก็คือ .doubleclick ไฟล์ดังกล่าว สิ่งที่มักจะเกิดขึ้นเมื่อคุณ doubleclick ไฟล์คือ แอปที่เชื่อมโยงกับประเภท MIME ของไฟล์จะเปิดขึ้น ตัวอย่างเช่น สำหรับ .docx นี่จะเป็น Microsoft Word

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

ผู้คนถามหาแอป Electron มาตั้งแต่แรก สาเหตุส่วนใหญ่ก็ต้องการเปิดไฟล์ด้วยการดับเบิลคลิก และเรายังตั้งใจที่จะนำแอปไปเผยแพร่ใน App Store ด้วย ในขณะเดียวกัน มีผู้แนะนำให้สร้าง PWA แทน เราจึงทำทั้ง 2 อย่าง โชคดีที่เรารู้จักกับ Project Fugu API เช่น การเข้าถึงระบบไฟล์ การเข้าถึงคลิปบอร์ด การจัดการไฟล์ และอื่นๆ อีกมากมาย เพียงคลิกเดียว คุณก็สามารถติดตั้งแอปบนเดสก์ท็อปหรืออุปกรณ์เคลื่อนที่ได้ โดยไม่ต้องเพิ่มน้ำหนักจากอิเล็กตรอนเข้าไป เลือกเลิกใช้งานเวอร์ชัน Electron ได้ง่ายๆ มุ่งเน้นเฉพาะเว็บแอป และทำให้เป็น PWA ที่เหมาะสมที่สุด นอกจากนี้ เรายังเผยแพร่ PWA ไปยัง Play Store และ Microsoft Store ได้ด้วย เยี่ยมยอด

บางคนอาจพูดได้ว่า Excalidraw สำหรับ Electron ไม่เลิกใช้งานแล้ว เพราะ Electron ไม่ดี ไม่ใช่เลย แต่เพราะเว็บดีพอแล้ว ฉันชอบคำตอบนี้!

การจัดการไฟล์

เมื่อผมพูดว่า "เว็บดีพอแล้ว" นั่นเป็นเพราะฟีเจอร์ต่างๆ เช่น การจัดการไฟล์ที่กำลังจะเปิดตัว

นี่คือการติดตั้ง macOS Big Sur ปกติ ลองดูว่าเกิดอะไรขึ้นเมื่อผมคลิกขวาที่ไฟล์ Excalidraw ฉันเลือกเปิดด้วย Excalidraw ซึ่งเป็น PWA ที่ติดตั้งไว้ได้ แน่นอนว่าการดับเบิลคลิกก็ใช้ได้ผลเหมือนกัน การแสดงใน Screencast นั้นเป็นเรื่องที่ไม่ค่อยน่าตื่นเต้นนัก

วิธีการทำงาน ขั้นตอนแรกคือกำหนดให้ระบบปฏิบัติการรู้จักประเภทไฟล์ที่แอปพลิเคชันสามารถจัดการได้ ดำเนินการในช่องใหม่ที่ชื่อ file_handlers ในไฟล์ Manifest ของเว็บแอป ค่าคืออาร์เรย์ของออบเจ็กต์ที่มีการดำเนินการและพร็อพเพอร์ตี้ accept การทำงานจะเป็นตัวกำหนดเส้นทาง URL ที่ระบบปฏิบัติการเปิดแอปของคุณ และออบเจ็กต์ยอมรับเป็นคู่คีย์ของประเภท MIME และนามสกุลไฟล์ที่เกี่ยวข้อง

{
  "name": "Excalidraw",
  "description": "Excalidraw is a whiteboard tool...",
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff",
  "file_handlers": [
    {
      "action": "/",
      "accept": {
        "application/vnd.excalidraw+json": [".excalidraw"]
      }
    }
  ]
}

ขั้นตอนถัดไปคือจัดการกับไฟล์เมื่อแอปพลิเคชันเปิดขึ้น ซึ่งจะเกิดขึ้นในอินเทอร์เฟซ launchQueue ซึ่งฉันต้องตั้งค่าผู้บริโภคด้วยการโทร setConsumer() พารามิเตอร์ของฟังก์ชันนี้คือฟังก์ชันแบบไม่พร้อมกันที่รับ launchParams ออบเจ็กต์ launchParams นี้มีช่องที่ชื่อ "ไฟล์" ที่กำหนดให้ฉันแฮนเดิลไฟล์เป็นอาร์เรย์ ฉันดูแลเฉพาะรายการแรกเท่านั้น และจากแฮนเดิลไฟล์นี้ ผมจะรับ Blob ไปส่งต่อไปให้เพื่อนเก่าของเรา loadFromBlob()

if ('launchQueue' in window && 'LaunchParams' in window) {
  window as any.launchQueue
    .setConsumer(async (launchParams: { files: any[] }) => {
      if (!launchParams.files.length) return;
      const fileHandle = launchParams.files[0];
      const blob: Blob = await fileHandle.getFile();
      blob.handle = fileHandle;
      loadFromBlob(blob, this.state).then(({ elements, appState }) =>
        // Initialize app state.
      ).catch((error) => {
        this.setState({ isLoading: false, errorMessage: error.message });
      });
    });
}

ขอย้ำอีกครั้งว่าหากอัปเดตเร็วเกินไป คุณจะอ่านข้อมูลเพิ่มเติมเกี่ยวกับ File Handling API ได้ในบทความของฉัน คุณเปิดใช้การจัดการไฟล์ได้โดยตั้งค่าแฟล็กฟีเจอร์แพลตฟอร์มเว็บทดลอง และมีกำหนดการย้ายใน Chrome ช่วงปลายปีนี้

การผสานรวมคลิปบอร์ด

ฟีเจอร์ที่ยอดเยี่ยมอีกอย่างของ Excalidraw คือการผสานรวมคลิปบอร์ด ผมสามารถคัดลอกภาพวาดทั้งหมด หรือแค่บางส่วนในคลิปบอร์ด หรือจะเพิ่มลายน้ำก็ได้ถ้าต้องการ แล้ววางลงในแอปอื่น นี่เป็นแอป Windows 95 Paint เวอร์ชันเว็บ

ซึ่งวิธีการทำงานก็ทำได้ง่ายมาก สิ่งที่ผมต้องใช้คือ Canvas ในรูปแบบ Blob ซึ่งผมเขียนไปยังคลิปบอร์ดด้วยการส่งผ่านอาร์เรย์ 1 องค์ประกอบที่มี ClipboardItem ที่มี blob ไปยังฟังก์ชัน navigator.clipboard.write() ดูข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่คุณทำได้ด้วย API ของคลิปบอร์ด ได้ที่ Jason และบทความของฉัน

export const copyCanvasToClipboardAsPng = async (canvas: HTMLCanvasElement) => {
  const blob = await canvasToBlob(canvas);
  await navigator.clipboard.write([
    new window.ClipboardItem({
      'image/png': blob,
    }),
  ]);
};

export const canvasToBlob = async (canvas: HTMLCanvasElement): Promise<Blob> => {
  return new Promise((resolve, reject) => {
    try {
      canvas.toBlob((blob) => {
        if (!blob) {
          return reject(new CanvasError(t('canvasError.canvasTooBig'), 'CANVAS_POSSIBLY_TOO_BIG'));
        }
        resolve(blob);
      });
    } catch (error) {
      reject(error);
    }
  });
};

การทำงานร่วมกับผู้อื่น

การแชร์ URL ของเซสชัน

คุณทราบไหมว่า Excalidraw มีโหมดการทำงานร่วมกันด้วย บุคคลต่างๆ สามารถทำงานร่วมกัน ในเอกสารเดียวกันได้ เพื่อเริ่มเซสชันใหม่ ฉันคลิกปุ่มการทำงานร่วมกันแบบเรียลไทม์แล้วเริ่มเซสชัน ฉันสามารถแชร์ URL ของเซสชันกับผู้ทำงานร่วมกันได้ง่ายๆ ด้วย Web Share API ที่ Excalidraw ได้ผสานรวมไว้

การทำงานร่วมกันแบบเรียลไทม์

ผมได้จำลองเซสชันการทำงานร่วมกันเอาไว้ภายในเครื่องโดยจัดทำโลโก้ Google I/O ใน Pixelbook, โทรศัพท์ Pixel 3a และ iPad Pro คุณจะเห็นว่าการเปลี่ยนแปลงที่ฉันทำในอุปกรณ์หนึ่งจะมีผลกับอุปกรณ์อื่นๆ ทั้งหมด

และยังเห็นเคอร์เซอร์ทั้งหมดเคลื่อนที่ไปมาด้วย เคอร์เซอร์ของ Pixelbook เคลื่อนไหวอย่างต่อเนื่องเพราะควบคุมด้วยแทร็กแพด แต่เคอร์เซอร์ของโทรศัพท์ Pixel 3a และเคอร์เซอร์ในแท็บเล็ตของ iPad Pro กลับกระตุกค่ะ เพราะฉันควบคุมอุปกรณ์เหล่านี้โดยแตะด้วยนิ้ว

การดูสถานะของผู้ทำงานร่วมกัน

เพื่อปรับปรุงประสบการณ์การทำงานร่วมกันแบบเรียลไทม์ จึงมีระบบตรวจจับการไม่ใช้งานทำงานอยู่ด้วย เคอร์เซอร์ของ iPad Pro แสดงจุดสีเขียวเมื่อฉันใช้งาน จุดจะเปลี่ยนเป็นสีดำเมื่อฉันเปลี่ยนไปใช้แท็บเบราว์เซอร์หรือแอปอื่น และเมื่อฉันอยู่ในแอป Excalidraw แต่ไม่ได้ทำอะไรอยู่ เคอร์เซอร์จะแสดงว่าไม่มีการใช้งาน โดยมี zZZ 3 สัญลักษณ์กำกับอยู่

นักอ่านสื่อเผยแพร่ของเราอาจมีแนวโน้มที่จะคิดว่าการตรวจจับเมื่อไม่มีการใช้งานเกิดขึ้นได้จาก Idle Detection API ซึ่งเป็นข้อเสนอในระยะเริ่มต้นที่นำไปใช้ในบริบทของ Project Fugu ระวังสปอยล์: ไม่ใช่นะ ในขณะที่เราทำการติดตั้งใช้งานตาม API นี้ใน Excalidraw ในท้ายที่สุด เราตัดสินใจใช้แนวทางเดิมๆ คือการวัดการเคลื่อนไหวของตัวชี้และระดับการเข้าถึงหน้าเว็บ

ภาพหน้าจอของความคิดเห็นเกี่ยวกับการตรวจจับเมื่อไม่มีการใช้งานในที่เก็บการตรวจจับเมื่อไม่มีการใช้งานของ WICG

เราได้ยื่นความคิดเห็นถึงสาเหตุที่ Idle Detection API ไม่ช่วยแก้ปัญหา Use Case ที่เรามี ส่วน Project Fugu API ทั้งหมดก็กำลังพัฒนาแบบเปิด ทุกคน จะได้ร่วมแสดงออกและรับฟังความคิดเห็นของตน

Lipis กับสิ่งที่กำลังทำให้ Excalidraw ไม่อยู่

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

File System Access API นั้นยอดเยี่ยม แต่คุณรู้อะไรไหม ไฟล์ส่วนใหญ่ที่ผมสนใจสมัยนี้ จะอยู่ใน Dropbox หรือ Google ไดรฟ์ ไม่ได้อยู่ในฮาร์ดดิสก์ ฉันอยากให้ File System Access API รวมชั้น Abstraction ที่ผู้ให้บริการระบบไฟล์ระยะไกลอย่าง Dropbox หรือ Google จะผสานรวมด้วยเพื่อให้นักพัฒนาซอฟต์แวร์เขียนโค้ดได้ ผู้ใช้จึงสบายใจและทราบว่าไฟล์ของตนปลอดภัย ด้วยผู้ให้บริการระบบคลาวด์ที่ตนเชื่อถือ

เห็นด้วยที่สุดกับ Lipis ฉันก็ทำงานในระบบคลาวด์เหมือนกัน เราหวังว่าจะเริ่มใช้งานได้ ในเร็วๆ นี้

โหมดแอปพลิเคชันแบบแท็บ

ว้าว เราได้เห็นการผสานรวม API ที่ยอดเยี่ยมมากมายใน Excalidraw ระบบไฟล์, การจัดการไฟล์, คลิปบอร์ด, การแชร์เว็บ และเป้าหมายการแชร์เว็บ แต่ยังมีอีกสิ่งหนึ่ง ก่อนหน้านี้ผมแก้ไขเอกสารได้เพียงครั้งละ 1 ฉบับเท่านั้น ไม่ได้แล้ว โปรดเพลิดเพลินกับโหมดแอปพลิเคชันแบบแท็บ ใน Excalidraw เวอร์ชันก่อนเปิดตัวเป็นครั้งแรก นี่คือหน้าตาของโฆษณา

ฉันมีไฟล์ที่มีอยู่เปิดอยู่ใน Excalidraw PWA ที่ติดตั้งไว้ซึ่งทำงานในโหมดสแตนด์อโลน ตอนนี้ฉันจะเปิดแท็บใหม่ในหน้าต่างแบบสแตนด์อโลน นี่ไม่ใช่แท็บเบราว์เซอร์ทั่วไป แต่เป็นแท็บ PWA ในแท็บใหม่นี้ ผมสามารถเปิดไฟล์รอง และทำงานในไฟล์ได้อย่างอิสระจากหน้าต่างแอปเดียวกัน

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

เปิดจากขอบ

หากต้องการติดตามข้อมูลเกี่ยวกับฟีเจอร์นี้และฟีเจอร์อื่นๆ อย่าลืมดู เครื่องมือติดตาม API ของ Fuugu เรายินดีเป็นอย่างยิ่งที่ได้ผลักดันเว็บให้ก้าวหน้ายิ่งขึ้น และให้คุณทำสิ่งต่างๆ บนแพลตฟอร์มได้มากขึ้น ขอเสนอแอป Excalidraw ที่พัฒนาขึ้นเรื่อยๆ และนี่คือแอป สุดเจ๋งทั้งหมดที่คุณจะสร้างขึ้น ไปเริ่มสร้างที่ excalidraw.com กันเลย

ฉันตื่นเต้นมากที่จะได้เห็น API บางรายการที่เราแสดงในวันนี้ป๊อปอัปขึ้นในแอปของคุณ ฉันชื่อต้อม คุณเรียกฉันว่า @tomayac ใน Twitter และบนอินเทอร์เน็ตโดยทั่วไป ขอขอบคุณที่รับชม และขอให้สนุกกับการรับชม Google I/O