Exalidraw và Fugu: Cải thiện hành trình cốt lõi của người dùng

Bất kỳ công nghệ nào đủ tiên tiến đều không thể phân biệt được với phép thuật. Trừ phi bạn hiểu rõ. Tôi là Thomas Steiner, làm việc tại Nhóm Quan hệ với nhà phát triển của Google. Trong bài viết này về bài nói của tôi tại Google I/O, tôi sẽ xem xét một số API Fugu mới và cách chúng cải thiện hành trình cốt lõi của người dùng trong PWA Excalidraw. Nhờ đó, bạn có thể lấy cảm hứng từ những ý tưởng này và áp dụng chúng cho các ứng dụng của riêng mình.

Cách tôi đến với Excalidraw

Tôi muốn bắt đầu bằng một câu chuyện. Vào ngày 1 tháng 1 năm 2020, Christopher Chedeau, một kỹ sư phần mềm tại Facebook, đã đăng một dòng tweet về một ứng dụng vẽ nhỏ mà anh bắt đầu phát triển. Với công cụ này, bạn có thể vẽ các hộp và mũi tên trông như hoạt hình và được vẽ bằng tay. Vào ngày hôm sau, bạn cũng có thể vẽ hình elip và văn bản, cũng như chọn các đối tượng và di chuyển chúng xung quanh. Vào ngày 3 tháng 1, ứng dụng này đã có tên là Excalidraw và giống như mọi dự án phụ tốt, việc mua tên miền là một trong những việc đầu tiên mà Christopher làm. Đến đây, bạn có thể sử dụng màu sắc và xuất toàn bộ bản vẽ dưới dạng PNG.

Ảnh chụp màn hình của ứng dụng nguyên mẫu Excalidraw cho thấy ứng dụng này hỗ trợ hình chữ nhật, mũi tên, hình elip và văn bản.

Vào ngày 15 tháng 1, Christopher đã đăng một bài đăng trên blog thu hút rất nhiều sự chú ý trên Twitter, kể cả tôi. Bài đăng bắt đầu bằng một số số liệu thống kê ấn tượng:

  • 12.000 người dùng hoạt động riêng biệt
  • 1.500 lượt gắn dấu sao trên GitHub
  • 26 người đóng góp

Đối với một dự án mới bắt đầu cách đây 2 tuần, đó là một kết quả không hề tệ. Nhưng điều thực sự khiến tôi quan tâm là phần bên dưới của bài đăng. Christopher viết rằng lần này anh đã thử một cách tiếp cận mới: cấp quyền cam kết vô điều kiện cho tất cả những người gửi yêu cầu kéo. Cùng ngày đọc bài đăng trên blog đó, tôi đã có một yêu cầu kéo để thêm hỗ trợ File System Access API vào Excalidraw, khắc phục một yêu cầu về tính năng mà ai đó đã gửi.

Ảnh chụp màn hình của bài đăng trên X nơi tôi thông báo về thông cáo báo chí của mình.

Yêu cầu kéo của tôi đã được hợp nhất một ngày sau đó và kể từ đó, tôi có toàn quyền thực hiện cam kết. Không cần phải nói, tôi không lạm dụng quyền lực của mình. Và cũng không có ai khác trong số 149 người đóng góp cho đến nay.

Ngày nay, Excalidraw là một ứng dụng web tiến bộ có thể cài đặt đầy đủ, hỗ trợ chế độ ngoại tuyến, có chế độ tối tuyệt đẹp và có khả năng mở cũng như lưu tệp nhờ File System Access API.

Ảnh chụp màn hình PWA Excalidraw ở trạng thái hiện tại.

Lipis chia sẻ lý do anh dành nhiều thời gian cho Excalidraw

Vậy là câu chuyện "làm sao tôi biết đến Excalidraw" của tôi đến đây là hết. Nhưng trước khi đi sâu vào một số tính năng tuyệt vời của Excalidraw, tôi xin giới thiệu Panayiotis. Panayiotis Lipiridis, hay còn được biết đến trên Internet với tên lipis, là người đóng góp tích cực nhất cho Excalidraw. Tôi đã hỏi lipis điều gì thúc đẩy anh ấy dành nhiều thời gian cho Excalidraw:

