canvas-drawing-editor 2.0.2 → 2.0.3

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/README.md CHANGED
@@ -128,16 +128,93 @@ function App() {
128
128
  | `show-import` | boolean | true | Show JSON load |
129
129
  | `show-color` | boolean | true | Show color picker |
130
130
  | `show-minimap` | boolean | true | Show navigation minimap |
131
+ | `initial-data` | string | - | Initial JSON data to render (see format below) |
132
+
133
+ ### 📊 Initial Data
134
+
135
+ You can pass JSON data to initialize the canvas content:
136
+
137
+ ```html
138
+ <canvas-drawing-editor
139
+ initial-data='{"objects":[{"id":"abc123","type":"RECTANGLE","x":100,"y":100,"width":200,"height":150,"color":"#3b82f6","lineWidth":2}]}'
140
+ ></canvas-drawing-editor>
141
+ ```
131
142
 
132
143
  ### 📡 Events
133
144
 
145
+ #### `editor-change` Event
146
+
147
+ Fires when canvas content changes. The `e.detail.objects` array contains all drawing objects.
148
+
134
149
  ```javascript
135
- // Listen for canvas changes
136
150
  document.addEventListener('editor-change', (e) => {
137
151
  console.log('Objects:', e.detail.objects);
152
+ // Save to server or localStorage
153
+ localStorage.setItem('canvas-data', JSON.stringify({ objects: e.detail.objects }));
154
+ });
155
+ ```
156
+
157
+ #### Object Types & Properties
158
+
159
+ Each object in `e.detail.objects` has the following base properties:
160
+
161
+ | Property | Type | Description |
162
+ |----------|------|-------------|
163
+ | `id` | string | Unique identifier |
164
+ | `type` | string | Object type: `RECTANGLE`, `CIRCLE`, `PATH`, `TEXT`, `IMAGE` |
165
+ | `x` | number | X coordinate |
166
+ | `y` | number | Y coordinate |
167
+ | `color` | string | Stroke/fill color (hex format, e.g., `#3b82f6`) |
168
+ | `lineWidth` | number | Line width in pixels |
169
+
170
+ **Rectangle** (`type: "RECTANGLE"`):
171
+ | Property | Type | Description |
172
+ |----------|------|-------------|
173
+ | `width` | number | Rectangle width |
174
+ | `height` | number | Rectangle height |
175
+
176
+ **Circle** (`type: "CIRCLE"`):
177
+ | Property | Type | Description |
178
+ |----------|------|-------------|
179
+ | `radius` | number | Circle radius |
180
+
181
+ **Path/Pencil** (`type: "PATH"`):
182
+ | Property | Type | Description |
183
+ |----------|------|-------------|
184
+ | `points` | Array<{x, y}> | Array of point coordinates |
185
+
186
+ **Text** (`type: "TEXT"`):
187
+ | Property | Type | Description |
188
+ |----------|------|-------------|
189
+ | `text` | string | Text content |
190
+ | `fontSize` | number | Font size in pixels |
191
+
192
+ **Image** (`type: "IMAGE"`):
193
+ | Property | Type | Description |
194
+ |----------|------|-------------|
195
+ | `width` | number | Image width |
196
+ | `height` | number | Image height |
197
+ | `dataUrl` | string | Base64 encoded image data |
198
+
199
+ #### Example: Saving and Loading
200
+
201
+ ```javascript
202
+ // Save canvas content
203
+ document.addEventListener('editor-change', (e) => {
204
+ const data = JSON.stringify({ objects: e.detail.objects });
205
+ localStorage.setItem('my-canvas', data);
138
206
  });
139
207
 
140
- // Listen for editor close
208
+ // Load canvas content
209
+ const savedData = localStorage.getItem('my-canvas');
210
+ if (savedData) {
211
+ document.querySelector('canvas-drawing-editor').setAttribute('initial-data', savedData);
212
+ }
213
+ ```
214
+
215
+ #### `editor-close` Event
216
+
217
+ ```javascript
141
218
  document.addEventListener('editor-close', () => {
142
219
  console.log('Editor closed');
143
220
  });
@@ -282,16 +359,93 @@ function App() {
282
359
  | `show-import` | boolean | true | 显示 JSON 加载 |
283
360
  | `show-color` | boolean | true | 显示颜色选择器 |
284
361
  | `show-minimap` | boolean | true | 显示导航小地图 |
362
+ | `initial-data` | string | - | 初始化 JSON 数据(格式见下方) |
363
+
364
+ ### 📊 初始化数据
365
+
366
+ 可以通过 `initial-data` 属性传入 JSON 数据来初始化画布内容:
367
+
368
+ ```html
369
+ <canvas-drawing-editor
370
+ initial-data='{"objects":[{"id":"abc123","type":"RECTANGLE","x":100,"y":100,"width":200,"height":150,"color":"#3b82f6","lineWidth":2}]}'
371
+ ></canvas-drawing-editor>
372
+ ```
285
373
 
286
374
  ### 📡 事件监听
287
375
 
376
+ #### `editor-change` 事件
377
+
378
+ 当画布内容变化时触发。`e.detail.objects` 数组包含所有绑图对象。
379
+
288
380
  ```javascript
289
- // 监听画布内容变化
290
381
  document.addEventListener('editor-change', (e) => {
291
382
  console.log('对象列表:', e.detail.objects);
383
+ // 保存到服务器或 localStorage
384
+ localStorage.setItem('canvas-data', JSON.stringify({ objects: e.detail.objects }));
385
+ });
386
+ ```
387
+
388
+ #### 对象类型和属性说明
389
+
390
+ `e.detail.objects` 中每个对象都有以下基础属性:
391
+
392
+ | 属性 | 类型 | 说明 |
393
+ |------|------|------|
394
+ | `id` | string | 唯一标识符 |
395
+ | `type` | string | 对象类型:`RECTANGLE`、`CIRCLE`、`PATH`、`TEXT`、`IMAGE` |
396
+ | `x` | number | X 坐标 |
397
+ | `y` | number | Y 坐标 |
398
+ | `color` | string | 描边/填充颜色(十六进制格式,如 `#3b82f6`) |
399
+ | `lineWidth` | number | 线条宽度(像素) |
400
+
401
+ **矩形** (`type: "RECTANGLE"`):
402
+ | 属性 | 类型 | 说明 |
403
+ |------|------|------|
404
+ | `width` | number | 矩形宽度 |
405
+ | `height` | number | 矩形高度 |
406
+
407
+ **圆形** (`type: "CIRCLE"`):
408
+ | 属性 | 类型 | 说明 |
409
+ |------|------|------|
410
+ | `radius` | number | 圆形半径 |
411
+
412
+ **画笔路径** (`type: "PATH"`):
413
+ | 属性 | 类型 | 说明 |
414
+ |------|------|------|
415
+ | `points` | Array<{x, y}> | 点坐标数组 |
416
+
417
+ **文本** (`type: "TEXT"`):
418
+ | 属性 | 类型 | 说明 |
419
+ |------|------|------|
420
+ | `text` | string | 文本内容 |
421
+ | `fontSize` | number | 字体大小(像素) |
422
+
423
+ **图片** (`type: "IMAGE"`):
424
+ | 属性 | 类型 | 说明 |
425
+ |------|------|------|
426
+ | `width` | number | 图片宽度 |
427
+ | `height` | number | 图片高度 |
428
+ | `dataUrl` | string | Base64 编码的图片数据 |
429
+
430
+ #### 示例:保存和加载画布
431
+
432
+ ```javascript
433
+ // 保存画布内容
434
+ document.addEventListener('editor-change', (e) => {
435
+ const data = JSON.stringify({ objects: e.detail.objects });
436
+ localStorage.setItem('my-canvas', data);
292
437
  });
293
438
 
294
- // 监听编辑器关闭
439
+ // 加载画布内容
440
+ const savedData = localStorage.getItem('my-canvas');
441
+ if (savedData) {
442
+ document.querySelector('canvas-drawing-editor').setAttribute('initial-data', savedData);
443
+ }
444
+ ```
445
+
446
+ #### `editor-close` 事件
447
+
448
+ ```javascript
295
449
  document.addEventListener('editor-close', () => {
296
450
  console.log('编辑器已关闭');
297
451
  });
@@ -12,7 +12,7 @@ const f = {
12
12
  showColor: !0,
13
13
  showMinimap: !0
14
14
  };
15
- class v extends HTMLElement {
15
+ class b extends HTMLElement {
16
16
  constructor() {
17
17
  super(), this.config = { ...f }, this.objects = [], this.selectedId = null, this.tool = "SELECT", this.color = "#000000", this.lineWidth = 3, this.isDragging = !1, this.dragStart = null, this.currentObject = null, this.dragOffset = { x: 0, y: 0 }, this.isTextInputVisible = !1, this.textInputPos = { x: 0, y: 0 }, this.textInputScreenPos = { x: 0, y: 0 }, this.editingTextId = null, this.isResizing = !1, this.resizeHandle = null, this.resizeStartBounds = null, this.resizeOriginalObject = null, this.history = [], this.clipboard = null, this.scale = 1, this.panOffset = { x: 0, y: 0 }, this.isPanning = !1, this.panStart = { x: 0, y: 0 }, this.shadow = this.attachShadow({ mode: "open" }), this.boundHandleResize = this.handleResize.bind(this), this.boundHandleKeyDown = this.handleKeyDown.bind(this), this.boundHandleWheel = this.handleWheel.bind(this);
18
18
  }
@@ -30,12 +30,13 @@ class v extends HTMLElement {
30
30
  "show-export",
31
31
  "show-import",
32
32
  "show-color",
33
- "show-minimap"
33
+ "show-minimap",
34
+ "initial-data"
34
35
  ];
35
36
  }
36
37
  // 生命周期:连接到 DOM
37
38
  connectedCallback() {
38
- this.parseAttributes(), this.render(), this.setupEventListeners(), this.initCanvas();
39
+ this.parseAttributes(), this.render(), this.setupEventListeners(), this.initCanvas(), this.loadInitialData();
39
40
  }
40
41
  // 生命周期:从 DOM 断开
41
42
  disconnectedCallback() {
@@ -43,7 +44,13 @@ class v extends HTMLElement {
43
44
  }
44
45
  // 生命周期:属性变化
45
46
  attributeChangedCallback(t, i, s) {
46
- i !== s && (this.parseAttributes(), this.container && this.updateUI());
47
+ if (i !== s) {
48
+ if (t === "initial-data" && s && this.canvas) {
49
+ this.loadInitialData();
50
+ return;
51
+ }
52
+ this.parseAttributes(), this.container && this.updateUI();
53
+ }
47
54
  }
48
55
  // 解析 HTML 属性
49
56
  parseAttributes() {
@@ -66,6 +73,24 @@ class v extends HTMLElement {
66
73
  generateId() {
67
74
  return Math.random().toString(36).substr(2, 9);
68
75
  }
76
+ // 加载初始数据
77
+ loadInitialData() {
78
+ const t = this.getAttribute("initial-data");
79
+ if (t)
80
+ try {
81
+ const i = JSON.parse(t);
82
+ i.objects && Array.isArray(i.objects) && (this.objects = i.objects, this.selectedId = null, this.objects.forEach((s) => {
83
+ if (s.type === "IMAGE" && s.dataUrl) {
84
+ const e = new Image();
85
+ e.onload = () => {
86
+ s.imageElement = e, this.renderCanvas(), this.renderMinimap();
87
+ }, e.src = s.dataUrl;
88
+ }
89
+ }), this.renderCanvas(), this.renderMinimap(), this.updateUI());
90
+ } catch (i) {
91
+ console.error("Failed to parse initial-data:", i);
92
+ }
93
+ }
69
94
  // 设置事件监听
70
95
  setupEventListeners() {
71
96
  window.addEventListener("resize", this.boundHandleResize), window.addEventListener("keydown", this.boundHandleKeyDown);
@@ -94,8 +119,8 @@ class v extends HTMLElement {
94
119
  s = t.clientX, e = t.clientY;
95
120
  else
96
121
  return { x: 0, y: 0 };
97
- const n = (s - i.left - this.panOffset.x) / this.scale, a = (e - i.top - this.panOffset.y) / this.scale;
98
- return { x: n, y: a };
122
+ const n = (s - i.left - this.panOffset.x) / this.scale, o = (e - i.top - this.panOffset.y) / this.scale;
123
+ return { x: n, y: o };
99
124
  }
100
125
  // 获取屏幕坐标(不考虑缩放和平移)
101
126
  getScreenPos(t) {
@@ -128,23 +153,23 @@ class v extends HTMLElement {
128
153
  case "PATH": {
129
154
  const i = t;
130
155
  if (i.points.length === 0) return { x: 0, y: 0, width: 0, height: 0 };
131
- const s = Math.min(...i.points.map((o) => o.x)), e = Math.max(...i.points.map((o) => o.x)), n = Math.min(...i.points.map((o) => o.y)), a = Math.max(...i.points.map((o) => o.y));
132
- return { x: s, y: n, width: e - s, height: a - n };
156
+ const s = Math.min(...i.points.map((a) => a.x)), e = Math.max(...i.points.map((a) => a.x)), n = Math.min(...i.points.map((a) => a.y)), o = Math.max(...i.points.map((a) => a.y));
157
+ return { x: s, y: n, width: e - s, height: o - n };
133
158
  }
134
159
  }
135
160
  return { x: 0, y: 0, width: 0, height: 0 };
136
161
  }
137
162
  // 检查调整大小手柄
138
163
  getResizeHandleAtPoint(t, i, s) {
139
- const e = this.getObjectBounds(t), n = 8, a = [
164
+ const e = this.getObjectBounds(t), n = 8, o = [
140
165
  { name: "nw", x: e.x, y: e.y },
141
166
  { name: "ne", x: e.x + e.width, y: e.y },
142
167
  { name: "sw", x: e.x, y: e.y + e.height },
143
168
  { name: "se", x: e.x + e.width, y: e.y + e.height }
144
169
  ];
145
- for (const o of a)
146
- if (Math.abs(i - o.x) <= n && Math.abs(s - o.y) <= n)
147
- return o.name;
170
+ for (const a of o)
171
+ if (Math.abs(i - a.x) <= n && Math.abs(s - a.y) <= n)
172
+ return a.name;
148
173
  return null;
149
174
  }
150
175
  // 碰撞检测
@@ -169,8 +194,8 @@ class v extends HTMLElement {
169
194
  case "PATH": {
170
195
  const e = t;
171
196
  if (e.points.length === 0) return !1;
172
- const n = Math.min(...e.points.map((r) => r.x)), a = Math.max(...e.points.map((r) => r.x)), o = Math.min(...e.points.map((r) => r.y)), l = Math.max(...e.points.map((r) => r.y));
173
- return i >= n && i <= a && s >= o && s <= l;
197
+ const n = Math.min(...e.points.map((r) => r.x)), o = Math.max(...e.points.map((r) => r.x)), a = Math.min(...e.points.map((r) => r.y)), l = Math.max(...e.points.map((r) => r.y));
198
+ return i >= n && i <= o && s >= a && s <= l;
174
199
  }
175
200
  }
176
201
  return !1;
@@ -266,13 +291,13 @@ class v extends HTMLElement {
266
291
  // 滚轮缩放
267
292
  handleWheel(t) {
268
293
  t.preventDefault();
269
- const i = this.canvas.getBoundingClientRect(), s = t.clientX - i.left, e = t.clientY - i.top, n = t.deltaY > 0 ? 0.9 : 1.1, a = this.scale * n;
270
- this.zoomAtPoint(a, s, e);
294
+ const i = this.canvas.getBoundingClientRect(), s = t.clientX - i.left, e = t.clientY - i.top, n = t.deltaY > 0 ? 0.9 : 1.1, o = this.scale * n;
295
+ this.zoomAtPoint(o, s, e);
271
296
  }
272
297
  // 以指定点为中心缩放
273
298
  zoomAtPoint(t, i, s) {
274
- const e = Math.min(Math.max(t, 0.2), 5), n = (i - this.panOffset.x) / this.scale, a = (s - this.panOffset.y) / this.scale, o = i - n * e, l = s - a * e;
275
- this.scale = e, this.panOffset = { x: o, y: l }, this.renderCanvas(), this.renderMinimap(), this.updateZoomDisplay();
299
+ const e = Math.min(Math.max(t, 0.2), 5), n = (i - this.panOffset.x) / this.scale, o = (s - this.panOffset.y) / this.scale, a = i - n * e, l = s - o * e;
300
+ this.scale = e, this.panOffset = { x: a, y: l }, this.renderCanvas(), this.renderMinimap(), this.updateZoomDisplay();
276
301
  }
277
302
  // 放大
278
303
  zoomIn() {
@@ -344,16 +369,16 @@ class v extends HTMLElement {
344
369
  const { x: i, y: s } = this.getMousePos(t), e = this.getScreenPos(t);
345
370
  if (this.dragStart = { x: i, y: s }, this.isDragging = !0, this.isTextInputVisible && this.tool !== "TEXT" && this.submitText(), this.tool === "SELECT") {
346
371
  if (this.selectedId) {
347
- const a = this.objects.find((o) => o.id === this.selectedId);
348
- if (a) {
349
- const o = this.getResizeHandleAtPoint(a, i, s);
350
- if (o) {
351
- this.saveHistory(), this.isResizing = !0, this.resizeHandle = o, this.resizeStartBounds = this.getObjectBounds(a), this.resizeOriginalObject = JSON.parse(JSON.stringify(a));
372
+ const o = this.objects.find((a) => a.id === this.selectedId);
373
+ if (o) {
374
+ const a = this.getResizeHandleAtPoint(o, i, s);
375
+ if (a) {
376
+ this.saveHistory(), this.isResizing = !0, this.resizeHandle = a, this.resizeStartBounds = this.getObjectBounds(o), this.resizeOriginalObject = JSON.parse(JSON.stringify(o));
352
377
  return;
353
378
  }
354
379
  }
355
380
  }
356
- const n = [...this.objects].reverse().find((a) => this.isHit(a, i, s));
381
+ const n = [...this.objects].reverse().find((o) => this.isHit(o, i, s));
357
382
  n ? (this.selectedId = n.id, this.dragOffset = { x: i - n.x, y: s - n.y }, this.saveHistory(), this.updateUI()) : (this.selectedId = null, this.isPanning = !0, this.panStart = e, this.updateUI());
358
383
  } else if (this.tool === "TEXT")
359
384
  this.textInputPos = { x: i, y: s }, this.showTextInput(e.x, e.y), this.isDragging = !1;
@@ -367,8 +392,8 @@ class v extends HTMLElement {
367
392
  // 画布鼠标移动
368
393
  handleCanvasPointerMove(t) {
369
394
  if (this.isPanning) {
370
- const e = this.getScreenPos(t), n = e.x - this.panStart.x, a = e.y - this.panStart.y;
371
- this.panOffset = { x: this.panOffset.x + n, y: this.panOffset.y + a }, this.panStart = e, this.renderCanvas(), this.renderMinimap();
395
+ const e = this.getScreenPos(t), n = e.x - this.panStart.x, o = e.y - this.panStart.y;
396
+ this.panOffset = { x: this.panOffset.x + n, y: this.panOffset.y + o }, this.panStart = e, this.renderCanvas(), this.renderMinimap();
372
397
  return;
373
398
  }
374
399
  if (!this.isDragging || !this.dragStart) return;
@@ -376,27 +401,27 @@ class v extends HTMLElement {
376
401
  if (this.isResizing && this.selectedId && this.resizeHandle && this.resizeStartBounds && this.resizeOriginalObject) {
377
402
  const e = this.objects.find((d) => d.id === this.selectedId);
378
403
  if (!e) return;
379
- const n = i - this.dragStart.x, a = s - this.dragStart.y;
380
- let o = this.resizeStartBounds.x, l = this.resizeStartBounds.y, r = this.resizeStartBounds.width, h = this.resizeStartBounds.height;
381
- switch (this.resizeHandle.includes("e") && (r = this.resizeStartBounds.width + n), this.resizeHandle.includes("w") && (o = this.resizeStartBounds.x + n, r = this.resizeStartBounds.width - n), this.resizeHandle.includes("s") && (h = this.resizeStartBounds.height + a), this.resizeHandle.includes("n") && (l = this.resizeStartBounds.y + a, h = this.resizeStartBounds.height - a), r = Math.max(10, r), h = Math.max(10, h), e.type) {
404
+ const n = i - this.dragStart.x, o = s - this.dragStart.y;
405
+ let a = this.resizeStartBounds.x, l = this.resizeStartBounds.y, r = this.resizeStartBounds.width, h = this.resizeStartBounds.height;
406
+ switch (this.resizeHandle.includes("e") && (r = this.resizeStartBounds.width + n), this.resizeHandle.includes("w") && (a = this.resizeStartBounds.x + n, r = this.resizeStartBounds.width - n), this.resizeHandle.includes("s") && (h = this.resizeStartBounds.height + o), this.resizeHandle.includes("n") && (l = this.resizeStartBounds.y + o, h = this.resizeStartBounds.height - o), r = Math.max(10, r), h = Math.max(10, h), e.type) {
382
407
  case "RECTANGLE":
383
408
  case "IMAGE":
384
- e.x = o, e.y = l, e.width = r, e.height = h;
409
+ e.x = a, e.y = l, e.width = r, e.height = h;
385
410
  break;
386
411
  case "CIRCLE": {
387
412
  const d = Math.max(r, h) / 2;
388
- e.x = o + d, e.y = l + d, e.radius = d;
413
+ e.x = a + d, e.y = l + d, e.radius = d;
389
414
  break;
390
415
  }
391
416
  case "TEXT": {
392
417
  const d = this.resizeOriginalObject, c = r / this.resizeStartBounds.width;
393
- e.x = o, e.y = l + h, e.fontSize = Math.max(8, Math.round(d.fontSize * c));
418
+ e.x = a, e.y = l + h, e.fontSize = Math.max(8, Math.round(d.fontSize * c));
394
419
  break;
395
420
  }
396
421
  case "PATH": {
397
422
  const d = this.resizeOriginalObject, c = r / this.resizeStartBounds.width, p = h / this.resizeStartBounds.height;
398
423
  e.points = d.points.map((u) => ({
399
- x: o + (u.x - this.resizeStartBounds.x) * c,
424
+ x: a + (u.x - this.resizeStartBounds.x) * c,
400
425
  y: l + (u.y - this.resizeStartBounds.y) * p
401
426
  }));
402
427
  break;
@@ -409,8 +434,8 @@ class v extends HTMLElement {
409
434
  const e = this.objects.find((n) => n.id === this.selectedId);
410
435
  if (e) {
411
436
  if (e.type === "PATH") {
412
- const n = e, a = i - this.dragStart.x, o = s - this.dragStart.y;
413
- n.points = n.points.map((l) => ({ x: l.x + a, y: l.y + o })), this.dragStart = { x: i, y: s };
437
+ const n = e, o = i - this.dragStart.x, a = s - this.dragStart.y;
438
+ n.points = n.points.map((l) => ({ x: l.x + o, y: l.y + a })), this.dragStart = { x: i, y: s };
414
439
  } else
415
440
  e.x = i - this.dragOffset.x, e.y = s - this.dragOffset.y;
416
441
  this.renderCanvas(), this.renderMinimap();
@@ -436,8 +461,8 @@ class v extends HTMLElement {
436
461
  if (e && e.type === "TEXT") {
437
462
  const n = e;
438
463
  this.editingTextId = n.id, this.textInputPos = { x: n.x, y: n.y };
439
- const a = n.x * this.scale + this.panOffset.x, o = n.y * this.scale + this.panOffset.y;
440
- this.showTextInput(a, o, n.text), this.setTool("SELECT");
464
+ const o = n.x * this.scale + this.panOffset.x, a = n.y * this.scale + this.panOffset.y;
465
+ this.showTextInput(o, a, n.text), this.setTool("SELECT");
441
466
  }
442
467
  }
443
468
  // 渲染画布
@@ -499,8 +524,8 @@ class v extends HTMLElement {
499
524
  { x: s.x + s.width, y: s.y },
500
525
  { x: s.x, y: s.y + s.height },
501
526
  { x: s.x + s.width, y: s.y + s.height }
502
- ].forEach((a) => {
503
- t.beginPath(), t.rect(a.x - e / 2, a.y - e / 2, e, e), t.fill(), t.stroke();
527
+ ].forEach((o) => {
528
+ t.beginPath(), t.rect(o.x - e / 2, o.y - e / 2, e, e), t.fill(), t.stroke();
504
529
  }), t.strokeStyle = "#3b82f6", t.lineWidth = 1, t.setLineDash([5, 5]), t.strokeRect(s.x, s.y, s.width, s.height), t.setLineDash([]);
505
530
  }
506
531
  // 渲染小地图
@@ -508,8 +533,8 @@ class v extends HTMLElement {
508
533
  if (!this.minimapCtx || !this.config.showMinimap) return;
509
534
  const t = this.minimapCanvas, i = this.canvas;
510
535
  this.minimapCtx.clearRect(0, 0, t.width, t.height);
511
- const s = t.width / i.width, e = t.height / i.height, n = Math.min(s, e) * 0.92, a = i.width * n, o = i.height * n, l = (t.width - a) / 2, r = (t.height - o) / 2;
512
- this.minimapCtx.fillStyle = "#ffffff", this.minimapCtx.fillRect(l, r, a, o), this.minimapCtx.save(), this.minimapCtx.translate(l, r), this.minimapCtx.scale(n, n), this.minimapCtx.translate(this.panOffset.x, this.panOffset.y), this.minimapCtx.scale(this.scale, this.scale), (this.currentObject ? [...this.objects, this.currentObject] : this.objects).forEach((d) => {
536
+ const s = t.width / i.width, e = t.height / i.height, n = Math.min(s, e) * 0.92, o = i.width * n, a = i.height * n, l = (t.width - o) / 2, r = (t.height - a) / 2;
537
+ this.minimapCtx.fillStyle = "#ffffff", this.minimapCtx.fillRect(l, r, o, a), this.minimapCtx.save(), this.minimapCtx.translate(l, r), this.minimapCtx.scale(n, n), this.minimapCtx.translate(this.panOffset.x, this.panOffset.y), this.minimapCtx.scale(this.scale, this.scale), (this.currentObject ? [...this.objects, this.currentObject] : this.objects).forEach((d) => {
513
538
  switch (this.minimapCtx.fillStyle = d.color, this.minimapCtx.strokeStyle = d.color, this.minimapCtx.lineWidth = Math.max(d.lineWidth, 1), this.minimapCtx.setLineDash([]), d.type) {
514
539
  case "RECTANGLE": {
515
540
  const c = d;
@@ -537,11 +562,11 @@ class v extends HTMLElement {
537
562
  break;
538
563
  }
539
564
  }
540
- }), this.minimapCtx.restore(), this.minimapCtx.strokeStyle = "#94a3b8", this.minimapCtx.lineWidth = 1, this.minimapCtx.strokeRect(l, r, a, o);
565
+ }), this.minimapCtx.restore(), this.minimapCtx.strokeStyle = "#94a3b8", this.minimapCtx.lineWidth = 1, this.minimapCtx.strokeRect(l, r, o, a);
541
566
  }
542
567
  // 小地图点击定位
543
568
  handleMinimapClick(t) {
544
- const i = this.minimapCanvas.getBoundingClientRect(), s = t.clientX - i.left, e = t.clientY - i.top, n = this.minimapCanvas.width / this.canvas.width, a = this.minimapCanvas.height / this.canvas.height, o = Math.min(n, a) * 0.92, l = this.canvas.width * o, r = this.canvas.height * o, h = (this.minimapCanvas.width - l) / 2, d = (this.minimapCanvas.height - r) / 2, c = s - h, p = e - d, u = (c / o - this.panOffset.x) / this.scale, m = (p / o - this.panOffset.y) / this.scale, g = this.canvas.width / 2, x = this.canvas.height / 2;
569
+ const i = this.minimapCanvas.getBoundingClientRect(), s = t.clientX - i.left, e = t.clientY - i.top, n = this.minimapCanvas.width / this.canvas.width, o = this.minimapCanvas.height / this.canvas.height, a = Math.min(n, o) * 0.92, l = this.canvas.width * a, r = this.canvas.height * a, h = (this.minimapCanvas.width - l) / 2, d = (this.minimapCanvas.height - r) / 2, c = s - h, p = e - d, u = (c / a - this.panOffset.x) / this.scale, m = (p / a - this.panOffset.y) / this.scale, g = this.canvas.width / 2, x = this.canvas.height / 2;
545
570
  this.panOffset = {
546
571
  x: g / this.scale - u,
547
572
  y: x / this.scale - m
@@ -554,11 +579,11 @@ class v extends HTMLElement {
554
579
  const s = i.files[0], e = new FileReader();
555
580
  e.onload = (n) => {
556
581
  var l;
557
- const a = (l = n.target) == null ? void 0 : l.result, o = new Image();
558
- o.onload = () => {
582
+ const o = (l = n.target) == null ? void 0 : l.result, a = new Image();
583
+ a.onload = () => {
559
584
  this.saveHistory();
560
585
  const r = 300;
561
- let h = o.width, d = o.height;
586
+ let h = a.width, d = a.height;
562
587
  if (h > r || d > r) {
563
588
  const p = Math.min(r / h, r / d);
564
589
  h *= p, d *= p;
@@ -572,19 +597,19 @@ class v extends HTMLElement {
572
597
  height: d,
573
598
  color: "#000000",
574
599
  lineWidth: 1,
575
- dataUrl: a,
576
- imageElement: o
600
+ dataUrl: o,
601
+ imageElement: a
577
602
  };
578
603
  this.objects.push(c), this.selectedId = c.id, this.setTool("SELECT"), this.renderCanvas(), this.renderMinimap(), this.updateUI(), this.dispatchChangeEvent();
579
- }, o.src = a;
604
+ }, a.src = o;
580
605
  }, e.readAsDataURL(s), i.value = "";
581
606
  }
582
607
  // 保存 JSON
583
608
  saveJson() {
584
609
  const t = {
585
610
  version: "1.0",
586
- objects: this.objects.map((a) => {
587
- const { imageElement: o, ...l } = a;
611
+ objects: this.objects.map((o) => {
612
+ const { imageElement: a, ...l } = o;
588
613
  return l;
589
614
  })
590
615
  }, i = JSON.stringify(t, null, 2), s = new Blob([i], { type: "application/json" }), e = URL.createObjectURL(s), n = document.createElement("a");
@@ -596,10 +621,10 @@ class v extends HTMLElement {
596
621
  if (!i.files || i.files.length === 0) return;
597
622
  const s = i.files[0], e = new FileReader();
598
623
  e.onload = (n) => {
599
- var a;
624
+ var o;
600
625
  try {
601
- const o = JSON.parse((a = n.target) == null ? void 0 : a.result);
602
- o.objects && Array.isArray(o.objects) && (this.saveHistory(), this.objects = o.objects, this.selectedId = null, this.objects.forEach((l) => {
626
+ const a = JSON.parse((o = n.target) == null ? void 0 : o.result);
627
+ a.objects && Array.isArray(a.objects) && (this.saveHistory(), this.objects = a.objects, this.selectedId = null, this.objects.forEach((l) => {
603
628
  if (l.type === "IMAGE" && l.dataUrl) {
604
629
  const r = new Image();
605
630
  r.onload = () => {
@@ -607,8 +632,8 @@ class v extends HTMLElement {
607
632
  }, r.src = l.dataUrl;
608
633
  }
609
634
  }), this.renderCanvas(), this.renderMinimap(), this.updateUI(), this.dispatchChangeEvent());
610
- } catch (o) {
611
- console.error("Failed to load JSON:", o);
635
+ } catch (a) {
636
+ console.error("Failed to load JSON:", a);
612
637
  }
613
638
  }, e.readAsText(s), i.value = "";
614
639
  }
@@ -628,7 +653,7 @@ class v extends HTMLElement {
628
653
  if (this.selectedId) {
629
654
  const e = this.objects.find((n) => n.id === this.selectedId);
630
655
  if (e) {
631
- const a = {
656
+ const o = {
632
657
  RECTANGLE: "矩形",
633
658
  CIRCLE: "圆形",
634
659
  PATH: "画笔",
@@ -636,15 +661,15 @@ class v extends HTMLElement {
636
661
  IMAGE: "图片"
637
662
  }[e.type] || e.type;
638
663
  t.innerHTML = `
639
- <span class="selection-label">已选择: ${a}</span>
664
+ <span class="selection-label">已选择: ${o}</span>
640
665
  <button class="delete-btn" title="删除">
641
666
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
642
667
  <path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/>
643
668
  </svg>
644
669
  </button>
645
670
  `, t.classList.add("visible");
646
- const o = t.querySelector(".delete-btn");
647
- o && o.addEventListener("click", () => this.deleteSelected());
671
+ const a = t.querySelector(".delete-btn");
672
+ a && a.addEventListener("click", () => this.deleteSelected());
648
673
  }
649
674
  } else
650
675
  t.classList.remove("visible"), t.innerHTML = "";
@@ -762,6 +787,7 @@ class v extends HTMLElement {
762
787
 
763
788
  <!-- 文本输入 -->
764
789
  <div class="text-input-container" style="display: none;">
790
+ <div class="text-input-hint">按 Enter 确认,Esc 取消</div>
765
791
  <input type="text" class="text-input" placeholder="输入文本..." />
766
792
  </div>
767
793
 
@@ -794,10 +820,10 @@ class v extends HTMLElement {
794
820
  });
795
821
  const s = this.shadow.querySelector(".image-input");
796
822
  s && s.addEventListener("change", (h) => this.handleImageUpload(h));
797
- const e = this.shadow.querySelector(".zoom-in-btn"), n = this.shadow.querySelector(".zoom-out-btn"), a = this.shadow.querySelector(".zoom-text");
798
- e && e.addEventListener("click", () => this.zoomIn()), n && n.addEventListener("click", () => this.zoomOut()), a && a.addEventListener("click", () => this.resetZoom());
799
- const o = this.shadow.querySelector(".save-json-btn"), l = this.shadow.querySelector(".load-json-input"), r = this.shadow.querySelector(".export-png-btn");
800
- o && o.addEventListener("click", () => this.saveJson()), l && l.addEventListener("change", (h) => this.loadJson(h)), r && r.addEventListener("click", () => this.exportPng()), this.minimapCanvas && this.minimapCanvas.addEventListener("click", (h) => this.handleMinimapClick(h)), this.textInput && (this.textInput.addEventListener("keydown", (h) => {
823
+ const e = this.shadow.querySelector(".zoom-in-btn"), n = this.shadow.querySelector(".zoom-out-btn"), o = this.shadow.querySelector(".zoom-text");
824
+ e && e.addEventListener("click", () => this.zoomIn()), n && n.addEventListener("click", () => this.zoomOut()), o && o.addEventListener("click", () => this.resetZoom());
825
+ const a = this.shadow.querySelector(".save-json-btn"), l = this.shadow.querySelector(".load-json-input"), r = this.shadow.querySelector(".export-png-btn");
826
+ a && a.addEventListener("click", () => this.saveJson()), l && l.addEventListener("change", (h) => this.loadJson(h)), r && r.addEventListener("click", () => this.exportPng()), this.minimapCanvas && this.minimapCanvas.addEventListener("click", (h) => this.handleMinimapClick(h)), this.textInput && (this.textInput.addEventListener("keydown", (h) => {
801
827
  h.key === "Enter" ? (h.preventDefault(), this.submitText()) : h.key === "Escape" && this.hideTextInput();
802
828
  }), this.textInput.addEventListener("blur", () => {
803
829
  this.isTextInputVisible && this.submitText();
@@ -1108,6 +1134,19 @@ class v extends HTMLElement {
1108
1134
  .text-input-container {
1109
1135
  position: absolute;
1110
1136
  z-index: 20;
1137
+ display: flex;
1138
+ flex-direction: column;
1139
+ align-items: flex-start;
1140
+ }
1141
+
1142
+ .text-input-hint {
1143
+ background: rgba(0, 0, 0, 0.75);
1144
+ color: #fff;
1145
+ font-size: 12px;
1146
+ padding: 4px 8px;
1147
+ border-radius: 4px;
1148
+ margin-bottom: 4px;
1149
+ white-space: nowrap;
1111
1150
  }
1112
1151
 
1113
1152
  .text-input {
@@ -1145,8 +1184,8 @@ class v extends HTMLElement {
1145
1184
  `;
1146
1185
  }
1147
1186
  }
1148
- typeof window < "u" && !customElements.get("canvas-drawing-editor") && customElements.define("canvas-drawing-editor", v);
1187
+ typeof window < "u" && !customElements.get("canvas-drawing-editor") && customElements.define("canvas-drawing-editor", b);
1149
1188
  export {
1150
- v as CanvasDrawingEditor
1189
+ b as CanvasDrawingEditor
1151
1190
  };
1152
1191
  //# sourceMappingURL=canvas-drawing-editor.es.js.map