@8btc/xcanvas 0.0.14-beta.1 → 0.0.14-beta.11
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 +10 -1
- package/dist/index.js +85 -23
- package/dist/index.umd.cjs +85 -23
- package/package.json +1 -1
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画布 */
|
|
@@ -32,10 +33,14 @@ export declare interface XCanvasAPI {
|
|
|
32
33
|
reset: () => void;
|
|
33
34
|
/**
|
|
34
35
|
* 导出当前选区为图片(dataUrl), 特殊情况:当前选区为单张外部传入图片,则直接返回外部图片url
|
|
36
|
+
*
|
|
37
|
+
* @param tryToSplit 是否尝试拆分选区内的图片元素(当选区内全是图片时生效)
|
|
38
|
+
* 如果选区内的元素都是图片,则返回选区内图片url数组
|
|
39
|
+
* 如果选区内有非图片元素,则返回选区区域的dataUrl
|
|
35
40
|
* @returns dataUrl | url
|
|
36
41
|
* @throws 如果没有选区,调用这个函数会抛出错误
|
|
37
42
|
*/
|
|
38
|
-
exportSelection: () => Promise<string>;
|
|
43
|
+
exportSelection: (tryToSplit?: boolean) => Promise<string | string[]>;
|
|
39
44
|
/**
|
|
40
45
|
* 插入embeddableElement元素
|
|
41
46
|
*
|
|
@@ -66,6 +71,10 @@ export declare interface XCanvasAPI {
|
|
|
66
71
|
* 裁切图片
|
|
67
72
|
*/
|
|
68
73
|
cropImage: () => void;
|
|
74
|
+
/**
|
|
75
|
+
* 删除选区里的全部元素
|
|
76
|
+
*/
|
|
77
|
+
deleteSelection: () => void;
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
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.
|
|
5
|
+
const version = "0.0.14-beta.11";
|
|
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 = "
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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(),
|
|
@@ -172,9 +175,47 @@ function ApiFactory() {
|
|
|
172
175
|
},
|
|
173
176
|
insertImgs,
|
|
174
177
|
appendImgs: async (urls) => {
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
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
|
+
}
|
|
178
219
|
},
|
|
179
220
|
save: () => {
|
|
180
221
|
const appState = excalidrawAPI.getAppState();
|
|
@@ -286,8 +327,7 @@ function ApiFactory() {
|
|
|
286
327
|
}
|
|
287
328
|
});
|
|
288
329
|
},
|
|
289
|
-
exportSelection: async () => {
|
|
290
|
-
if (!excalidrawAPI) return;
|
|
330
|
+
exportSelection: async (tryToSplit) => {
|
|
291
331
|
const appState = excalidrawAPI.getAppState();
|
|
292
332
|
const selectedElements = getSelectedElements(
|
|
293
333
|
excalidrawAPI.getSceneElements(),
|
|
@@ -297,13 +337,20 @@ function ApiFactory() {
|
|
|
297
337
|
throw "没有选中任何元素";
|
|
298
338
|
}
|
|
299
339
|
const files = excalidrawAPI.getFiles();
|
|
300
|
-
if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId) {
|
|
301
|
-
console.log(files[selectedElements[0].fileId].dataURL);
|
|
340
|
+
if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId && !selectedElements[0].crop) {
|
|
302
341
|
if (selectedElements[0].fileId.startsWith("http")) {
|
|
303
342
|
return selectedElements[0].fileId;
|
|
304
343
|
}
|
|
305
344
|
return files[selectedElements[0].fileId].dataURL;
|
|
306
345
|
}
|
|
346
|
+
if (tryToSplit && selectedElements.every((el) => el.type === "image")) {
|
|
347
|
+
return selectedElements.map((el) => {
|
|
348
|
+
if (el.fileId?.startsWith("http") && !el.crop) {
|
|
349
|
+
return selectedElements[0].fileId;
|
|
350
|
+
}
|
|
351
|
+
return files[el.fileId].dataURL;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
307
354
|
const canvas = await exportToCanvas({
|
|
308
355
|
elements: selectedElements,
|
|
309
356
|
appState: {
|
|
@@ -351,18 +398,18 @@ function ApiFactory() {
|
|
|
351
398
|
y: minY,
|
|
352
399
|
width: h,
|
|
353
400
|
height: h,
|
|
354
|
-
...customPos,
|
|
355
|
-
id: `loading-${Date.now()}`,
|
|
356
|
-
link: data.link ?? "https://www.google.com",
|
|
357
401
|
// 添加必需的基本样式属性
|
|
358
|
-
strokeColor: "#
|
|
402
|
+
strokeColor: "#ffffff00",
|
|
359
403
|
backgroundColor: "transparent",
|
|
360
404
|
fillStyle: "solid",
|
|
361
|
-
strokeWidth:
|
|
405
|
+
strokeWidth: 2,
|
|
362
406
|
strokeStyle: "solid",
|
|
363
407
|
roundness: null,
|
|
364
|
-
roughness:
|
|
408
|
+
roughness: 0,
|
|
365
409
|
opacity: 100,
|
|
410
|
+
...customPos,
|
|
411
|
+
id: `loading-${Date.now()}`,
|
|
412
|
+
link: data.link ?? "https://www.google.com",
|
|
366
413
|
// 添加必需的变换属性
|
|
367
414
|
angle: 0,
|
|
368
415
|
seed: Math.random(),
|
|
@@ -414,6 +461,21 @@ function ApiFactory() {
|
|
|
414
461
|
croppingElementId: selectedElements[0].id
|
|
415
462
|
}
|
|
416
463
|
});
|
|
464
|
+
},
|
|
465
|
+
deleteSelection: () => {
|
|
466
|
+
const appState = excalidrawAPI.getAppState();
|
|
467
|
+
const selectedIds = appState.selectedElementIds;
|
|
468
|
+
const currentElements = excalidrawAPI.getSceneElements();
|
|
469
|
+
const newElements = currentElements.filter((el) => {
|
|
470
|
+
return !selectedIds[el.id];
|
|
471
|
+
});
|
|
472
|
+
excalidrawAPI.updateScene({
|
|
473
|
+
elements: [...newElements],
|
|
474
|
+
appState: {
|
|
475
|
+
...appState,
|
|
476
|
+
selectedElementIds: {}
|
|
477
|
+
}
|
|
478
|
+
});
|
|
417
479
|
}
|
|
418
480
|
});
|
|
419
481
|
}, [excalidrawAPI, setApi]);
|
package/dist/index.umd.cjs
CHANGED
|
@@ -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.
|
|
5
|
+
const version = "0.0.14-beta.11";
|
|
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 = "
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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(),
|
|
@@ -172,9 +175,47 @@
|
|
|
172
175
|
},
|
|
173
176
|
insertImgs,
|
|
174
177
|
appendImgs: async (urls) => {
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
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
|
+
}
|
|
178
219
|
},
|
|
179
220
|
save: () => {
|
|
180
221
|
const appState = excalidrawAPI.getAppState();
|
|
@@ -286,8 +327,7 @@
|
|
|
286
327
|
}
|
|
287
328
|
});
|
|
288
329
|
},
|
|
289
|
-
exportSelection: async () => {
|
|
290
|
-
if (!excalidrawAPI) return;
|
|
330
|
+
exportSelection: async (tryToSplit) => {
|
|
291
331
|
const appState = excalidrawAPI.getAppState();
|
|
292
332
|
const selectedElements = getSelectedElements(
|
|
293
333
|
excalidrawAPI.getSceneElements(),
|
|
@@ -297,13 +337,20 @@
|
|
|
297
337
|
throw "没有选中任何元素";
|
|
298
338
|
}
|
|
299
339
|
const files = excalidrawAPI.getFiles();
|
|
300
|
-
if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId) {
|
|
301
|
-
console.log(files[selectedElements[0].fileId].dataURL);
|
|
340
|
+
if (selectedElements.length === 1 && selectedElements[0].type === "image" && selectedElements[0].fileId && !selectedElements[0].crop) {
|
|
302
341
|
if (selectedElements[0].fileId.startsWith("http")) {
|
|
303
342
|
return selectedElements[0].fileId;
|
|
304
343
|
}
|
|
305
344
|
return files[selectedElements[0].fileId].dataURL;
|
|
306
345
|
}
|
|
346
|
+
if (tryToSplit && selectedElements.every((el) => el.type === "image")) {
|
|
347
|
+
return selectedElements.map((el) => {
|
|
348
|
+
if (el.fileId?.startsWith("http") && !el.crop) {
|
|
349
|
+
return selectedElements[0].fileId;
|
|
350
|
+
}
|
|
351
|
+
return files[el.fileId].dataURL;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
307
354
|
const canvas = await excalidraw.exportToCanvas({
|
|
308
355
|
elements: selectedElements,
|
|
309
356
|
appState: {
|
|
@@ -351,18 +398,18 @@
|
|
|
351
398
|
y: minY,
|
|
352
399
|
width: h,
|
|
353
400
|
height: h,
|
|
354
|
-
...customPos,
|
|
355
|
-
id: `loading-${Date.now()}`,
|
|
356
|
-
link: data.link ?? "https://www.google.com",
|
|
357
401
|
// 添加必需的基本样式属性
|
|
358
|
-
strokeColor: "#
|
|
402
|
+
strokeColor: "#ffffff00",
|
|
359
403
|
backgroundColor: "transparent",
|
|
360
404
|
fillStyle: "solid",
|
|
361
|
-
strokeWidth:
|
|
405
|
+
strokeWidth: 2,
|
|
362
406
|
strokeStyle: "solid",
|
|
363
407
|
roundness: null,
|
|
364
|
-
roughness:
|
|
408
|
+
roughness: 0,
|
|
365
409
|
opacity: 100,
|
|
410
|
+
...customPos,
|
|
411
|
+
id: `loading-${Date.now()}`,
|
|
412
|
+
link: data.link ?? "https://www.google.com",
|
|
366
413
|
// 添加必需的变换属性
|
|
367
414
|
angle: 0,
|
|
368
415
|
seed: Math.random(),
|
|
@@ -414,6 +461,21 @@
|
|
|
414
461
|
croppingElementId: selectedElements[0].id
|
|
415
462
|
}
|
|
416
463
|
});
|
|
464
|
+
},
|
|
465
|
+
deleteSelection: () => {
|
|
466
|
+
const appState = excalidrawAPI.getAppState();
|
|
467
|
+
const selectedIds = appState.selectedElementIds;
|
|
468
|
+
const currentElements = excalidrawAPI.getSceneElements();
|
|
469
|
+
const newElements = currentElements.filter((el) => {
|
|
470
|
+
return !selectedIds[el.id];
|
|
471
|
+
});
|
|
472
|
+
excalidrawAPI.updateScene({
|
|
473
|
+
elements: [...newElements],
|
|
474
|
+
appState: {
|
|
475
|
+
...appState,
|
|
476
|
+
selectedElementIds: {}
|
|
477
|
+
}
|
|
478
|
+
});
|
|
417
479
|
}
|
|
418
480
|
});
|
|
419
481
|
}, [excalidrawAPI, setApi]);
|