Giống như mọi người, tôi biết đến dự án này qua dòng tweet của Christopher. Đóng góp đầu tiên của tôi là thêm thư viện Open Color, những màu sắc vẫn là một phần của Excalidraw cho đến ngày nay. Khi dự án phát triển và chúng tôi nhận được khá nhiều yêu cầu, đóng góp lớn tiếp theo của tôi là xây dựng một phần phụ trợ để lưu trữ bản vẽ để người dùng có thể chia sẻ chúng. Nhưng điều thực sự thúc đẩy tôi đóng góp là bất cứ ai đã thử Excalidraw đều muốn tìm lý do để sử dụng lại công cụ này.

Tôi hoàn toàn đồng ý với lipis. Bất cứ ai đã thử Excalidraw đều muốn tìm lý do để sử dụng lại công cụ này.

Excalidraw trong thực tế

Bây giờ, tôi muốn chỉ cho bạn cách sử dụng Excalidraw trong thực tế. Tôi không phải là một nghệ sĩ tài ba, nhưng biểu trưng của Google I/O khá đơn giản, nên tôi sẽ thử xem sao. Một hộp là chữ "i", một đường thẳng có thể là dấu gạch chéo và chữ "o" là một vòng tròn. Tôi nhấn giữ phím shift để vẽ một hình tròn hoàn hảo. Tôi sẽ di chuyển dấu gạch chéo một chút để trông đẹp hơn. Giờ hãy thêm một chút màu cho chữ "i" và chữ "o". Màu xanh dương là tốt. Có thể là một kiểu tô khác? Tất cả đều là đường liền nét hay đường gạch chéo? Không, đường gạch trông rất đẹp. Công cụ này chưa hoàn hảo, nhưng đó là ý tưởng của Excalidraw, vì vậy, hãy để tôi lưu lại.

Tôi nhấp vào biểu tượng lưu và nhập tên tệp trong hộp thoại lưu tệp. Trong Chrome (một trình duyệt hỗ trợ File System Access API), đây không phải là một thao tác tải xuống mà là một thao tác lưu thực sự, trong đó tôi có thể chọn vị trí và tên của tệp, đồng thời nếu chỉnh sửa, tôi chỉ cần lưu các nội dung chỉnh sửa vào cùng một tệp.

Tôi muốn thay đổi biểu trưng và chuyển chữ "i" thành màu đỏ. Nếu tôi nhấp vào lưu lại, nội dung sửa đổi của tôi sẽ được lưu vào cùng một tệp như trước. Để chứng minh, tôi sẽ xoá canvas và mở lại tệp. Như bạn có thể thấy, biểu trưng màu đỏ và xanh dương đã được sửa đổi lại xuất hiện.

Làm việc với tệp

Trên những trình duyệt hiện không hỗ trợ File System Access API, mỗi thao tác lưu là một thao tác tải xuống, vì vậy khi tôi thực hiện các thay đổi, tôi sẽ có nhiều tệp có số tăng dần trong tên tệp và lấp đầy thư mục Tải xuống. Mặc dù có nhược điểm này, tôi vẫn có thể lưu tệp.

Mở tệp

Vậy bí quyết là gì? Làm cách nào để mở và lưu tệp trên các trình duyệt có thể hỗ trợ hoặc không hỗ trợ File System Access API? Việc mở một tệp trong Excalidraw diễn ra trong một hàm có tên là loadFromJSON)(, hàm này sẽ gọi một hàm có tên là 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);
};

Hàm fileOpen() có trong một thư viện nhỏ mà tôi đã viết có tên là browser-fs-access mà chúng tôi sử dụng trong Excalidraw. Thư viện này cung cấp quyền truy cập vào hệ thống tệp thông qua File System Access API (API Truy cập hệ thống tệp) với một giải pháp dự phòng cũ, vì vậy, bạn có thể sử dụng thư viện này trong mọi trình duyệt.

Trước tiên, hãy để tôi cho bạn xem cách triển khai khi API được hỗ trợ. Sau khi thương lượng các loại MIME và tiện ích tệp được chấp nhận, phần trung tâm là gọi hàm showOpenFilePicker() của API Truy cập hệ thống tệp. Hàm này trả về một mảng gồm các tệp hoặc một tệp duy nhất, tuỳ thuộc vào việc bạn chọn nhiều tệp hay không. Sau đó, bạn chỉ cần đặt mã nhận dạng tệp vào đối tượng tệp để có thể truy xuất lại.

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;
  };
};

Hoạt động triển khai dự phòng dựa vào một phần tử input thuộc loại "file". Sau khi thương lượng các loại MIME và tiện ích sẽ được chấp nhận, bước tiếp theo là lập trình để nhấp vào phần tử đầu vào để hộp thoại mở tệp xuất hiện. Khi người dùng đã chọn một hoặc nhiều tệp, lời hứa sẽ được thực hiện.

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

Lưu tệp

Bây giờ, hãy lưu. Trong Excalidraw, việc lưu diễn ra trong một hàm có tên là saveAsJSON(). Trước tiên, nó sẽ chuyển đổi mảng các phần tử Excalidraw thành JSON, chuyển đổi JSON thành một blob, rồi gọi một hàm có tên là fileSave(). Hàm này cũng do thư viện browser-fs-access cung cấp.

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 };
};

Trước tiên, hãy để tôi xem xét việc triển khai cho các trình duyệt có hỗ trợ API Truy cập hệ thống tệp. Hai dòng đầu tiên có vẻ hơi phức tạp, nhưng tất cả những gì chúng làm là thương lượng các loại MIME và tiện ích tệp. Khi tôi đã lưu trước đó và đã có một mã nhận dạng tệp, không cần hiển thị hộp thoại lưu. Nhưng nếu đây là lần lưu đầu tiên, một hộp thoại tệp sẽ xuất hiện và ứng dụng sẽ nhận được một tay cầm tệp để sử dụng trong tương lai. Sau đó, phần còn lại chỉ là ghi vào tệp, việc này diễn ra thông qua một luồng có thể ghi.

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;
};

Tính năng "lưu dưới dạng"

Nếu quyết định bỏ qua một mã nhận dạng tệp đã tồn tại, tôi có thể triển khai tính năng "lưu dưới dạng" để tạo một tệp mới dựa trên một tệp hiện có. Để minh hoạ điều này, tôi sẽ mở một tệp hiện có, chỉnh sửa một chút rồi không ghi đè tệp hiện có mà tạo một tệp mới bằng cách sử dụng tính năng lưu dưới dạng. Thao tác này sẽ giữ nguyên tệp gốc.

Việc triển khai cho các trình duyệt không hỗ trợ File System Access API (API Truy cập hệ thống tệp) rất ngắn, vì tất cả những gì cần làm là tạo một phần tử liên kết có thuộc tính download mà giá trị là tên tệp mong muốn và một URL blob làm giá trị thuộc tính 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();
};

Sau đó, phần tử neo sẽ được nhấp vào theo cách lập trình. Để tránh làm rò rỉ bộ nhớ, bạn cần thu hồi URL blob sau khi sử dụng. Vì đây chỉ là một lượt tải xuống, nên hộp thoại lưu tệp sẽ không bao giờ xuất hiện và tất cả các tệp đều nằm trong thư mục Downloads mặc định.

Kéo và thả

Một trong những tính năng tích hợp hệ thống mà tôi yêu thích trên máy tính là kéo và thả. Trong Excalidraw, khi tôi thả tệp .excalidraw vào ứng dụng, tệp đó sẽ mở ngay lập tức và tôi có thể bắt đầu chỉnh sửa. Trên những trình duyệt hỗ trợ File System Access API, tôi thậm chí có thể lưu ngay các thay đổi. Không cần phải chuyển qua hộp thoại lưu tệp vì bạn đã lấy được mã nhận dạng tệp cần thiết từ thao tác kéo và thả.

Bí quyết để thực hiện việc này là gọi getAsFileSystemHandle() trên mục truyền dữ liệu khi File System Access API (API Truy cập hệ thống tệp) được hỗ trợ. Sau đó, tôi chuyển mã nhận dạng tệp này đến loadFromBlob(). Bạn có thể nhớ mã nhận dạng này ở một vài đoạn phía trên. Có rất nhiều việc bạn có thể làm với tệp: mở, lưu, lưu đè, kéo, thả. Tôi và đồng nghiệp Pete đã ghi lại tất cả những mẹo này và nhiều mẹo khác trong bài viết của chúng tôi để bạn có thể nắm bắt được nếu tất cả những điều này diễn ra hơi nhanh.

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

Chia sẻ tệp

Một phương thức tích hợp hệ thống khác hiện có trên Android, ChromeOS và Windows là thông qua Web Share Target API. Đây là thư mục Downloads của tôi trong ứng dụng Files. Tôi thấy 2 tệp, một trong số đó có tên không rõ ràng là untitled và có dấu thời gian. Để kiểm tra nội dung của tệp, tôi nhấp vào biểu tượng ba dấu chấm, sau đó nhấp vào biểu tượng chia sẻ. Một trong các lựa chọn xuất hiện là Excalidraw. Khi nhấn vào biểu tượng này, tôi chỉ thấy tệp chứa biểu trưng I/O.

Lipis trên phiên bản Electron không được dùng nữa

Một việc bạn có thể làm với các tệp mà tôi chưa đề cập đến là nhấp đúp vào chúng. Điều thường xảy ra khi bạn nhấp đúp vào một tệp là ứng dụng được liên kết với loại MIME của tệp sẽ mở ra. Ví dụ: đối với .docx, đây sẽ là Microsoft Word.

Excalidraw trước đây có một phiên bản Electron của ứng dụng hỗ trợ các mối liên kết loại tệp như vậy. Vì vậy, khi bạn nhấp đúp vào một tệp .excalidraw, ứng dụng Excalidraw Electron sẽ mở ra. Lipis (người mà bạn đã gặp trước đây) vừa là nhà sáng tạo vừa là người ngừng cung cấp Excalidraw Electron. Tôi hỏi anh ấy tại sao anh ấy cảm thấy có thể ngừng cung cấp phiên bản Electron:

Mọi người đã yêu cầu một ứng dụng Electron ngay từ đầu, chủ yếu là vì họ muốn mở tệp bằng cách nhấp đúp. Chúng tôi cũng dự định đưa ứng dụng này vào các cửa hàng ứng dụng. Đồng thời, có người đề xuất tạo một PWA thay thế, vì vậy chúng tôi đã làm cả hai. May mắn thay, chúng tôi đã được giới thiệu về các API của Dự án Fugu như quyền truy cập vào hệ thống tệp, quyền truy cập vào bảng nhớ tạm, tính năng xử lý tệp và nhiều API khác. Chỉ cần nhấp một lần, bạn có thể cài đặt ứng dụng này trên máy tính hoặc thiết bị di động mà không cần thêm dung lượng cho Electron. Chúng tôi đã dễ dàng quyết định ngừng cung cấp phiên bản Electron, chỉ tập trung vào ứng dụng web và biến ứng dụng này thành PWA tốt nhất có thể. Ngoài ra, giờ đây, chúng tôi có thể xuất bản PWA lên Cửa hàng Play và Microsoft Store! Thật là nhiều!

Có thể nói Excalidraw cho Electron không bị ngừng sử dụng vì Electron không tốt, hoàn toàn không phải vậy, mà vì web đã trở nên đủ tốt. Tôi thích nội dung này!

Xử lý tệp

Khi tôi nói "web đã đủ tốt", đó là vì những tính năng như Xử lý tệp sắp ra mắt.

Đây là quy trình cài đặt macOS Big Sur thông thường. Bây giờ, hãy xem điều gì xảy ra khi tôi nhấp chuột phải vào một tệp Excalidraw. Tôi có thể chọn mở tệp này bằng Excalidraw, PWA đã cài đặt. Tất nhiên, bạn cũng có thể nhấp đúp. Chỉ là cách này ít hiệu quả hơn khi minh hoạ trong bản ghi màn hình.

Vậy tính năng này hoạt động như thế nào? Bước đầu tiên là cho hệ điều hành biết những loại tệp mà ứng dụng của tôi có thể xử lý. Tôi thực hiện việc này trong một trường mới có tên là file_handlers trong tệp kê khai ứng dụng web. Giá trị của thuộc tính này là một mảng các đối tượng có một hành động và một thuộc tính accept. Thao tác này xác định đường dẫn URL mà hệ điều hành sẽ chạy ứng dụng của bạn và đối tượng chấp nhận là các cặp khoá-giá trị của các loại MIME và các tiện ích tệp được liên kết.

{
  "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"]
      }
    }
  ]
}

Bước tiếp theo là xử lý tệp khi ứng dụng khởi chạy. Điều này xảy ra trong giao diện launchQueue, nơi tôi cần thiết lập một người dùng bằng cách gọi setConsumer(). Tham số cho hàm này là một hàm không đồng bộ nhận launchParams. Đối tượng launchParams này có một trường tên là files, giúp tôi lấy được một mảng các mã nhận dạng tệp để làm việc. Tôi chỉ quan tâm đến tệp đầu tiên và từ tệp này, tôi nhận được một blob mà sau đó tôi sẽ truyền đến người bạn cũ của chúng ta 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 });
      });
    });
}

Nếu thấy tốc độ quá nhanh, bạn có thể đọc thêm về File Handling API trong bài viết của tôi. Bạn có thể bật tính năng xử lý tệp bằng cách đặt cờ tính năng thử nghiệm của nền tảng web. Tính năng này dự kiến sẽ ra mắt trong Chrome vào cuối năm nay.

Tích hợp bảng nhớ tạm

Một tính năng thú vị khác của Excalidraw là tích hợp bảng nhớ tạm. Tôi có thể sao chép toàn bộ bản vẽ hoặc chỉ một phần bản vẽ vào bảng nhớ tạm, có thể thêm hình mờ nếu muốn, rồi dán vào một ứng dụng khác. Đây là phiên bản web của ứng dụng Paint trên Windows 95.

Cách thức hoạt động của tính năng này đơn giản đến bất ngờ. Tất cả những gì tôi cần là canvas dưới dạng một blob, sau đó tôi sẽ ghi vào bảng nhớ tạm bằng cách truyền một mảng có một phần tử bằng ClipboardItem với blob vào hàm navigator.clipboard.write(). Để biết thêm thông tin về những việc bạn có thể làm với Clipboard API, hãy xem bài viết của Jason và bài viết của tôi.

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

Cộng tác với người khác

Chia sẻ URL của phiên

Bạn có biết rằng Excalidraw cũng có chế độ cộng tác không? Nhiều người có thể cùng làm việc trên cùng một tài liệu. Để bắt đầu một phiên mới, tôi nhấp vào nút cộng tác trực tiếp rồi bắt đầu một phiên. Tôi có thể dễ dàng chia sẻ URL của phiên với cộng tác viên nhờ Web Share API mà Excalidraw đã tích hợp.

Cộng tác trực tiếp

Tôi đã mô phỏng một phiên cộng tác cục bộ bằng cách làm việc trên biểu trưng Google I/O trên Pixelbook, điện thoại Pixel 3a và iPad Pro của mình. Bạn có thể thấy rằng những thay đổi tôi thực hiện trên một thiết bị sẽ được phản ánh trên tất cả các thiết bị khác.

Tôi thậm chí có thể thấy tất cả con trỏ di chuyển xung quanh. Con trỏ của Pixelbook di chuyển ổn định vì được điều khiển bằng bàn di chuột, nhưng con trỏ của điện thoại Pixel 3a và con trỏ của máy tính bảng iPad Pro lại nhảy lung tung vì tôi điều khiển các thiết bị này bằng cách nhấn ngón tay.

Xem trạng thái của cộng tác viên

Để cải thiện trải nghiệm cộng tác theo thời gian thực, chúng tôi còn có một hệ thống phát hiện trạng thái không hoạt động. Con trỏ của iPad Pro có một chấm màu xanh lục khi tôi sử dụng. Dấu chấm chuyển sang màu đen khi tôi chuyển sang một thẻ trình duyệt hoặc ứng dụng khác. Và khi tôi đang ở trong ứng dụng Excalidraw nhưng không làm gì cả, con trỏ sẽ cho thấy tôi đang ở trạng thái không hoạt động, được biểu thị bằng 3 chữ zZZ.

Những người thường xuyên đọc các bài viết của chúng tôi có thể cho rằng tính năng phát hiện trạng thái không hoạt động được triển khai thông qua API Phát hiện trạng thái không hoạt động, một đề xuất ở giai đoạn đầu đã được thực hiện trong bối cảnh của Dự án Fugu. Cảnh báo tiết lộ nội dung: không phải. Mặc dù đã triển khai dựa trên API này trong Excalidraw, nhưng cuối cùng, chúng tôi quyết định chọn một phương pháp truyền thống hơn dựa trên việc đo lường chuyển động của con trỏ và khả năng hiển thị trang.

