canvas-drawing-editor 2.0.10 → 2.0.12
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/LICENSE +22 -0
- package/README.md +4 -10
- package/dist/canvas-drawing-editor.es.js +39 -25
- package/dist/canvas-drawing-editor.es.js.map +1 -1
- package/dist/canvas-drawing-editor.umd.js +12 -4
- package/dist/canvas-drawing-editor.umd.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/package.json +3 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 typsusan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ npm install canvas-drawing-editor
|
|
|
39
39
|
</style>
|
|
40
40
|
</head>
|
|
41
41
|
<body>
|
|
42
|
-
<canvas-drawing-editor title="My Canvas"
|
|
42
|
+
<canvas-drawing-editor title="My Canvas"></canvas-drawing-editor>
|
|
43
43
|
|
|
44
44
|
<script src="https://unpkg.com/canvas-drawing-editor/dist/canvas-drawing-editor.umd.js"></script>
|
|
45
45
|
</body>
|
|
@@ -52,7 +52,6 @@ npm install canvas-drawing-editor
|
|
|
52
52
|
<template>
|
|
53
53
|
<canvas-drawing-editor
|
|
54
54
|
title="Vue Canvas"
|
|
55
|
-
show-minimap="true"
|
|
56
55
|
style="width: 100%; height: 600px;"
|
|
57
56
|
></canvas-drawing-editor>
|
|
58
57
|
</template>
|
|
@@ -90,7 +89,6 @@ import 'canvas-drawing-editor'
|
|
|
90
89
|
<template>
|
|
91
90
|
<canvas-drawing-editor
|
|
92
91
|
title="Vue2 Canvas"
|
|
93
|
-
show-minimap="true"
|
|
94
92
|
style="width: 100%; height: 600px;"
|
|
95
93
|
></canvas-drawing-editor>
|
|
96
94
|
</template>
|
|
@@ -105,7 +103,6 @@ function App() {
|
|
|
105
103
|
return (
|
|
106
104
|
<canvas-drawing-editor
|
|
107
105
|
title="React Canvas"
|
|
108
|
-
show-minimap="true"
|
|
109
106
|
style={{ width: '100%', height: '600px' }}
|
|
110
107
|
/>
|
|
111
108
|
);
|
|
@@ -127,7 +124,7 @@ function App() {
|
|
|
127
124
|
| `show-export` | boolean | true | Show JSON save |
|
|
128
125
|
| `show-import` | boolean | true | Show JSON load |
|
|
129
126
|
| `show-color` | boolean | true | Show color picker |
|
|
130
|
-
| `show-
|
|
127
|
+
| `show-clear` | boolean | true | Show clear canvas button |
|
|
131
128
|
| `initial-data` | string | - | Initial JSON data to render (see format below) |
|
|
132
129
|
|
|
133
130
|
### 📊 Initial Data
|
|
@@ -270,7 +267,7 @@ npm install canvas-drawing-editor
|
|
|
270
267
|
</style>
|
|
271
268
|
</head>
|
|
272
269
|
<body>
|
|
273
|
-
<canvas-drawing-editor title="我的画板"
|
|
270
|
+
<canvas-drawing-editor title="我的画板"></canvas-drawing-editor>
|
|
274
271
|
|
|
275
272
|
<script src="https://unpkg.com/canvas-drawing-editor/dist/canvas-drawing-editor.umd.js"></script>
|
|
276
273
|
</body>
|
|
@@ -283,7 +280,6 @@ npm install canvas-drawing-editor
|
|
|
283
280
|
<template>
|
|
284
281
|
<canvas-drawing-editor
|
|
285
282
|
title="Vue 画板"
|
|
286
|
-
show-minimap="true"
|
|
287
283
|
style="width: 100%; height: 600px;"
|
|
288
284
|
></canvas-drawing-editor>
|
|
289
285
|
</template>
|
|
@@ -321,7 +317,6 @@ import 'canvas-drawing-editor'
|
|
|
321
317
|
<template>
|
|
322
318
|
<canvas-drawing-editor
|
|
323
319
|
title="Vue2 画板"
|
|
324
|
-
show-minimap="true"
|
|
325
320
|
style="width: 100%; height: 600px;"
|
|
326
321
|
></canvas-drawing-editor>
|
|
327
322
|
</template>
|
|
@@ -336,7 +331,6 @@ function App() {
|
|
|
336
331
|
return (
|
|
337
332
|
<canvas-drawing-editor
|
|
338
333
|
title="React 画板"
|
|
339
|
-
show-minimap="true"
|
|
340
334
|
style={{ width: '100%', height: '600px' }}
|
|
341
335
|
/>
|
|
342
336
|
);
|
|
@@ -358,7 +352,7 @@ function App() {
|
|
|
358
352
|
| `show-export` | boolean | true | 显示 JSON 保存 |
|
|
359
353
|
| `show-import` | boolean | true | 显示 JSON 加载 |
|
|
360
354
|
| `show-color` | boolean | true | 显示颜色选择器 |
|
|
361
|
-
| `show-
|
|
355
|
+
| `show-clear` | boolean | true | 显示清空画布按钮 |
|
|
362
356
|
| `initial-data` | string | - | 初始化 JSON 数据(格式见下方) |
|
|
363
357
|
|
|
364
358
|
### 📊 初始化数据
|
|
@@ -9,7 +9,8 @@ const f = {
|
|
|
9
9
|
showDownload: !0,
|
|
10
10
|
showExport: !0,
|
|
11
11
|
showImport: !0,
|
|
12
|
-
showColor: !0
|
|
12
|
+
showColor: !0,
|
|
13
|
+
showClear: !0
|
|
13
14
|
};
|
|
14
15
|
class g extends HTMLElement {
|
|
15
16
|
constructor() {
|
|
@@ -63,7 +64,8 @@ class g extends HTMLElement {
|
|
|
63
64
|
showDownload: this.getAttribute("show-download") !== "false",
|
|
64
65
|
showExport: this.getAttribute("show-export") !== "false",
|
|
65
66
|
showImport: this.getAttribute("show-import") !== "false",
|
|
66
|
-
showColor: this.getAttribute("show-color") !== "false"
|
|
67
|
+
showColor: this.getAttribute("show-color") !== "false",
|
|
68
|
+
showClear: this.getAttribute("show-clear") !== "false"
|
|
67
69
|
};
|
|
68
70
|
}
|
|
69
71
|
// 生成唯一 ID
|
|
@@ -396,28 +398,28 @@ class g extends HTMLElement {
|
|
|
396
398
|
if (!this.isDragging || !this.dragStart) return;
|
|
397
399
|
const { x: s, y: i } = this.getMousePos(t);
|
|
398
400
|
if (this.isResizing && this.selectedId && this.resizeHandle && this.resizeStartBounds && this.resizeOriginalObject) {
|
|
399
|
-
const e = this.objects.find((
|
|
401
|
+
const e = this.objects.find((r) => r.id === this.selectedId);
|
|
400
402
|
if (!e) return;
|
|
401
403
|
const n = s - this.dragStart.x, a = i - this.dragStart.y;
|
|
402
|
-
let o = this.resizeStartBounds.x, l = this.resizeStartBounds.y, h = this.resizeStartBounds.width,
|
|
403
|
-
switch (this.resizeHandle.includes("e") && (h = this.resizeStartBounds.width + n), this.resizeHandle.includes("w") && (o = this.resizeStartBounds.x + n, h = this.resizeStartBounds.width - n), this.resizeHandle.includes("s") && (
|
|
404
|
+
let o = this.resizeStartBounds.x, l = this.resizeStartBounds.y, h = this.resizeStartBounds.width, c = this.resizeStartBounds.height;
|
|
405
|
+
switch (this.resizeHandle.includes("e") && (h = this.resizeStartBounds.width + n), this.resizeHandle.includes("w") && (o = this.resizeStartBounds.x + n, h = this.resizeStartBounds.width - n), this.resizeHandle.includes("s") && (c = this.resizeStartBounds.height + a), this.resizeHandle.includes("n") && (l = this.resizeStartBounds.y + a, c = this.resizeStartBounds.height - a), h = Math.max(10, h), c = Math.max(10, c), e.type) {
|
|
404
406
|
case "RECTANGLE":
|
|
405
407
|
case "IMAGE":
|
|
406
|
-
e.x = o, e.y = l, e.width = h, e.height =
|
|
408
|
+
e.x = o, e.y = l, e.width = h, e.height = c;
|
|
407
409
|
break;
|
|
408
410
|
case "CIRCLE": {
|
|
409
|
-
const
|
|
410
|
-
e.x = o +
|
|
411
|
+
const r = Math.max(h, c) / 2;
|
|
412
|
+
e.x = o + r, e.y = l + r, e.radius = r;
|
|
411
413
|
break;
|
|
412
414
|
}
|
|
413
415
|
case "TEXT": {
|
|
414
|
-
const
|
|
415
|
-
e.x = o, e.y = l +
|
|
416
|
+
const r = this.resizeOriginalObject, d = h / this.resizeStartBounds.width;
|
|
417
|
+
e.x = o, e.y = l + c, e.fontSize = Math.max(8, Math.round(r.fontSize * d));
|
|
416
418
|
break;
|
|
417
419
|
}
|
|
418
420
|
case "PATH": {
|
|
419
|
-
const
|
|
420
|
-
e.points =
|
|
421
|
+
const r = this.resizeOriginalObject, d = h / this.resizeStartBounds.width, u = c / this.resizeStartBounds.height;
|
|
422
|
+
e.points = r.points.map((p) => ({
|
|
421
423
|
x: o + (p.x - this.resizeStartBounds.x) * d,
|
|
422
424
|
y: l + (p.y - this.resizeStartBounds.y) * u
|
|
423
425
|
}));
|
|
@@ -536,18 +538,18 @@ class g extends HTMLElement {
|
|
|
536
538
|
o.onload = () => {
|
|
537
539
|
this.saveHistory();
|
|
538
540
|
const h = 300;
|
|
539
|
-
let
|
|
540
|
-
if (
|
|
541
|
-
const u = Math.min(h /
|
|
542
|
-
|
|
541
|
+
let c = o.width, r = o.height;
|
|
542
|
+
if (c > h || r > h) {
|
|
543
|
+
const u = Math.min(h / c, h / r);
|
|
544
|
+
c *= u, r *= u;
|
|
543
545
|
}
|
|
544
546
|
const d = {
|
|
545
547
|
id: this.generateId(),
|
|
546
548
|
type: "IMAGE",
|
|
547
549
|
x: 100,
|
|
548
550
|
y: 100,
|
|
549
|
-
width:
|
|
550
|
-
height:
|
|
551
|
+
width: c,
|
|
552
|
+
height: r,
|
|
551
553
|
color: "#000000",
|
|
552
554
|
lineWidth: 1,
|
|
553
555
|
dataUrl: a,
|
|
@@ -599,6 +601,10 @@ class g extends HTMLElement {
|
|
|
599
601
|
const i = t.toDataURL("image/png"), e = document.createElement("a");
|
|
600
602
|
e.href = i, e.download = "canvas-export.png", e.click();
|
|
601
603
|
}
|
|
604
|
+
// 清空画布
|
|
605
|
+
clearCanvas() {
|
|
606
|
+
this.objects.length !== 0 && (this.saveHistory(), this.objects = [], this.selectedId = null, this.render(), this.dispatchChangeEvent(), this.updateUI());
|
|
607
|
+
}
|
|
602
608
|
// 更新 UI
|
|
603
609
|
updateUI() {
|
|
604
610
|
const t = this.shadow.querySelector(".selection-info");
|
|
@@ -691,7 +697,7 @@ class g extends HTMLElement {
|
|
|
691
697
|
</button>
|
|
692
698
|
</div>
|
|
693
699
|
` : ""}
|
|
694
|
-
${this.config.showExport || this.config.showImport || this.config.showDownload ? `
|
|
700
|
+
${this.config.showExport || this.config.showImport || this.config.showDownload || this.config.showClear ? `
|
|
695
701
|
<div class="file-controls">
|
|
696
702
|
${this.config.showExport ? `
|
|
697
703
|
<button class="file-btn save-json-btn" title="保存项目 (JSON)">
|
|
@@ -716,6 +722,14 @@ class g extends HTMLElement {
|
|
|
716
722
|
</svg>
|
|
717
723
|
</button>
|
|
718
724
|
` : ""}
|
|
725
|
+
${this.config.showClear ? `
|
|
726
|
+
<button class="file-btn clear-canvas-btn" title="清空画布">
|
|
727
|
+
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
728
|
+
<path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/>
|
|
729
|
+
<line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/>
|
|
730
|
+
</svg>
|
|
731
|
+
</button>
|
|
732
|
+
` : ""}
|
|
719
733
|
</div>
|
|
720
734
|
` : ""}
|
|
721
735
|
</div>
|
|
@@ -744,12 +758,12 @@ class g extends HTMLElement {
|
|
|
744
758
|
// 绑定事件
|
|
745
759
|
bindEvents() {
|
|
746
760
|
this.canvas.addEventListener("mousedown", (r) => this.handleCanvasPointerDown(r)), this.canvas.addEventListener("mousemove", (r) => this.handleCanvasPointerMove(r)), this.canvas.addEventListener("mouseup", () => this.handleCanvasPointerUp()), this.canvas.addEventListener("mouseleave", () => this.handleCanvasPointerUp()), this.canvas.addEventListener("dblclick", (r) => this.handleCanvasDoubleClick(r)), this.canvas.addEventListener("touchstart", (r) => this.handleCanvasPointerDown(r)), this.canvas.addEventListener("touchmove", (r) => this.handleCanvasPointerMove(r)), this.canvas.addEventListener("touchend", () => this.handleCanvasPointerUp()), this.canvas.addEventListener("wheel", this.boundHandleWheel, { passive: !1 }), this.shadow.querySelectorAll(".tool-btn[data-tool]").forEach((r) => {
|
|
747
|
-
r.addEventListener("mousedown", (
|
|
748
|
-
|
|
761
|
+
r.addEventListener("mousedown", (d) => {
|
|
762
|
+
d.preventDefault();
|
|
749
763
|
}), r.addEventListener("click", () => {
|
|
750
764
|
this.isTextInputVisible && this.submitText();
|
|
751
|
-
const
|
|
752
|
-
this.setTool(
|
|
765
|
+
const d = r.getAttribute("data-tool");
|
|
766
|
+
this.setTool(d);
|
|
753
767
|
});
|
|
754
768
|
});
|
|
755
769
|
const t = this.shadow.querySelector(".undo-btn");
|
|
@@ -762,8 +776,8 @@ class g extends HTMLElement {
|
|
|
762
776
|
i && i.addEventListener("change", (r) => this.handleImageUpload(r));
|
|
763
777
|
const e = this.shadow.querySelector(".zoom-in-btn"), n = this.shadow.querySelector(".zoom-out-btn"), a = this.shadow.querySelector(".zoom-text");
|
|
764
778
|
e && e.addEventListener("click", () => this.zoomIn()), n && n.addEventListener("click", () => this.zoomOut()), a && a.addEventListener("click", () => this.resetZoom());
|
|
765
|
-
const o = this.shadow.querySelector(".save-json-btn"), l = this.shadow.querySelector(".load-json-input"), h = this.shadow.querySelector(".export-png-btn");
|
|
766
|
-
o && o.addEventListener("click", () => this.saveJson()), l && l.addEventListener("change", (r) => this.loadJson(r)), h && h.addEventListener("click", () => this.exportPng()), this.textInput && (this.textInput.addEventListener("keydown", (r) => {
|
|
779
|
+
const o = this.shadow.querySelector(".save-json-btn"), l = this.shadow.querySelector(".load-json-input"), h = this.shadow.querySelector(".export-png-btn"), c = this.shadow.querySelector(".clear-canvas-btn");
|
|
780
|
+
o && o.addEventListener("click", () => this.saveJson()), l && l.addEventListener("change", (r) => this.loadJson(r)), h && h.addEventListener("click", () => this.exportPng()), c && c.addEventListener("click", () => this.clearCanvas()), this.textInput && (this.textInput.addEventListener("keydown", (r) => {
|
|
767
781
|
r.key === "Enter" ? (r.preventDefault(), this.submitText()) : r.key === "Escape" && this.hideTextInput();
|
|
768
782
|
}), this.textInput.addEventListener("blur", () => {
|
|
769
783
|
this.isTextInputVisible && this.submitText();
|