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

เมื่อวันที่ 15 มกราคม Christopher ได้เผยแพร่บล็อกโพสต์ที่ได้รับความสนใจอย่างมากใน Twitter รวมถึงจากผมด้วย โพสต์เริ่มต้นด้วยสถิติที่น่าประทับใจดังนี้
- ผู้ใช้ที่ใช้งานอยู่ไม่ซ้ำกัน 1.2 หมื่นราย
- ดาว 1.5 พันดวงบน GitHub
- ผู้ร่วมให้ข้อมูล 26 ราย
สำหรับโปรเจ็กต์ที่เพิ่งเริ่มเมื่อ 2 สัปดาห์ก่อน ถือว่าไม่เลวเลย แต่สิ่งที่ทำให้ฉันสนใจจริงๆ คือข้อมูลที่อยู่ด้านล่างของโพสต์ Christopher เขียนว่าเขาได้ลองทำสิ่งใหม่ในครั้งนี้ นั่นคือการให้สิทธิ์เข้าถึงการคอมมิตแบบไม่มีเงื่อนไขแก่ทุกคนที่ส่ง Pull Request ในวันเดียวกันกับที่อ่านบล็อกโพสต์นั้น ผมได้สร้าง Pull Request เพื่อเพิ่มการรองรับ File System Access API ลงใน Excalidraw ซึ่งช่วยแก้ไขคำขอฟีเจอร์ที่มีคนส่งเข้ามา

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

