Библиотека раскрашенных вручную досок 22.7K Star, какие методы стоит изучить?

внешний интерфейс GitHub JavaScript
Библиотека раскрашенных вручную досок 22.7K Star, какие методы стоит изучить?

⚠️ Эта статья является первой подписанной статьей сообщества Nuggets, и ее перепечатка без разрешения запрещена.

существует7 лет технического письма, делюсь 6 опытомВ этой статье Brother A Bao представляет хороший онлайн-инструмент для рисования, который он часто использует:Excalidraw. С его помощью вы можете легко рисовать всевозможные красивые схемы, нарисованные от руки, в настоящее время на Github.Excalidrawдостиг количества звезд22.7K, так что это также очень хороший проект с открытым исходным кодом.

при обычном использованииExcalidrawВ то время Baoge обнаружил, что онлайн-инструмент предоставляет несколько полезных функций. например сохранить *.excalidrawФайл в указанный каталог, перетащите, чтобы открыть *.excalidrawФайлируйте и сохраняйте в текущий файл, копируйте изображения в буфер обмена, делитесь ссылками только для чтения и сотрудничайте в режиме реального времени.

Совет: на изображении выше показано перетаскивание, чтобы открыть *.excalidrawФайлы и сохранить в текущий файл

Многие из вышеперечисленных функций связаны с файловыми операциями. По поводу обработки файлов брат Абао писал ранееЗагрузка файлов, достаточно понять эти 8 сценариева такжеЗагрузка файла, достаточно понять эти 9 сценариевэти две статьи. И третья статья, брат А Бао возьмет вас на анализExcalidrawТехнология, лежащая в основе этого, связана с манипулированием файлами.

После понимания и освоения этих связанных технологий это может быть полезно в будущей работе, особенно для некоторых сценариев онлайн-веб-редактора, использование этих технологий значительно улучшит взаимодействие с пользователем продукта. Например, на платформах, поддерживающих соответствующие веб-технологии, разработанный вами онлайн-редактор может прекрасно поддерживатьОткрыть -> Изменить -> СохранитьЭто общий поток обработки файлов.

Без лишних слов, давайте сразу к делу, здесь мы сначала разберемСохраните файл .excalidraw в указанный каталог.функция.

1. Сохраните файл в указанный каталог

Совет. Во всех демонстрационных примерах в этой статье используется версия Chrome: версия 92.0.4515.159 (официальная версия) (x86_64).

Приведенная выше Gif-анимация демонстрирует процесс сохранения файла в указанную директорию, т.к.ExcalidrawЭтот онлайн-инструмент имеет открытый исходный код, поэтому, анализируя егоисходный код, находим реализациюСохраните файл в указанную директориюФункция реализации функции:

// https://github.com/excalidraw/excalidraw/blob/master/src/data/json.ts#L31
import { fileOpen, fileSave } from "browser-fs-access";

export const saveAsJSON = async (
  elements: readonly ExcalidrawElement[],
  appState: AppState,
) => {
  const serialized = serializeAsJSON(elements, appState);
  const blob = new Blob([serialized], {
    type: MIME_TYPES.excalidraw,
  });
	
  const fileHandle = await fileSave(
    blob,
    {
      fileName: `${appState.name}.excalidraw`,
      description: "Excalidraw file",
      extensions: [".excalidraw"],
    },
    isImageFileHandle(appState.fileHandle) ? null : appState.fileHandle,
  );
  return { fileHandle };
};

Как видно из приведенного выше кода, вsaveAsJSONВнутри функции вызываетсяfileSaveфункция сохранения файла.fileSaveфункция изbrowser-fs-accessЭта третья библиотека импортирована. Библиотека инкапсулируетFile_System_Access_API, который предоставляет разработчикамЧтение, запись файлов и управление файламиСпособность. а такжеСохраните файл в указанную директориюфункцию, черезshowSaveFilePickerметод достижения. существуетshowSaveFilePickerДо появления метода функция сохранения файлов была реализована на стороне клиента, более распространенным решением было использованиеэтикеткаилиFileSaver.jsэта библиотека.

const saveFile = async (blob, filename) => {
  const a = document.createElement('a');
  a.download = filename;
  a.href = URL.createObjectURL(blob);
  a.addEventListener('click', (e) => {
    setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
  });
  a.click();
};

Совет: Если вы хотите узнать о других способах загрузки файлов, вы можете прочитатьЗагрузка файла, достаточно понять эти 9 сценариевЭта статья.