Ảnh chụp màn hình về ý kiến phản hồi của Idle Detection (Phát hiện trạng thái rảnh) được lưu trữ trên kho lưu trữ Idle Detection của WICG.

Chúng tôi đã gửi phản hồi về lý do API Phát hiện trạng thái rảnh không giải quyết được trường hợp sử dụng mà chúng tôi có. Tất cả các API của Dự án Fugu đều đang được phát triển công khai, vì vậy, mọi người đều có thể tham gia và đóng góp ý kiến!

Lipis on what is holding back Excalidraw

Nhân tiện, tôi đã hỏi lipis một câu hỏi cuối cùng về những gì mà anh ấy cho là còn thiếu trên nền tảng web khiến Excalidraw chưa phát triển được:

API Quyền truy cập vào hệ thống tệp rất tuyệt, nhưng bạn biết không? Hầu hết các tệp mà tôi quan tâm hiện nay đều nằm trong Dropbox hoặc Google Drive, chứ không phải trên ổ cứng của tôi. Tôi mong muốn File System Access API sẽ có một lớp trừu tượng để các nhà cung cấp hệ thống tệp từ xa như Dropbox hoặc Google tích hợp và các nhà phát triển có thể viết mã dựa trên đó. Sau đó, người dùng có thể yên tâm vì biết rằng các tệp của họ được an toàn với nhà cung cấp dịch vụ đám mây mà họ tin tưởng.

Tôi hoàn toàn đồng ý với lipis, tôi cũng sống trên đám mây. Hy vọng tính năng này sẽ sớm được triển khai.

Chế độ ứng dụng theo thẻ

Rất ấn tượng! Chúng tôi đã thấy rất nhiều hoạt động tích hợp API thực sự tuyệt vời trong Excalidraw. Hệ thống tệp, xử lý tệp, bảng nhớ tạm, chia sẻ trên webmục tiêu chia sẻ trên web. Nhưng còn một điều nữa. Cho đến nay, tôi chỉ có thể chỉnh sửa một tài liệu tại một thời điểm nhất định. Giờ thì không còn chuyện đó nữa! Vui lòng trải nghiệm lần đầu phiên bản phát hành sớm của chế độ ứng dụng theo thẻ trong Excalidraw. Đây là hình ảnh minh hoạ.

Tôi có một tệp hiện có đang mở trong PWA Excalidraw đã cài đặt và đang chạy ở chế độ độc lập. Bây giờ, tôi mở một thẻ mới trong cửa sổ độc lập. Đây không phải là thẻ trình duyệt thông thường mà là thẻ PWA. Trong thẻ mới này, tôi có thể mở một tệp phụ và thao tác trên tệp đó một cách độc lập trong cùng một cửa sổ ứng dụng.

Chế độ ứng dụng theo thẻ đang ở giai đoạn đầu và chưa có gì là chắc chắn. Nếu bạn quan tâm, hãy nhớ đọc thông tin về trạng thái hiện tại của tính năng này trong bài viết của tôi.

Closing (Đang đóng)

Để nắm bắt thông tin về tính năng này và các tính năng khác, hãy nhớ theo dõi trình theo dõi Fugu API của chúng tôi. Chúng tôi rất vui mừng khi có thể thúc đẩy sự phát triển của web và giúp bạn làm được nhiều việc hơn trên nền tảng này. Chúc Excalidraw ngày càng hoàn thiện và chúc bạn xây dựng được nhiều ứng dụng tuyệt vời. Hãy bắt đầu tạo tại excalidraw.com.

Tôi rất mong chờ được thấy một số API mà tôi đã giới thiệu hôm nay xuất hiện trong các ứng dụng của bạn. Tôi là Tom, bạn có thể tìm thấy tôi với tên người dùng @tomayac trên Twitter và Internet nói chung. Cảm ơn bạn rất nhiều vì đã theo dõi và chúc bạn tận hưởng phần còn lại của sự kiện Google I/O.