@8btc/xcanvas 0.0.14-beta.0 → 0.0.14-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export declare interface XCanvasAPI {
13
13
  insertImgs: (url: string[]) => Promise<void>;
14
14
  /** 追加多张图片到画布末尾, 如果画布中存在对应图片(url相同),则不做任何操作 */
15
15
  appendImgs: (urls: string[]) => Promise<void>;
16
+ appendImgsForMaze: (urls: string[]) => Promise<void>;
16
17
  /** 保存canvas数据 */
17
18
  save: () => any;
18
19
  /** 从数据中恢复canvas画布 */
@@ -58,6 +59,18 @@ export declare interface XCanvasAPI {
58
59
  width: number;
59
60
  height: number;
60
61
  }) => string | undefined;
62
+ /**
63
+ * 判断当前是否可以裁切图片
64
+ */
65
+ predicateCropImage: () => boolean;
66
+ /**
67
+ * 裁切图片
68
+ */
69
+ cropImage: () => void;
70
+ /**
71
+ * 删除选区里的全部元素
72
+ */
73
+ deleteSelection: () => void;
61
74
  }
62
75
 
63
76
  export declare interface XcanvasProps extends ExcalidrawProps {
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
3
3
  import { Excalidraw, convertToExcalidrawElements, exportToCanvas } from "@8btc/excalidraw";
4
4
  import { useEffect } from "react";
5
- const version = "0.0.14-beta.0";
5
+ const version = "0.0.14-beta.10";
6
6
  const packageJson = {
7
7
  version
8
8
  };
@@ -40,7 +40,7 @@ function CustomExcalidraw(props) {
40
40
  function fetchImageAsDataUrl(src) {
41
41
  return new Promise((resolve, reject) => {
42
42
  const img = new Image();
43
- img.crossOrigin = "Anonymous";
43
+ img.crossOrigin = "anonymous";
44
44
  img.onload = () => {
45
45
  const canvas = document.createElement("canvas");
46
46
  canvas.width = img.naturalWidth;
@@ -118,15 +118,18 @@ function ApiFactory() {
118
118
  };
119
119
  })
120
120
  );
121
+ const files = excalidrawAPI.getFiles();
121
122
  rets.forEach((it) => {
122
- excalidrawAPI.addFiles([
123
- {
124
- mimeType: "image/png",
125
- id: it.url,
126
- dataURL: it.dataUrl,
127
- created: Date.now()
128
- }
129
- ]);
123
+ if (!files[it.url]) {
124
+ excalidrawAPI.addFiles([
125
+ {
126
+ mimeType: "image/png",
127
+ id: it.url,
128
+ dataURL: it.dataUrl,
129
+ created: Date.now()
130
+ }
131
+ ]);
132
+ }
130
133
  });
131
134
  const places = findPlaceForNewElements(
132
135
  excalidrawAPI.getAppState(),
@@ -153,6 +156,13 @@ function ApiFactory() {
153
156
  animate: currentElements.length === 0 ? false : true
154
157
  });
155
158
  };
159
+ const getSelectedElements = (elements, appState) => {
160
+ const selectedIds = appState.selectedElementIds;
161
+ const selectedElements = elements.filter(
162
+ (element) => selectedIds[element.id]
163
+ );
164
+ return selectedElements;
165
+ };
156
166
  setApi({
157
167
  rawApi: excalidrawAPI,
158
168
  __printCanvasInfo: () => {
@@ -165,9 +175,47 @@ function ApiFactory() {
165
175
  },
166
176
  insertImgs,
167
177
  appendImgs: async (urls) => {
168
- const files = excalidrawAPI.getFiles();
169
- const newUrls = urls.filter((it) => !files[it]);
170
- await insertImgs(newUrls);
178
+ const els = excalidrawAPI.getSceneElements();
179
+ const imgEls = els.filter((el) => el.type === "image");
180
+ const idSet = /* @__PURE__ */ new Set();
181
+ const intersecitonEls = [];
182
+ imgEls.forEach((el) => {
183
+ idSet.add(el.fileId);
184
+ if (urls.includes(el.fileId)) {
185
+ intersecitonEls.push(el);
186
+ }
187
+ });
188
+ const newUrls = urls.filter((url) => !idSet.has(url));
189
+ if (newUrls.length !== 0) {
190
+ await insertImgs(newUrls);
191
+ } else {
192
+ excalidrawAPI.scrollToContent(intersecitonEls, {
193
+ animate: els.length === 0 ? false : true
194
+ });
195
+ }
196
+ },
197
+ // 插入的图片后缀会自动带上时间戳后缀,用于解决maze跨域问题
198
+ // `${it}?t=${Date.now()}`;
199
+ appendImgsForMaze: async (urls) => {
200
+ const els = excalidrawAPI.getSceneElements();
201
+ const imgEls = els.filter((el) => el.type === "image");
202
+ const idSet = /* @__PURE__ */ new Set();
203
+ const intersecitonEls = [];
204
+ imgEls.forEach((el) => {
205
+ let elFileId = el.fileId.split("?t=")[0];
206
+ idSet.add(elFileId);
207
+ if (urls.includes(elFileId)) {
208
+ intersecitonEls.push(el);
209
+ }
210
+ });
211
+ const newUrls = urls.filter((url) => !idSet.has(url)).map((url) => `${url}?t=${Date.now()}`);
212
+ if (newUrls.length !== 0) {
213
+ await insertImgs(newUrls);
214
+ } else {
215
+ excalidrawAPI.scrollToContent(intersecitonEls, {
216
+ animate: els.length === 0 ? false : true
217
+ });
218
+ }
171
219
  },
172
220
  save: () => {
173
221
  const appState = excalidrawAPI.getAppState();
@@ -282,14 +330,15 @@ function ApiFactory() {
282
330
  exportSelection: async () => {
283
331
  if (!excalidrawAPI) return;
284
332
  const appState = excalidrawAPI.getAppState();
285
- const selectedIds = appState.selectedElementIds;
286
- if (Object.keys(selectedIds).length === 0) {
333
+ const selectedElements = getSelectedElements(
334
+ excalidrawAPI.getSceneElements(),
335
+ appState
336
+ );
337
+ if (selectedElements.length === 0) {
287
338
  throw "没有选中任何元素";
288
339
  }
289
- const selectedElements = excalidrawAPI.getSceneElements().filter((element) => appState.selectedElementIds[element.id]);
290
340
  const files = excalidrawAPI.getFiles();
291
- if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId) {
292
- console.log(files[selectedElements[0].fileId].dataURL);
341
+ if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId && !selectedElements[0].crop) {
293
342
  if (selectedElements[0].fileId.startsWith("http")) {
294
343
  return selectedElements[0].fileId;
295
344
  }
@@ -298,8 +347,7 @@ function ApiFactory() {
298
347
  const canvas = await exportToCanvas({
299
348
  elements: selectedElements,
300
349
  appState: {
301
- ...appState,
302
- selectedElementIds: selectedIds
350
+ ...appState
303
351
  },
304
352
  files,
305
353
  mimeType: "image/png",
@@ -312,11 +360,10 @@ function ApiFactory() {
312
360
  insertCustomElement: (data, positonCalculator) => {
313
361
  if (!excalidrawAPI) return;
314
362
  const appState = excalidrawAPI.getAppState();
315
- const selectedIds = appState.selectedElementIds;
316
- if (Object.keys(selectedIds).length === 0) {
317
- throw "没有选中任何元素";
318
- }
319
- const selectedElements = excalidrawAPI.getSceneElements().filter((element) => appState.selectedElementIds[element.id]);
363
+ const selectedElements = getSelectedElements(
364
+ excalidrawAPI.getSceneElements(),
365
+ appState
366
+ );
320
367
  if (selectedElements.length === 0) {
321
368
  throw "没有选中任何可见元素";
322
369
  }
@@ -344,18 +391,18 @@ function ApiFactory() {
344
391
  y: minY,
345
392
  width: h,
346
393
  height: h,
347
- ...customPos,
348
- id: `loading-${Date.now()}`,
349
- link: data.link ?? "https://www.google.com",
350
394
  // 添加必需的基本样式属性
351
- strokeColor: "#ffffff",
395
+ strokeColor: "#ffffff00",
352
396
  backgroundColor: "transparent",
353
397
  fillStyle: "solid",
354
- strokeWidth: 1,
398
+ strokeWidth: 2,
355
399
  strokeStyle: "solid",
356
400
  roundness: null,
357
- roughness: 1,
401
+ roughness: 0,
358
402
  opacity: 100,
403
+ ...customPos,
404
+ id: `loading-${Date.now()}`,
405
+ link: data.link ?? "https://www.google.com",
359
406
  // 添加必需的变换属性
360
407
  angle: 0,
361
408
  seed: Math.random(),
@@ -383,6 +430,45 @@ function ApiFactory() {
383
430
  elements: [...currentElements, ...el]
384
431
  });
385
432
  return el[0].id;
433
+ },
434
+ predicateCropImage: () => {
435
+ const appState = excalidrawAPI.getAppState();
436
+ const selectedElements = getSelectedElements(
437
+ excalidrawAPI.getSceneElements(),
438
+ appState
439
+ );
440
+ if (!appState.croppingElementId && selectedElements.length === 1 && selectedElements[0].type === "image") {
441
+ return true;
442
+ }
443
+ return false;
444
+ },
445
+ cropImage: () => {
446
+ const appState = excalidrawAPI.getAppState();
447
+ const selectedIds = appState.selectedElementIds;
448
+ const selectedElements = excalidrawAPI.getSceneElements().filter((element) => selectedIds[element.id]);
449
+ if (selectedElements.length !== 1) return;
450
+ excalidrawAPI.updateScene({
451
+ appState: {
452
+ ...appState,
453
+ isCropping: false,
454
+ croppingElementId: selectedElements[0].id
455
+ }
456
+ });
457
+ },
458
+ deleteSelection: () => {
459
+ const appState = excalidrawAPI.getAppState();
460
+ const selectedIds = appState.selectedElementIds;
461
+ const currentElements = excalidrawAPI.getSceneElements();
462
+ const newElements = currentElements.filter((el) => {
463
+ return !selectedIds[el.id];
464
+ });
465
+ excalidrawAPI.updateScene({
466
+ elements: [...newElements],
467
+ appState: {
468
+ ...appState,
469
+ selectedElementIds: {}
470
+ }
471
+ });
386
472
  }
387
473
  });
388
474
  }, [excalidrawAPI, setApi]);
@@ -2,7 +2,7 @@
2
2
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("jotai"), require("@8btc/excalidraw"), require("react")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "jotai", "@8btc/excalidraw", "react"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.XCanvas = {}, global.jsxRuntime, global.Jotai, global.Excalidraw, global.React));
3
3
  })(this, (function(exports2, jsxRuntime, jotai, excalidraw, react) {
4
4
  "use strict";
5
- const version = "0.0.14-beta.0";
5
+ const version = "0.0.14-beta.10";
6
6
  const packageJson = {
7
7
  version
8
8
  };
@@ -40,7 +40,7 @@
40
40
  function fetchImageAsDataUrl(src) {
41
41
  return new Promise((resolve, reject) => {
42
42
  const img = new Image();
43
- img.crossOrigin = "Anonymous";
43
+ img.crossOrigin = "anonymous";
44
44
  img.onload = () => {
45
45
  const canvas = document.createElement("canvas");
46
46
  canvas.width = img.naturalWidth;
@@ -118,15 +118,18 @@
118
118
  };
119
119
  })
120
120
  );
121
+ const files = excalidrawAPI.getFiles();
121
122
  rets.forEach((it) => {
122
- excalidrawAPI.addFiles([
123
- {
124
- mimeType: "image/png",
125
- id: it.url,
126
- dataURL: it.dataUrl,
127
- created: Date.now()
128
- }
129
- ]);
123
+ if (!files[it.url]) {
124
+ excalidrawAPI.addFiles([
125
+ {
126
+ mimeType: "image/png",
127
+ id: it.url,
128
+ dataURL: it.dataUrl,
129
+ created: Date.now()
130
+ }
131
+ ]);
132
+ }
130
133
  });
131
134
  const places = findPlaceForNewElements(
132
135
  excalidrawAPI.getAppState(),
@@ -153,6 +156,13 @@
153
156
  animate: currentElements.length === 0 ? false : true
154
157
  });
155
158
  };
159
+ const getSelectedElements = (elements, appState) => {
160
+ const selectedIds = appState.selectedElementIds;
161
+ const selectedElements = elements.filter(
162
+ (element) => selectedIds[element.id]
163
+ );
164
+ return selectedElements;
165
+ };
156
166
  setApi({
157
167
  rawApi: excalidrawAPI,
158
168
  __printCanvasInfo: () => {
@@ -165,9 +175,47 @@
165
175
  },
166
176
  insertImgs,
167
177
  appendImgs: async (urls) => {
168
- const files = excalidrawAPI.getFiles();
169
- const newUrls = urls.filter((it) => !files[it]);
170
- await insertImgs(newUrls);
178
+ const els = excalidrawAPI.getSceneElements();
179
+ const imgEls = els.filter((el) => el.type === "image");
180
+ const idSet = /* @__PURE__ */ new Set();
181
+ const intersecitonEls = [];
182
+ imgEls.forEach((el) => {
183
+ idSet.add(el.fileId);
184
+ if (urls.includes(el.fileId)) {
185
+ intersecitonEls.push(el);
186
+ }
187
+ });
188
+ const newUrls = urls.filter((url) => !idSet.has(url));
189
+ if (newUrls.length !== 0) {
190
+ await insertImgs(newUrls);
191
+ } else {
192
+ excalidrawAPI.scrollToContent(intersecitonEls, {
193
+ animate: els.length === 0 ? false : true
194
+ });
195
+ }
196
+ },
197
+ // 插入的图片后缀会自动带上时间戳后缀,用于解决maze跨域问题
198
+ // `${it}?t=${Date.now()}`;
199
+ appendImgsForMaze: async (urls) => {
200
+ const els = excalidrawAPI.getSceneElements();
201
+ const imgEls = els.filter((el) => el.type === "image");
202
+ const idSet = /* @__PURE__ */ new Set();
203
+ const intersecitonEls = [];
204
+ imgEls.forEach((el) => {
205
+ let elFileId = el.fileId.split("?t=")[0];
206
+ idSet.add(elFileId);
207
+ if (urls.includes(elFileId)) {
208
+ intersecitonEls.push(el);
209
+ }
210
+ });
211
+ const newUrls = urls.filter((url) => !idSet.has(url)).map((url) => `${url}?t=${Date.now()}`);
212
+ if (newUrls.length !== 0) {
213
+ await insertImgs(newUrls);
214
+ } else {
215
+ excalidrawAPI.scrollToContent(intersecitonEls, {
216
+ animate: els.length === 0 ? false : true
217
+ });
218
+ }
171
219
  },
172
220
  save: () => {
173
221
  const appState = excalidrawAPI.getAppState();
@@ -282,14 +330,15 @@
282
330
  exportSelection: async () => {
283
331
  if (!excalidrawAPI) return;
284
332
  const appState = excalidrawAPI.getAppState();
285
- const selectedIds = appState.selectedElementIds;
286
- if (Object.keys(selectedIds).length === 0) {
333
+ const selectedElements = getSelectedElements(
334
+ excalidrawAPI.getSceneElements(),
335
+ appState
336
+ );
337
+ if (selectedElements.length === 0) {
287
338
  throw "没有选中任何元素";
288
339
  }
289
- const selectedElements = excalidrawAPI.getSceneElements().filter((element) => appState.selectedElementIds[element.id]);
290
340
  const files = excalidrawAPI.getFiles();
291
- if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId) {
292
- console.log(files[selectedElements[0].fileId].dataURL);
341
+ if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId && !selectedElements[0].crop) {
293
342
  if (selectedElements[0].fileId.startsWith("http")) {
294
343
  return selectedElements[0].fileId;
295
344
  }
@@ -298,8 +347,7 @@
298
347
  const canvas = await excalidraw.exportToCanvas({
299
348
  elements: selectedElements,
300
349
  appState: {
301
- ...appState,
302
- selectedElementIds: selectedIds
350
+ ...appState
303
351
  },
304
352
  files,
305
353
  mimeType: "image/png",
@@ -312,11 +360,10 @@
312
360
  insertCustomElement: (data, positonCalculator) => {
313
361
  if (!excalidrawAPI) return;
314
362
  const appState = excalidrawAPI.getAppState();
315
- const selectedIds = appState.selectedElementIds;
316
- if (Object.keys(selectedIds).length === 0) {
317
- throw "没有选中任何元素";
318
- }
319
- const selectedElements = excalidrawAPI.getSceneElements().filter((element) => appState.selectedElementIds[element.id]);
363
+ const selectedElements = getSelectedElements(
364
+ excalidrawAPI.getSceneElements(),
365
+ appState
366
+ );
320
367
  if (selectedElements.length === 0) {
321
368
  throw "没有选中任何可见元素";
322
369
  }
@@ -344,18 +391,18 @@
344
391
  y: minY,
345
392
  width: h,
346
393
  height: h,
347
- ...customPos,
348
- id: `loading-${Date.now()}`,
349
- link: data.link ?? "https://www.google.com",
350
394
  // 添加必需的基本样式属性
351
- strokeColor: "#ffffff",
395
+ strokeColor: "#ffffff00",
352
396
  backgroundColor: "transparent",
353
397
  fillStyle: "solid",
354
- strokeWidth: 1,
398
+ strokeWidth: 2,
355
399
  strokeStyle: "solid",
356
400
  roundness: null,
357
- roughness: 1,
401
+ roughness: 0,
358
402
  opacity: 100,
403
+ ...customPos,
404
+ id: `loading-${Date.now()}`,
405
+ link: data.link ?? "https://www.google.com",
359
406
  // 添加必需的变换属性
360
407
  angle: 0,
361
408
  seed: Math.random(),
@@ -383,6 +430,45 @@
383
430
  elements: [...currentElements, ...el]
384
431
  });
385
432
  return el[0].id;
433
+ },
434
+ predicateCropImage: () => {
435
+ const appState = excalidrawAPI.getAppState();
436
+ const selectedElements = getSelectedElements(
437
+ excalidrawAPI.getSceneElements(),
438
+ appState
439
+ );
440
+ if (!appState.croppingElementId && selectedElements.length === 1 && selectedElements[0].type === "image") {
441
+ return true;
442
+ }
443
+ return false;
444
+ },
445
+ cropImage: () => {
446
+ const appState = excalidrawAPI.getAppState();
447
+ const selectedIds = appState.selectedElementIds;
448
+ const selectedElements = excalidrawAPI.getSceneElements().filter((element) => selectedIds[element.id]);
449
+ if (selectedElements.length !== 1) return;
450
+ excalidrawAPI.updateScene({
451
+ appState: {
452
+ ...appState,
453
+ isCropping: false,
454
+ croppingElementId: selectedElements[0].id
455
+ }
456
+ });
457
+ },
458
+ deleteSelection: () => {
459
+ const appState = excalidrawAPI.getAppState();
460
+ const selectedIds = appState.selectedElementIds;
461
+ const currentElements = excalidrawAPI.getSceneElements();
462
+ const newElements = currentElements.filter((el) => {
463
+ return !selectedIds[el.id];
464
+ });
465
+ excalidrawAPI.updateScene({
466
+ elements: [...newElements],
467
+ appState: {
468
+ ...appState,
469
+ selectedElementIds: {}
470
+ }
471
+ });
386
472
  }
387
473
  });
388
474
  }, [excalidrawAPI, setApi]);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@8btc/xcanvas",
3
3
  "private": false,
4
- "version": "0.0.14-beta.0",
4
+ "version": "0.0.14-beta.10",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {