浏览代码

Add save/load functionality from file (#146)

Fixes #143
Lucas Azzola 5 年之前
父节点
当前提交
7c321b49ab
共有 1 个文件被更改,包括 87 次插入10 次删除
  1. 87 10
      src/index.tsx

+ 87 - 10
src/index.tsx

@@ -460,6 +460,47 @@ function renderScene(
   }
 }
 
+function saveAsJSON() {
+  const serialized = JSON.stringify({
+    version: 1,
+    source: window.location.origin,
+    elements
+  });
+
+  saveFile(
+    "excalidraw.json",
+    "data:text/plain;charset=utf-8," + encodeURIComponent(serialized)
+  );
+}
+
+function loadFromJSON() {
+  const input = document.createElement("input");
+  const reader = new FileReader();
+  input.type = "file";
+  input.accept = ".json";
+
+  input.onchange = () => {
+    if (!input.files!.length) {
+      alert("A file was not selected.");
+      return;
+    }
+
+    reader.readAsText(input.files![0], "utf8");
+  };
+
+  input.click();
+
+  return new Promise(resolve => {
+    reader.onloadend = () => {
+      if (reader.readyState === FileReader.DONE) {
+        const data = JSON.parse(reader.result as string);
+        restore(data.elements, null);
+        resolve();
+      }
+    };
+  });
+}
+
 function exportAsPNG({
   exportBackground,
   exportPadding = 10,
@@ -513,15 +554,21 @@ function exportAsPNG({
     }
   );
 
+  saveFile("excalidraw.png", tempCanvas.toDataURL("image/png"));
+
+  // clean up the DOM
+  if (tempCanvas !== canvas) tempCanvas.remove();
+}
+
+function saveFile(name: string, data: string) {
   // create a temporary <a> elem which we'll use to download the image
   const link = document.createElement("a");
-  link.setAttribute("download", "excalidraw.png");
-  link.setAttribute("href", tempCanvas.toDataURL("image/png"));
+  link.setAttribute("download", name);
+  link.setAttribute("href", data);
   link.click();
 
-  // clean up the DOM
+  // clean up
   link.remove();
-  if (tempCanvas !== canvas) tempCanvas.remove();
 }
 
 function rotate(x1: number, y1: number, x2: number, y2: number, angle: number) {
@@ -709,13 +756,26 @@ function save(state: AppState) {
   localStorage.setItem(LOCAL_STORAGE_KEY_STATE, JSON.stringify(state));
 }
 
-function restore() {
-  try {
-    const savedElements = localStorage.getItem(LOCAL_STORAGE_KEY);
-    const savedState = localStorage.getItem(LOCAL_STORAGE_KEY_STATE);
+function restoreFromLocalStorage() {
+  const savedElements = localStorage.getItem(LOCAL_STORAGE_KEY);
+  const savedState = localStorage.getItem(LOCAL_STORAGE_KEY_STATE);
+
+  return restore(savedElements, savedState);
+}
 
+function restore(
+  savedElements: string | ExcalidrawElement[] | null,
+  savedState: string | null
+) {
+  try {
     if (savedElements) {
-      elements.splice(0, elements.length, ...JSON.parse(savedElements));
+      elements.splice(
+        0,
+        elements.length,
+        ...(typeof savedElements === "string"
+          ? JSON.parse(savedElements)
+          : savedElements)
+      );
       elements.forEach((element: ExcalidrawElement) => generateDraw(element));
     }
 
@@ -843,7 +903,7 @@ class App extends React.Component<{}, AppState> {
     document.addEventListener("keydown", this.onKeyDown, false);
     window.addEventListener("resize", this.onResize, false);
 
-    const savedState = restore();
+    const savedState = restoreFromLocalStorage();
     if (savedState) {
       this.setState(savedState);
     }
@@ -1117,6 +1177,23 @@ class App extends React.Component<{}, AppState> {
               background
             </label>
           </div>
+          <h4>Save/Load</h4>
+          <div className="panelColumn">
+            <button
+              onClick={() => {
+                saveAsJSON();
+              }}
+            >
+              Save as...
+            </button>
+            <button
+              onClick={() => {
+                loadFromJSON().then(() => this.forceUpdate());
+              }}
+            >
+              Load file...
+            </button>
+          </div>
           {someElementIsSelected() && (
             <>
               <h4>Shape options</h4>