Lipis พูดถึงเหตุผลที่เขาทุ่มเทเวลาอย่างมากให้กับ Excalidraw
เรื่องราว "ฉันมาใช้ Excalidraw ได้อย่างไร" ก็จบลงเพียงเท่านี้ แต่ก่อนที่จะเจาะลึกฟีเจอร์ที่น่าทึ่งบางอย่างของ Excalidraw ฉันยินดีที่จะแนะนำให้รู้จักกับ Panayiotis Panayiotis Lipiridis หรือที่รู้จักกันในอินเทอร์เน็ตว่า lipis เป็นผู้ร่วมให้ข้อมูลที่ทำงานอย่างต่อเนื่องที่สุดใน Excalidraw เราได้ถาม lipis ว่าอะไรเป็นแรงจูงใจให้เขาทุ่มเทเวลาให้กับ Excalidraw มากขนาดนี้
ผมทราบเกี่ยวกับโปรเจ็กต์นี้จากทวีตของ Christopher เหมือนกับคนอื่นๆ การมีส่วนร่วมครั้งแรกของฉันคือการเพิ่มไลบรารี Open Color ซึ่งเป็นสีที่ยังคงเป็นส่วนหนึ่งของ Excalidraw ในปัจจุบัน เมื่อโปรเจ็กต์เติบโตขึ้นและเราได้รับคำขอจำนวนมาก การมีส่วนร่วมครั้งใหญ่ครั้งถัดไปของฉันคือการสร้างแบ็กเอนด์สำหรับจัดเก็บภาพวาดเพื่อให้ผู้ใช้แชร์ภาพวาดได้ แต่สิ่งที่กระตุ้นให้ฉันมีส่วนร่วมจริงๆ คือใครก็ตามที่ลองใช้ Excalidraw จะพยายามหาข้ออ้างเพื่อใช้มันอีกครั้ง
ฉันเห็นด้วยกับ lipis อย่างยิ่ง ใครก็ตามที่เคยลองใช้ Excalidraw จะพยายามหาข้ออ้างเพื่อใช้เครื่องมือนี้อีกครั้ง
การทำงานของ Excalidraw
ตอนนี้ฉันอยากแสดงให้คุณเห็นวิธีใช้ Excalidraw ในทางปฏิบัติ ฉันไม่ใช่ศิลปินที่เก่งกาจอะไร แต่โลโก้ Google I/O นั้นเรียบง่ายพอ ฉันจึงอยากลองวาดดู กล่องคือ "i" เส้นคือเครื่องหมายทับ และ "o" คือวงกลม ฉันกดปุ่ม shift ค้างไว้เพื่อให้ได้วงกลมที่สมบูรณ์แบบ ขอขยับเครื่องหมายทับเล็กน้อย เพื่อให้ดูดีขึ้น ตอนนี้ก็ถึงเวลาเติมสีสันให้ตัว "i" และ "o" สีน้ำเงินบ่งชี้ว่าดี ลองใช้สไตล์การเติมสีแบบอื่นไหม ทึบทั้งหมดหรือลายตาราง ไม่ล่ะ ลายเส้นดูดีแล้ว อาจจะไม่สมบูรณ์แบบ แต่ก็เป็นแนวคิดของ Excalidraw ดังนั้นผมจะบันทึกไว้
ฉันคลิกไอคอนบันทึกและป้อนชื่อไฟล์ในกล่องโต้ตอบบันทึกไฟล์ ใน Chrome ซึ่งเป็นเบราว์เซอร์ที่รองรับ File System Access API การดำเนินการนี้ไม่ใช่การดาวน์โหลด แต่เป็นการบันทึกจริง ซึ่งฉันสามารถเลือกตำแหน่งและชื่อไฟล์ได้ และหากทำการแก้ไข ฉันก็สามารถบันทึกการแก้ไขลงในไฟล์เดียวกันได้
ฉันจะเปลี่ยนโลโก้และทำให้ตัว "i" เป็นสีแดง หากตอนนี้ฉันคลิกบันทึกอีกครั้ง ระบบจะบันทึกการแก้ไขลงในไฟล์เดียวกับก่อนหน้านี้ เพื่อเป็นการพิสูจน์ ฉันจะล้าง Canvas และเปิดไฟล์อีกครั้ง คุณจะเห็นว่าโลโก้สีแดง-น้ำเงินที่แก้ไขแล้วปรากฏขึ้นอีกครั้ง
การทำงานกับไฟล์
ในเบราว์เซอร์ที่ปัจจุบันไม่รองรับ 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 ไฟล์ ระบบจะแก้ไข Promise
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-3 บรรทัดแรกอาจดูซับซ้อนเล็กน้อย แต่จริงๆ แล้วเป็นเพียงการเจรจาประเภท 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 โดยอัตโนมัติ คุณต้องเพิกถอน URL ของ BLOB หลังจากใช้งานเพื่อป้องกันไม่ให้หน่วยความจำรั่วไหล เนื่องจากนี่เป็นเพียงการดาวน์โหลด จึงไม่มีกล่องโต้ตอบการบันทึกไฟล์แสดงขึ้น และไฟล์ทั้งหมดจะอยู่ในโฟลเดอร์ Downloads เริ่มต้น
ลากและวาง
การผสานรวมระบบที่ฉันชอบมากที่สุดอย่างหนึ่งบนเดสก์ท็อปคือการลากและวาง ใน Excalidraw เมื่อฉันวางไฟล์ .excalidraw ลงในแอปพลิเคชัน ไฟล์จะเปิดขึ้นทันทีและฉันก็เริ่มแก้ไขได้ ในเบราว์เซอร์ที่รองรับ File System Access API ฉันสามารถบันทึกการเปลี่ยนแปลงได้ทันที ไม่จำเป็นต้องผ่านกล่องโต้ตอบการบันทึกไฟล์เนื่องจากแฮนเดิลไฟล์ที่จำเป็นได้มาจากการดำเนินการลากและวาง
เคล็ดลับในการทำให้สิ่งนี้เกิดขึ้นคือการเรียกใช้ getAsFileSystemHandle() ในรายการการโอนข้อมูลเมื่อระบบรองรับ File System Access API จากนั้นฉันจะส่งแฮนเดิลไฟล์นี้ไปยัง loadFromBlob() ซึ่งคุณอาจจำได้จากย่อหน้าด้านบน คุณทำสิ่งต่างๆ กับไฟล์ได้มากมาย ไม่ว่าจะเป็นการเปิด บันทึก บันทึกทับ ลาก และวาง 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 ที่เลิกใช้งานแล้ว
สิ่งหนึ่งที่คุณทำกับไฟล์ได้ซึ่งฉันยังไม่ได้พูดถึงคือการดับเบิลคลิก โดยปกติแล้ว เมื่อคุณดับเบิลคลิกไฟล์ แอปที่เชื่อมโยงกับประเภท MIME ของไฟล์จะเปิดขึ้น เช่น สำหรับ .docx จะเป็น Microsoft Word
Excalidraw เคยมีแอปเวอร์ชัน Electron ที่รองรับการเชื่อมโยงประเภทไฟล์ดังกล่าว ดังนั้นเมื่อคุณดับเบิลคลิกไฟล์ .excalidraw แอป Excalidraw Electron จะเปิดขึ้น Lipis ซึ่งคุณเคยพบมาก่อนหน้านี้ เป็นทั้งผู้สร้างและผู้เลิกใช้งาน Excalidraw Electron ฉันถามเขาว่าทำไมเขาจึงคิดว่าสามารถเลิกใช้งานเวอร์ชัน Electron ได้
ผู้ใช้ขอแอป Electron มาตั้งแต่แรก โดยส่วนใหญ่เป็นเพราะต้องการเปิดไฟล์ด้วยการดับเบิลคลิก นอกจากนี้ เรายังตั้งใจที่จะนำแอปนี้ไปไว้ใน App Store ด้วย ในขณะเดียวกัน มีคนแนะนำให้สร้าง PWA แทน เราจึงทำทั้งสองอย่าง โชคดีที่เราได้รู้จักกับ API ของ Project Fugu เช่น การเข้าถึงระบบไฟล์ การเข้าถึงคลิปบอร์ด การจัดการไฟล์ และอื่นๆ คุณสามารถติดตั้งแอปบนเดสก์ท็อปหรืออุปกรณ์เคลื่อนที่ได้ด้วยการคลิกเพียงครั้งเดียว โดยไม่ต้องใช้ Electron เพิ่มเติม เราจึงตัดสินใจเลิกใช้งานเวอร์ชัน Electron ได้อย่างง่ายดาย และมุ่งเน้นไปที่เว็บแอปเพียงอย่างเดียวเพื่อทำให้เป็น PWA ที่ดีที่สุดเท่าที่จะเป็นไปได้ นอกจากนี้ เรายังเผยแพร่ PWA ไปยัง Play Store และ Microsoft Store ได้แล้ว ถือเป็นเรื่องใหญ่มาก
อาจกล่าวได้ว่า Excalidraw สำหรับ Electron ไม่ได้เลิกใช้งานเพราะ Electron ไม่ดี แต่เป็นเพราะเว็บดีพอแล้ว ฉันชอบอันนี้
File Handling
ที่ผมบอกว่า "เว็บดีขึ้นมาก" ก็เพราะฟีเจอร์อย่าง File Handling ที่กำลังจะเปิดตัว
นี่คือการติดตั้ง macOS Big Sur ตามปกติ คราวนี้มาดูสิ่งที่จะเกิดขึ้นเมื่อฉันคลิกขวาที่ไฟล์ Excalidraw ฉันเลือกเปิดไฟล์ด้วย Excalidraw ซึ่งเป็น PWA ที่ติดตั้งไว้ได้ แน่นอนว่าการดับเบิลคลิกก็ใช้ได้เช่นกัน แต่จะดูน่าตื่นเต้นน้อยกว่าเมื่อสาธิตใน Screencast
แล้วฟีเจอร์นี้ทำงานอย่างไร ขั้นตอนแรกคือการทำให้ระบบปฏิบัติการรู้จักประเภทไฟล์ที่แอปพลิเคชันของฉันจัดการได้ ฉันทำเช่นนี้ในช่องใหม่ที่ชื่อว่า file_handlers ในไฟล์ Manifest ของเว็บแอป ค่าของพร็อพเพอร์ตี้นี้คืออาร์เรย์ของออบเจ็กต์ที่มีการดำเนินการและพร็อพเพอร์ตี้ accept การดำเนินการจะกำหนดเส้นทาง URL ที่ระบบปฏิบัติการเปิดแอปของคุณ และออบเจ็กต์ accept คือคู่คีย์-ค่าของประเภท 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 นี้มีฟิลด์ที่ชื่อว่า files ซึ่งจะให้ชุด File Handle เพื่อใช้ทำงาน ฉันสนใจเฉพาะไฟล์แรกเท่านั้น และจากแฮนเดิลไฟล์นี้ ฉันจะได้รับ 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 ได้ในบทความของฉัน คุณเปิดใช้ File Handling ได้โดยตั้งค่าแฟล็กฟีเจอร์แพลตฟอร์มเว็บเวอร์ชันทดลอง โดยมีกำหนดจะเปิดตัวใน Chrome ภายในปีนี้
การผสานรวมคลิปบอร์ด
ฟีเจอร์เจ๋งๆ อีกอย่างของ Excalidraw คือการผสานรวมคลิปบอร์ด ฉันสามารถคัดลอกภาพวาดทั้งหมดหรือแค่บางส่วนลงในคลิปบอร์ด อาจเพิ่มลายน้ำหากต้องการ แล้ววางลงในแอปอื่น นี่คือแอป Paint ของ Windows 95 เวอร์ชันเว็บ
วิธีทำงานของฟีเจอร์นี้เรียบง่ายอย่างไม่น่าเชื่อ สิ่งที่ฉันต้องการมีเพียง Canvas เป็น Blob ซึ่งฉันจะเขียนลงในคลิปบอร์ดโดยส่งอาร์เรย์องค์ประกอบเดียวที่มี ClipboardItem พร้อม Blob ไปยังฟังก์ชัน navigator.clipboard.write() ดูข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่คุณทำได้ด้วย Clipboard 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 แต่สุดท้ายเราก็ตัดสินใจเลือกแนวทางแบบดั้งเดิมที่อิงตามการวัดการเคลื่อนไหวของเคอร์เซอร์และการมองเห็นหน้าเว็บ