Для представленной выше схемы сохранения файлов на стороне клиента самая большая проблема заключается в том, что ее невозможно реализовать.Открыть -> Изменить -> СохранитьЭто общий процесс работы с файлами. Поскольку у нас нет возможности перезаписать исходный файл, мы можем только создать новый. и использовать новыйFile_System_Access_APIНапример, чтобы решить вышеуказанную проблему, мы можем использоватьwindow.showOpenFilePickerметод, чтобы открыть файл, и после того, как файл был отредактирован, используйтеwindow.showSaveFilePickerчтобы сохранить файл.

(адрес текстового редактора:Google Chrome labs.GitHub.IO/текстовый редактор…

Давайте представимshowSaveFilePickerАПИ, этоWindowМетод, определенный в интерфейсе. После вызова этого метода будет отображаться средство выбора файла, позволяющее пользователю выбрать путь для сохранения. Сигнатура метода выглядит так:

let FileSystemFileHandle = Window.showSaveFilePicker(options);

showSaveFilePickerМетод поддерживает необязательный параметр типа object, который может содержать следующие свойства:

  • excludeAcceptAllOption: Логический тип, значение по умолчаниюfalse. По умолчанию селектор должен содержать параметр, позволяющий не применять фильтры по типам файлов (как указано в следующемtypesопция включена). Установите этот параметр наtrueиметь в видуtypesВарианты недоступны.
  • types: Тип массива, указывающий список типов файлов, которые разрешено сохранять. Каждый элемент массива представляет собой объект конфигурации, содержащий следующие свойства:
    • description(可选): Используется для описания категории типов файлов, которые разрешено сохранять.
    • accept: это объект, чейkeyдаMIMEТип, значение — список расширений файлов.

передачаshowSaveFilePickerметод, он вернетFileSystemFileHandleобъект. С помощью этого объекта вы можете вызывать методы объекта для управления файлом. Например, вызовcreateWritableметод, он вернетFileSystemWritableFileStreamобъект, вы можете записать данные в файл. Конкретное использование заключается в следующем:

async function saveFile(blob, filename) {
  try {
    const handle = await window.showSaveFilePicker({
      suggestedName: filename,
      types: [
        {
          description: "PNG file",
          accept: {
            "image/png": [".png"],
          },
        },
      ],
     });
    const writable = await handle.createWritable();
    await writable.write(blob);
    await writable.close();
    return handle;
  } catch (err) {
     console.error(err.name, err.message);
  }
}

saveFile(imgBlob, "face.png");

когда вы используете вышеsaveFileфункция, при сохранении изображения отображается следующий селектор файла сохранения:

Смотри сюда, тебе не кажетсяshowSaveFilePickerФункция API довольно мощная, но, к сожалению, совместимость API в настоящее время не очень хорошая, как показано на следующем рисунке:

(Источник изображения:потрите newser.com/?search=yes о…

showSaveFilePickerдаFile System Accessметоды, определенные в API, за исключениемshowSaveFilePickerКроме того, естьshowOpenFilePickerа такжеshowDirectoryPickerи другие методы. Далее Brother Abao кратко представит два других полезных API.

showOpenFilePickerАПИ, этоWindowМетод, определенный в интерфейсе, который при вызове отображает средство выбора файлов, позволяющее пользователю выбрать один или несколько файлов. Сигнатура метода выглядит так:

let FileSystemHandles = Window.showOpenFilePicker();

showOpenFilePickerМетод поддерживает необязательный параметр типа object, который может содержать следующие свойства:

  • multiple: Логический тип, значение по умолчаниюfalse. Если установленоtrue, можно выбрать несколько файлов.
  • excludeAcceptAllOption: Логический тип, значение по умолчаниюfalse. По умолчанию селектор должен содержать параметр, позволяющий не применять фильтры по типам файлов (как указано в следующемtypesопция включена). Установите этот параметр наtrueиметь в видуtypesВарианты недоступны.
  • types: Тип массива, указывающий список типов файлов, которые разрешено сохранять. Каждый элемент массива представляет собой объект конфигурации, содержащий следующие свойства:
    • description(可选): Используется для описания категории типов файлов, которые разрешено сохранять.
    • accept: это объект, чейkeyдаMIMEТип, значение — список расширений файлов.

передачаshowOpenFilePickerПосле метода он вернетFileSystemHandlesкоторыйFileSystemFileHandleмассив объектов. имеютFileSystemFileHandleобъект, вы можете затем вызывать методы этого объекта для управления файлом. Возьмем простой пример использования:

<div>
   <textarea id="container" rows="5" cols="30"></textarea>
</div>
<button onclick="openFile()">打开文件</button>
<script>
   const container = document.querySelector("#container");
  
   async function openFile() {
     let [fileHandle] = await window.showOpenFilePicker();
     const file = await fileHandle.getFile();
     const contents = await file.text();
     container.value = contents;
   }
</script>

В приведенном выше примере, когда пользователь нажимаетоткрыть файлкнопка, отображается селектор файлов. После выбора текстового файла содержимое файла будет отображаться вtextarea#containerтекстовое окно. Для нетекстовых файлов это можно сделать, вызвавarrayBufferметод для чтения двоичного содержимого файла.

(Источник изображения:потрите newser.com/?search=yes о…

Как видно из приведенного выше рисунка, токshowOpenFilePickerСовместимость API по-прежнему относительно плохая. но если вы хотите поддержатьFile System AccessНа платформе API, если вы отдаете приоритет использованию этих API, вы можете рассмотреть возможность использованияGoogleChromeLabsОткрытый исходный кодbrowser-fs-accessэта библиотека, которая позволяет вам поддерживатьFile System AccessПроще использовать на платформе APIFile System AccessAPI, в то время как для неподдерживаемых платформ он будет автоматически понижен до использования<input type="file">а также<a download>Путь.

Помимо выбора файлов, мы также можем выбирать каталоги. Для этого сценария мы можем использоватьshowDirectoryPickerAPI. этоWindow接口中定义的方法,调用该方法后会显示一个允许用户选择目录的选择器。 Сигнатура метода выглядит так:

var FileSystemDirectoryHandle = Window.showDirectoryPicker();

с ранее описаннымshowOpenFilePickerРазница в том, что вызовshowDirectoryPickerПосле того, как метод, возвратFileSystemDirectoryHandleобъект. Используя этот объект, мы можем выполнять некоторые операции, связанные с каталогом. Например, прочитать информацию о каталоге, прочитать указанный файл в каталоге, удалить указанный файл в каталоге или создать новый файл в каталоге. Опять же, давайте возьмем несколько простых примеров.

читать информацию каталога

async function readDirectory() {
  const dirHandle = await window.showDirectoryPicker();
  for await (const entry of dirHandle.values()) {
    console.log(entry.kind, entry.name);
  }
}

Прочитать указанный файл в каталоге

const container = document.querySelector("#container");

async function readFile() {
  const dirHandle = await window.showDirectoryPicker();
  const fileHandle = await dirHandle.getFileHandle("hello.txt");
  const file = await fileHandle.getFile();
  const contents = await file.text();
  container.value = contents;
}

Удалить указанный файл в каталоге

async function removeFile() {
  const dirHandle = await window.showDirectoryPicker();
  const result = await dirHandle.removeEntry("hello.copy.txt");
  container.value = `删除hello.copy.txt文件${
    typeof result == "undefined" ? "成功" : "失败"
  }`;
}

должны знать о том,removeEntryВ дополнение к поддержке удаления указанного файла метод также может поддерживать удаление указанного каталога.

Создать указанный файл

async function createFile() {
  const dirHandle = await window.showDirectoryPicker();
  const fileHandle = await dirHandle.getFileHandle("hello.new.txt", {
    create: true,
  });
  container.value = "hello.new.txt文件创建成功!";
  const writable = await fileHandle.createWritable();
  await writable.write(new Blob(["大家好,我是阿宝哥!"]));
  await writable.close();
}

В приведенном выше коде мы вызываемgetFileHandleметод получения указанного файла, соответствующийFileSystemFileHandleобъект.create: trueУказывает, что если указанный файл не найден в текущем каталоге, будет создан новый файл. После понимания приведенных выше примеров чувствуете ли вы, что возможности браузера по обработке файлов становятся все сильнее и сильнее. Аналогично, давайте посмотрим наshowDirectoryPickerСовместимость с API:

(Источник изображения:потрите newser.com/?search=yes о…

2. Перетащите, чтобы открыть *.excalidrawфайл и сохранить в текущий файл

Приведенная выше анимация Gif демонстрирует перетаскивание, чтобы открыть *.excalidrawВ процессе сохранения файла в текущий файл можно обнаружить, что после редактирования файла нам нужно только подтвердить, следует ли сохранять файл, не выбирая путь сохранения файла, что значительно улучшает взаимодействие с пользователем.

class App extends React.Component<AppProps, AppState> {
	  // 省略大部分代码
    const file = event.dataTransfer?.files[0];
    if (
      file?.type === MIME_TYPES.excalidrawlib ||
      file?.name?.endsWith(".excalidrawlib")
    ) {
      // 处理导入的控件库的逻辑
    } else {
      this.setState({ isLoading: true });
      if (fsSupported) { // 判断是否支持File System Access API
        try {
          const item = event.dataTransfer.items[0];
          // 关键点:获取FileSystemHandle对象
          (file as any).handle = await (item as any).getAsFileSystemHandle();
        } catch (error) {
          console.warn(error.name, error.message);
        }
      }
      // 加载.excalidraw文件到Canvas
      await this.loadFileToCanvas(file);
    }
  };
}

Ключевым моментом приведенного выше кода является то, что вызовDataTransferItem.getAsFileSystemHandle()способ получитьFileSystemFileHandleобъект. Получив этот объект, мы можем читать и писать в файл. Конкретное использование заключается в следующем:

Пример чтения файла

async function getTheFile() {
  // open file picker
  [fileHandle] = await window.showOpenFilePicker(pickerOpts);

  // get file contents
  const fileData = await fileHandle.getFile();
}

пример файла записи

async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();

  // Write the contents of the file to the stream.
  await writable.write(contents);

  // Close the file and write the contents to disk.
  await writable.close();
}

3. Скопируйте изображение в буфер обмена

// https://github.com/excalidraw/excalidraw/blob/master/src/clipboard.ts
export const copyBlobToClipboardAsPng = async (blob: Blob) => {
  await navigator.clipboard.write([
    new window.ClipboardItem({ "image/png": blob }),
  ]);
};

В приведенном выше кодеcopyBlobToClipboardAsPngфункция поддерживаетblobпараметр, который будет вызываться внутри функцииnavigator.clipboard.writeметод копирования изображения в буфер обмена. А для обычного текста можно пройтиnavigator.clipboard.writeTextспособ записи их в системный буфер обмена.

фактическиnavigator.clipboard.writeа такжеnavigator.clipboard.writeTextпутьClipboardметоды, определенные интерфейсом, реализующимClipboardAPI, если пользователь предоставляет соответствующие разрешения, предоставляет доступ для чтения и записи к системному буферу обмена. В веб-приложении API буфера обмена можно использовать для реализации функций вырезания, копирования и вставки. Этот API используется для заменыdocument.execCommandAPI для реализации операций с буфером обмена.

В реальной работе нам не нужно вручную создаватьClipboardобъект, но черезnavigator.clipboardполучитьClipboardОбъект:

в полученииClipboardПосле объекта мы можем использовать API, предоставляемый объектом, для доступа к буферу обмена. Например, поnavigator.clipboard.readTextспособ чтения содержимого буфера обмена:

navigator.clipboard.readText().then(
  clipText => document.querySelector(".editor").innerText = clipText
);

Приведенный выше код будет включать в себя HTML.editorСодержимое первого элемента класса заменяется содержимым буфера обмена. Если буфер обмена пуст или не содержит текста, содержимое элемента будет очищено. Это связано с тем, что когда буфер обмена пуст или не содержит текста,readTextМетод возвращает пустую строку.

API асинхронного буфера обмена — это относительно новый API, и браузеры все еще постепенно внедряют его. Из-за потенциальных проблем с безопасностью и технической сложности большинство браузеров постепенно интегрируют этот API. В настоящее времяNavigator API: clipboardСовместимость показана на следующем рисунке:

(Источник изображения:потрите newser.com/madonna-api_thatv…

Для расширений браузера вы можете запроситьclipboardReadа такжеclipboardWriteРазрешение на использование clipboard.readText() и clipboard.writeText(). Если вас интересуют другие API буфера обмена, вы можете прочитатьХотите скопировать изображение? Взгляните на API буфера обменаЭта статья.

На самом деле, помимо методов, описанных выше,ExcalidrawДругие веб-API также используются для реализации определенных функций. такие как использованиеwindow.cryptoКогда API используется для экспорта ссылок только для чтения, данные холста шифруются и защищаются. использоватьWebSocketAPI для совместного редактирования и использованияShareВ API реализована функция обмена файлами, заинтересованные партнеры могут прочитатьExcalidrawсоответствующий исходный код.

4. Резюме

В этой статье брат Абао анализируетExcalidrawЭтот онлайн-инструмент рисования, предоставил некоторые приятные функции для использования технологий. Надеюсь, закончите чтение этой статьи, выFile_System_Access_APIопределено вwindow.showOpenFilePicker,window.showSaveFilePicker,window.showDirectoryPickerа такжеDataTransferItem.getAsFileSystemHandleЭти методы имеют определенное понимание.

Из-за текущегоFile_System_Access_APIСовместимость не очень, если хотите использовать в проекте скажем. Мы предлагаем вам использоватьGoogleChromeLabsОткрытый исходный кодbrowser-fs-accessЭта библиотека, которая не только предоставляет нам более лаконичный API, но и предоставляет схему автоматического перехода на более раннюю версию. В будущих проектах, если у вас есть возможность, вы можете попробовать это.

5. Справочные ресурсы