เราได้ส่งความคิดเห็นเกี่ยวกับสาเหตุที่ Idle Detection API ไม่สามารถแก้ปัญหากรณีการใช้งานที่เรามี API ทั้งหมดของ Project Fugu ได้รับการพัฒนาแบบเปิด ดังนั้นทุกคนจึงสามารถมีส่วนร่วมและแสดงความคิดเห็นได้
Lipis พูดถึงสิ่งที่ฉุดรั้ง Excalidraw ไว้
เมื่อพูดถึงเรื่องนี้ ฉันได้ถาม lipis เป็นคำถามสุดท้ายเกี่ยวกับสิ่งที่เขาคิดว่าขาดหายไปจากแพลตฟอร์มเว็บที่ฉุดรั้ง Excalidraw ไว้
File System Access API นั้นยอดเยี่ยมมาก แต่คุณรู้อะไรไหม ไฟล์ส่วนใหญ่ที่ฉันสนใจในปัจจุบัน อยู่ใน Dropbox หรือ Google ไดรฟ์ ไม่ใช่ในฮาร์ดดิสก์ ฉันหวังว่า File System Access API จะมีเลเยอร์นามธรรมสำหรับผู้ให้บริการระบบไฟล์ระยะไกล เช่น Dropbox หรือ Google เพื่อผสานรวมและนักพัฒนาซอฟต์แวร์สามารถเขียนโค้ดได้ ผู้ใช้จึงสบายใจได้ว่าไฟล์จะปลอดภัยเมื่ออยู่กับผู้ให้บริการคลาวด์ที่ตนไว้วางใจ
ฉันเห็นด้วยกับ lipis อย่างยิ่ง ฉันก็อยู่ในระบบคลาวด์เช่นกัน หวังว่าฟีเจอร์นี้จะเปิดให้ใช้งานในเร็วๆ นี้
โหมดแอปพลิเคชันแบบแท็บ
ว้าว เราได้เห็นการผสานรวม API ที่ยอดเยี่ยมมากมายใน Excalidraw ระบบไฟล์, File Handling, คลิปบอร์ด, Web Share และ Web Share Target แต่ยังมีอีกสิ่งหนึ่ง ที่ผ่านมา ผมแก้ไขเอกสารได้ ทีละฉบับเท่านั้น ปัญหานี้จะหมดไป โปรดเพลิดเพลินกับโหมดแอปพลิเคชันแบบแท็บเวอร์ชันแรกใน Excalidraw เป็นครั้งแรก ซึ่งจะมีลักษณะดังนี้
ฉันเปิดไฟล์ที่มีอยู่ใน Excalidraw PWA ที่ติดตั้งไว้ซึ่งทำงานในโหมดสแตนด์อโลน ตอนนี้ฉันจะเปิดแท็บใหม่ในหน้าต่างสแตนด์อโลน นี่ไม่ใช่แท็บเบราว์เซอร์ปกติ แต่เป็นแท็บ PWA ในแท็บใหม่นี้ ฉันสามารถเปิดไฟล์รองและทำงานกับไฟล์นั้นแยกต่างหากจากหน้าต่างแอปเดียวกันได้
โหมดแอปพลิเคชันแบบแท็บยังอยู่ในช่วงเริ่มต้นและทุกอย่างยังไม่แน่นอน หากสนใจ โปรดอ่านสถานะปัจจุบันของฟีเจอร์นี้ในบทความของฉัน
เปิดจากขอบ
หากต้องการติดตามข้อมูลเกี่ยวกับฟีเจอร์นี้และฟีเจอร์อื่นๆ โปรดดูเครื่องมือติดตาม Fugu API เราตื่นเต้นมากที่จะได้พัฒนาเว็บให้ก้าวหน้ายิ่งขึ้นและช่วยให้คุณทำสิ่งต่างๆ บนแพลตฟอร์มได้มากขึ้น ขอให้ Excalidraw มีการพัฒนาอย่างต่อเนื่อง และขอให้คุณสร้างแอปพลิเคชันที่ยอดเยี่ยมได้มากมาย เริ่มสร้างสรรค์ได้ที่ excalidraw.com
เราหวังว่าจะได้เห็น API บางรายการที่แสดงในวันนี้ปรากฏในแอปของคุณ ฉันชื่อ Tom คุณสามารถค้นหาฉันได้ที่ @tomayac บน Twitter และอินเทอร์เน็ตโดยทั่วไป ขอบคุณมากที่รับชม และขอให้สนุกกับช่วงที่เหลือของ Google I/O