canvas-drawing-editor 2.0.5 → 2.0.7
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.
|
@@ -36,7 +36,7 @@ class b extends HTMLElement {
|
|
|
36
36
|
}
|
|
37
37
|
// 生命周期:连接到 DOM
|
|
38
38
|
connectedCallback() {
|
|
39
|
-
this.parseAttributes(), this.render(), this.setupEventListeners(), this.initCanvas()
|
|
39
|
+
this.parseAttributes(), this.render(), this.setupEventListeners(), this.initCanvas(!0);
|
|
40
40
|
}
|
|
41
41
|
// 生命周期:从 DOM 断开
|
|
42
42
|
disconnectedCallback() {
|
|
@@ -46,7 +46,7 @@ class b extends HTMLElement {
|
|
|
46
46
|
attributeChangedCallback(t, i, s) {
|
|
47
47
|
if (i !== s) {
|
|
48
48
|
if (t === "initial-data" && s && this.canvas) {
|
|
49
|
-
this.loadInitialData();
|
|
49
|
+
this.loadInitialData(), this.renderCanvas(), this.renderMinimap();
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
this.parseAttributes(), this.container && this.updateUI();
|
|
@@ -86,7 +86,7 @@ class b extends HTMLElement {
|
|
|
86
86
|
s.imageElement = e, this.renderCanvas(), this.renderMinimap();
|
|
87
87
|
}, e.src = s.dataUrl;
|
|
88
88
|
}
|
|
89
|
-
}), this.
|
|
89
|
+
}), this.updateUI());
|
|
90
90
|
} catch (i) {
|
|
91
91
|
console.error("Failed to parse initial-data:", i);
|
|
92
92
|
}
|
|
@@ -101,12 +101,12 @@ class b extends HTMLElement {
|
|
|
101
101
|
}
|
|
102
102
|
// 窗口大小变化处理
|
|
103
103
|
handleResize() {
|
|
104
|
-
this.initCanvas();
|
|
104
|
+
this.initCanvas(!1);
|
|
105
105
|
}
|
|
106
106
|
// 初始化画布
|
|
107
|
-
initCanvas() {
|
|
107
|
+
initCanvas(t = !1) {
|
|
108
108
|
!this.canvasContainer || !this.canvas || requestAnimationFrame(() => {
|
|
109
|
-
this.canvas.width = this.canvasContainer.clientWidth, this.canvas.height = this.canvasContainer.clientHeight, this.renderCanvas(), this.renderMinimap();
|
|
109
|
+
this.canvas.width = this.canvasContainer.clientWidth, this.canvas.height = this.canvasContainer.clientHeight, t && this.loadInitialData(), this.renderCanvas(), this.renderMinimap();
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
// 获取鼠标在画布上的位置(考虑缩放和平移)
|
|
@@ -534,7 +534,7 @@ class b extends HTMLElement {
|
|
|
534
534
|
const t = this.minimapCanvas, i = this.canvas;
|
|
535
535
|
this.minimapCtx.clearRect(0, 0, t.width, t.height);
|
|
536
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),
|
|
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.currentObject ? [...this.objects, this.currentObject] : this.objects).forEach((d) => {
|
|
538
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) {
|
|
539
539
|
case "RECTANGLE": {
|
|
540
540
|
const c = d;
|
|
@@ -566,10 +566,10 @@ class b extends HTMLElement {
|
|
|
566
566
|
}
|
|
567
567
|
// 小地图点击定位
|
|
568
568
|
handleMinimapClick(t) {
|
|
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,
|
|
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, g = (p / a - this.panOffset.y) / this.scale, m = this.canvas.width / 2, x = this.canvas.height / 2;
|
|
570
570
|
this.panOffset = {
|
|
571
|
-
x:
|
|
572
|
-
y: x / this.scale -
|
|
571
|
+
x: m / this.scale - u,
|
|
572
|
+
y: x / this.scale - g
|
|
573
573
|
}, this.renderCanvas(), this.renderMinimap();
|
|
574
574
|
}
|
|
575
575
|
// 图片上传处理
|
|
@@ -792,7 +792,7 @@ class b extends HTMLElement {
|
|
|
792
792
|
</div>
|
|
793
793
|
|
|
794
794
|
<!-- 空画布提示 -->
|
|
795
|
-
<div class="empty-hint">
|
|
795
|
+
<div class="empty-hint" style="display: ${this.getAttribute("initial-data") ? "none" : "flex"};">
|
|
796
796
|
<h3>开始创作</h3>
|
|
797
797
|
<p>选择左侧的工具开始绘制</p>
|
|
798
798
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-drawing-editor.es.js","sources":["../src/core/CanvasDrawingEditor.ts"],"sourcesContent":["/**\n * Canvas Drawing Editor - 纯 JavaScript Web Component\n * 无任何框架依赖\n */\n\n// 类型定义\nexport type ToolType = 'SELECT' | 'PENCIL' | 'RECTANGLE' | 'CIRCLE' | 'TEXT' | 'IMAGE';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface BaseObject {\n id: string;\n type: string;\n x: number;\n y: number;\n color: string;\n lineWidth: number;\n}\n\nexport interface RectObject extends BaseObject {\n type: 'RECTANGLE';\n width: number;\n height: number;\n}\n\nexport interface CircleObject extends BaseObject {\n type: 'CIRCLE';\n radius: number;\n}\n\nexport interface PathObject extends BaseObject {\n type: 'PATH';\n points: Point[];\n}\n\nexport interface TextObject extends BaseObject {\n type: 'TEXT';\n text: string;\n fontSize: number;\n}\n\nexport interface ImageObject extends BaseObject {\n type: 'IMAGE';\n width: number;\n height: number;\n dataUrl: string;\n imageElement?: HTMLImageElement;\n}\n\nexport type CanvasObject = RectObject | CircleObject | PathObject | TextObject | ImageObject;\n\nexport interface EditorConfig {\n title?: string;\n showPencil?: boolean;\n showRectangle?: boolean;\n showCircle?: boolean;\n showText?: boolean;\n showImage?: boolean;\n showZoom?: boolean;\n showDownload?: boolean;\n showExport?: boolean;\n showImport?: boolean;\n showColor?: boolean;\n showMinimap?: boolean;\n}\n\n// 默认配置\nconst defaultConfig: EditorConfig = {\n title: 'Canvas Editor',\n showPencil: true,\n showRectangle: true,\n showCircle: true,\n showText: true,\n showImage: true,\n showZoom: true,\n showDownload: true,\n showExport: true,\n showImport: true,\n showColor: true,\n showMinimap: true,\n};\n\n/**\n * Canvas Drawing Editor Web Component\n */\nexport class CanvasDrawingEditor extends HTMLElement {\n // Shadow DOM\n private shadow: ShadowRoot;\n\n // DOM 元素\n private container!: HTMLDivElement;\n private toolbar!: HTMLDivElement;\n private topBar!: HTMLDivElement;\n private canvasContainer!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx!: CanvasRenderingContext2D;\n private minimapCanvas!: HTMLCanvasElement;\n private minimapCtx!: CanvasRenderingContext2D;\n private textInput!: HTMLInputElement;\n private textInputContainer!: HTMLDivElement;\n\n // 配置\n private config: EditorConfig = { ...defaultConfig };\n\n // 状态\n private objects: CanvasObject[] = [];\n private selectedId: string | null = null;\n private tool: ToolType = 'SELECT';\n private color: string = '#000000';\n private lineWidth: number = 3;\n\n // 交互状态\n private isDragging: boolean = false;\n private dragStart: Point | null = null;\n private currentObject: CanvasObject | null = null;\n private dragOffset: Point = { x: 0, y: 0 };\n\n // 文本输入状态\n private isTextInputVisible: boolean = false;\n private textInputPos: Point = { x: 0, y: 0 };\n private textInputScreenPos: Point = { x: 0, y: 0 };\n private editingTextId: string | null = null;\n\n // 调整大小状态\n private isResizing: boolean = false;\n private resizeHandle: string | null = null;\n private resizeStartBounds: { x: number; y: number; width: number; height: number } | null = null;\n private resizeOriginalObject: CanvasObject | null = null;\n\n // 历史记录\n private history: CanvasObject[][] = [];\n private clipboard: CanvasObject | null = null;\n\n // 缩放状态\n private scale: number = 1;\n private panOffset: Point = { x: 0, y: 0 };\n\n // 平移状态\n private isPanning: boolean = false;\n private panStart: Point = { x: 0, y: 0 };\n\n // 绑定的事件处理器(用于移除监听)\n private boundHandleResize: () => void;\n private boundHandleKeyDown: (e: KeyboardEvent) => void;\n private boundHandleWheel: (e: WheelEvent) => void;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n \n // 绑定事件处理器\n this.boundHandleResize = this.handleResize.bind(this);\n this.boundHandleKeyDown = this.handleKeyDown.bind(this);\n this.boundHandleWheel = this.handleWheel.bind(this);\n }\n\n // 观察的属性\n static get observedAttributes(): string[] {\n return [\n 'title', 'show-pencil', 'show-rectangle', 'show-circle', 'show-text',\n 'show-image', 'show-zoom', 'show-download', 'show-export', 'show-import',\n 'show-color', 'show-minimap', 'initial-data'\n ];\n }\n\n // 生命周期:连接到 DOM\n connectedCallback(): void {\n this.parseAttributes();\n this.render();\n this.setupEventListeners();\n this.initCanvas();\n this.loadInitialData();\n }\n\n // 生命周期:从 DOM 断开\n disconnectedCallback(): void {\n this.removeEventListeners();\n }\n\n // 生命周期:属性变化\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n // 处理 initial-data 属性变化\n if (name === 'initial-data' && newValue && this.canvas) {\n this.loadInitialData();\n return;\n }\n\n this.parseAttributes();\n if (this.container) {\n this.updateUI();\n }\n }\n\n // 解析 HTML 属性\n private parseAttributes(): void {\n this.config = {\n title: this.getAttribute('title') || defaultConfig.title,\n showPencil: this.getAttribute('show-pencil') !== 'false',\n showRectangle: this.getAttribute('show-rectangle') !== 'false',\n showCircle: this.getAttribute('show-circle') !== 'false',\n showText: this.getAttribute('show-text') !== 'false',\n showImage: this.getAttribute('show-image') !== 'false',\n showZoom: this.getAttribute('show-zoom') !== 'false',\n showDownload: this.getAttribute('show-download') !== 'false',\n showExport: this.getAttribute('show-export') !== 'false',\n showImport: this.getAttribute('show-import') !== 'false',\n showColor: this.getAttribute('show-color') !== 'false',\n showMinimap: this.getAttribute('show-minimap') !== 'false',\n };\n }\n\n // 生成唯一 ID\n private generateId(): string {\n return Math.random().toString(36).substr(2, 9);\n }\n\n // 加载初始数据\n private loadInitialData(): void {\n const initialData = this.getAttribute('initial-data');\n if (!initialData) return;\n\n try {\n const data = JSON.parse(initialData);\n if (data.objects && Array.isArray(data.objects)) {\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n } catch (err) {\n console.error('Failed to parse initial-data:', err);\n }\n }\n\n // 设置事件监听\n private setupEventListeners(): void {\n window.addEventListener('resize', this.boundHandleResize);\n window.addEventListener('keydown', this.boundHandleKeyDown);\n }\n\n // 移除事件监听\n private removeEventListeners(): void {\n window.removeEventListener('resize', this.boundHandleResize);\n window.removeEventListener('keydown', this.boundHandleKeyDown);\n if (this.canvas) {\n this.canvas.removeEventListener('wheel', this.boundHandleWheel);\n }\n }\n\n // 窗口大小变化处理\n private handleResize(): void {\n this.initCanvas();\n }\n\n // 初始化画布\n private initCanvas(): void {\n if (!this.canvasContainer || !this.canvas) return;\n\n // 使用 requestAnimationFrame 确保 DOM 已经渲染\n requestAnimationFrame(() => {\n this.canvas.width = this.canvasContainer.clientWidth;\n this.canvas.height = this.canvasContainer.clientHeight;\n this.renderCanvas();\n this.renderMinimap();\n });\n }\n\n // 获取鼠标在画布上的位置(考虑缩放和平移)\n private getMousePos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n const x = (clientX - rect.left - this.panOffset.x) / this.scale;\n const y = (clientY - rect.top - this.panOffset.y) / this.scale;\n return { x, y };\n }\n\n // 获取屏幕坐标(不考虑缩放和平移)\n private getScreenPos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n return { x: clientX - rect.left, y: clientY - rect.top };\n }\n\n // 获取对象边界\n private getObjectBounds(obj: CanvasObject): { x: number; y: number; width: number; height: number } {\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE': {\n const r = obj as RectObject | ImageObject;\n return { x: r.x, y: r.y, width: r.width, height: r.height };\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n return { x: c.x - c.radius, y: c.y - c.radius, width: c.radius * 2, height: c.radius * 2 };\n }\n case 'TEXT': {\n const t = obj as TextObject;\n const width = t.text.length * t.fontSize * 0.6;\n return { x: t.x, y: t.y - t.fontSize, width, height: t.fontSize };\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return { x: 0, y: 0, width: 0, height: 0 };\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n }\n return { x: 0, y: 0, width: 0, height: 0 };\n }\n\n // 检查调整大小手柄\n private getResizeHandleAtPoint(obj: CanvasObject, x: number, y: number): string | null {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n const handles = [\n { name: 'nw', x: bounds.x, y: bounds.y },\n { name: 'ne', x: bounds.x + bounds.width, y: bounds.y },\n { name: 'sw', x: bounds.x, y: bounds.y + bounds.height },\n { name: 'se', x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n for (const handle of handles) {\n if (Math.abs(x - handle.x) <= handleSize && Math.abs(y - handle.y) <= handleSize) {\n return handle.name;\n }\n }\n return null;\n }\n\n // 碰撞检测\n private isHit(obj: CanvasObject, x: number, y: number): boolean {\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n return x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n const dist = Math.sqrt(Math.pow(x - c.x, 2) + Math.pow(y - c.y, 2));\n return dist <= c.radius;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n return x >= img.x && x <= img.x + img.width && y >= img.y && y <= img.y + img.height;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n return x >= t.x && x <= t.x + (t.text.length * t.fontSize * 0.6) && y >= t.y - t.fontSize && y <= t.y;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return false;\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return x >= minX && x <= maxX && y >= minY && y <= maxY;\n }\n }\n return false;\n }\n\n // 保存历史\n private saveHistory(): void {\n this.history.push(JSON.parse(JSON.stringify(this.objects)));\n }\n\n // 撤销\n private undo(): void {\n if (this.history.length === 0) return;\n const previousState = this.history.pop();\n if (previousState) {\n this.objects = previousState;\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.dispatchChangeEvent();\n }\n }\n\n // 删除选中对象\n private deleteSelected(): void {\n if (this.selectedId) {\n this.saveHistory();\n this.objects = this.objects.filter(o => o.id !== this.selectedId);\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 复制选中对象\n private copySelected(): void {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.clipboard = JSON.parse(JSON.stringify(selectedObj));\n }\n }\n }\n\n // 粘贴对象\n private pasteObject(): void {\n if (this.clipboard) {\n this.saveHistory();\n const newObj = {\n ...JSON.parse(JSON.stringify(this.clipboard)),\n id: this.generateId(),\n x: this.clipboard.x + 20,\n y: this.clipboard.y + 20\n };\n if (newObj.type === 'PATH' && newObj.points) {\n newObj.points = newObj.points.map((pt: Point) => ({\n x: pt.x + 20,\n y: pt.y + 20\n }));\n }\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.clipboard = newObj;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 派发变化事件\n private dispatchChangeEvent(): void {\n this.dispatchEvent(new CustomEvent('editor-change', {\n bubbles: true,\n composed: true,\n detail: { objects: this.objects }\n }));\n }\n\n // 键盘事件处理\n private handleKeyDown(e: KeyboardEvent): void {\n if (this.isTextInputVisible) return;\n\n // Ctrl+Z: 撤销\n if ((e.ctrlKey || e.metaKey) && e.key === 'z') {\n e.preventDefault();\n this.undo();\n return;\n }\n\n // Ctrl+C: 复制\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n if (this.selectedId) {\n e.preventDefault();\n this.copySelected();\n }\n return;\n }\n\n // Ctrl+V: 粘贴\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n if (this.clipboard) {\n e.preventDefault();\n this.pasteObject();\n }\n return;\n }\n\n // Delete/Backspace: 删除\n if ((e.key === 'Delete' || e.key === 'Backspace') && this.selectedId) {\n e.preventDefault();\n this.deleteSelected();\n return;\n }\n\n // 快捷键切换工具\n if (!e.ctrlKey && !e.metaKey) {\n switch (e.key.toLowerCase()) {\n case 'v':\n this.setTool('SELECT');\n break;\n case 'p':\n case 'b':\n this.setTool('PENCIL');\n break;\n case 'r':\n this.setTool('RECTANGLE');\n break;\n case 'o':\n this.setTool('CIRCLE');\n break;\n case 't':\n this.setTool('TEXT');\n break;\n case 'escape':\n this.selectedId = null;\n this.hideTextInput();\n this.renderCanvas();\n this.updateUI();\n break;\n }\n }\n }\n\n // 滚轮缩放\n private handleWheel(e: WheelEvent): void {\n e.preventDefault();\n const rect = this.canvas.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n const delta = e.deltaY > 0 ? 0.9 : 1.1;\n const newScale = this.scale * delta;\n\n this.zoomAtPoint(newScale, mouseX, mouseY);\n }\n\n // 以指定点为中心缩放\n private zoomAtPoint(newScale: number, centerX: number, centerY: number): void {\n const clampedScale = Math.min(Math.max(newScale, 0.2), 5);\n\n const mouseXBeforeZoom = (centerX - this.panOffset.x) / this.scale;\n const mouseYBeforeZoom = (centerY - this.panOffset.y) / this.scale;\n\n const newPanOffsetX = centerX - mouseXBeforeZoom * clampedScale;\n const newPanOffsetY = centerY - mouseYBeforeZoom * clampedScale;\n\n this.scale = clampedScale;\n this.panOffset = { x: newPanOffsetX, y: newPanOffsetY };\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 放大\n private zoomIn(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale * 1.2, centerX, centerY);\n }\n\n // 缩小\n private zoomOut(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale / 1.2, centerX, centerY);\n }\n\n // 重置缩放\n private resetZoom(): void {\n this.scale = 1;\n this.panOffset = { x: 0, y: 0 };\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 更新缩放显示\n private updateZoomDisplay(): void {\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomText) {\n zoomText.textContent = `${Math.round(this.scale * 100)}%`;\n }\n }\n\n // 设置工具\n private setTool(tool: ToolType): void {\n this.tool = tool;\n this.updateToolButtons();\n }\n\n // 更新工具按钮状态\n private updateToolButtons(): void {\n const buttons = this.shadow.querySelectorAll('.tool-btn');\n buttons.forEach(btn => {\n const btnTool = btn.getAttribute('data-tool');\n if (btnTool === this.tool) {\n btn.classList.add('active');\n } else {\n btn.classList.remove('active');\n }\n });\n }\n\n // 隐藏文本输入\n private hideTextInput(): void {\n this.isTextInputVisible = false;\n if (this.textInputContainer) {\n this.textInputContainer.style.display = 'none';\n }\n this.editingTextId = null;\n }\n\n // 显示文本输入\n private showTextInput(screenX: number, screenY: number, text: string = ''): void {\n this.isTextInputVisible = true;\n this.textInputScreenPos = { x: screenX, y: screenY };\n\n if (this.textInputContainer && this.textInput) {\n this.textInputContainer.style.display = 'block';\n this.textInputContainer.style.left = `${screenX}px`;\n this.textInputContainer.style.top = `${screenY - 30}px`;\n this.textInput.value = text;\n this.textInput.style.color = this.color;\n setTimeout(() => {\n this.textInput.focus();\n if (text) this.textInput.select();\n }, 0);\n }\n }\n\n // 提交文本\n private submitText(): void {\n const value = this.textInput?.value?.trim();\n if (value) {\n if (this.editingTextId) {\n const existingObj = this.objects.find(o => o.id === this.editingTextId) as TextObject | undefined;\n if (existingObj && existingObj.text !== value) {\n this.saveHistory();\n existingObj.text = value;\n }\n this.selectedId = this.editingTextId;\n } else {\n this.saveHistory();\n const newObj: TextObject = {\n id: this.generateId(),\n type: 'TEXT',\n x: this.textInputPos.x,\n y: this.textInputPos.y,\n text: value,\n fontSize: 24,\n color: this.color,\n lineWidth: this.lineWidth\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n }\n this.dispatchChangeEvent();\n }\n this.hideTextInput();\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 画布鼠标按下\n private handleCanvasPointerDown(e: MouseEvent | TouchEvent): void {\n const { x, y } = this.getMousePos(e);\n const screenPos = this.getScreenPos(e);\n this.dragStart = { x, y };\n this.isDragging = true;\n\n // 如果文本输入可见且不是文本工具,先保存文本\n if (this.isTextInputVisible && this.tool !== 'TEXT') {\n this.submitText();\n }\n\n if (this.tool === 'SELECT') {\n // 检查是否点击调整大小手柄\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const handle = this.getResizeHandleAtPoint(selectedObj, x, y);\n if (handle) {\n this.saveHistory();\n this.isResizing = true;\n this.resizeHandle = handle;\n this.resizeStartBounds = this.getObjectBounds(selectedObj);\n this.resizeOriginalObject = JSON.parse(JSON.stringify(selectedObj));\n return;\n }\n }\n }\n\n // 查找点击的对象\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject) {\n this.selectedId = clickedObject.id;\n this.dragOffset = { x: x - clickedObject.x, y: y - clickedObject.y };\n this.saveHistory();\n this.updateUI();\n } else {\n // 开始拖拽画布\n this.selectedId = null;\n this.isPanning = true;\n this.panStart = screenPos;\n this.updateUI();\n }\n } else if (this.tool === 'TEXT') {\n // 显示文本输入\n this.textInputPos = { x, y };\n this.showTextInput(screenPos.x, screenPos.y);\n this.isDragging = false;\n } else {\n // 开始绘制图形\n this.saveHistory();\n const id = this.generateId();\n if (this.tool === 'RECTANGLE') {\n this.currentObject = { id, type: 'RECTANGLE', x, y, width: 0, height: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'CIRCLE') {\n this.currentObject = { id, type: 'CIRCLE', x, y, radius: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'PENCIL') {\n this.currentObject = { id, type: 'PATH', x, y, points: [{ x, y }], color: this.color, lineWidth: this.lineWidth };\n }\n }\n\n this.renderCanvas();\n }\n\n // 画布鼠标移动\n private handleCanvasPointerMove(e: MouseEvent | TouchEvent): void {\n // 处理画布拖拽\n if (this.isPanning) {\n const screenPos = this.getScreenPos(e);\n const dx = screenPos.x - this.panStart.x;\n const dy = screenPos.y - this.panStart.y;\n this.panOffset = { x: this.panOffset.x + dx, y: this.panOffset.y + dy };\n this.panStart = screenPos;\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n if (!this.isDragging || !this.dragStart) return;\n const { x, y } = this.getMousePos(e);\n\n // 处理调整大小\n if (this.isResizing && this.selectedId && this.resizeHandle && this.resizeStartBounds && this.resizeOriginalObject) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (!obj) return;\n\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n let newX = this.resizeStartBounds.x;\n let newY = this.resizeStartBounds.y;\n let newWidth = this.resizeStartBounds.width;\n let newHeight = this.resizeStartBounds.height;\n\n if (this.resizeHandle.includes('e')) newWidth = this.resizeStartBounds.width + dx;\n if (this.resizeHandle.includes('w')) {\n newX = this.resizeStartBounds.x + dx;\n newWidth = this.resizeStartBounds.width - dx;\n }\n if (this.resizeHandle.includes('s')) newHeight = this.resizeStartBounds.height + dy;\n if (this.resizeHandle.includes('n')) {\n newY = this.resizeStartBounds.y + dy;\n newHeight = this.resizeStartBounds.height - dy;\n }\n\n newWidth = Math.max(10, newWidth);\n newHeight = Math.max(10, newHeight);\n\n // 根据对象类型应用变化\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE':\n (obj as RectObject | ImageObject).x = newX;\n (obj as RectObject | ImageObject).y = newY;\n (obj as RectObject | ImageObject).width = newWidth;\n (obj as RectObject | ImageObject).height = newHeight;\n break;\n case 'CIRCLE': {\n const radius = Math.max(newWidth, newHeight) / 2;\n (obj as CircleObject).x = newX + radius;\n (obj as CircleObject).y = newY + radius;\n (obj as CircleObject).radius = radius;\n break;\n }\n case 'TEXT': {\n const origT = this.resizeOriginalObject as TextObject;\n const scaleFactor = newWidth / this.resizeStartBounds.width;\n (obj as TextObject).x = newX;\n (obj as TextObject).y = newY + newHeight;\n (obj as TextObject).fontSize = Math.max(8, Math.round(origT.fontSize * scaleFactor));\n break;\n }\n case 'PATH': {\n const origP = this.resizeOriginalObject as PathObject;\n const scaleX = newWidth / this.resizeStartBounds.width;\n const scaleY = newHeight / this.resizeStartBounds.height;\n (obj as PathObject).points = origP.points.map(pt => ({\n x: newX + (pt.x - this.resizeStartBounds!.x) * scaleX,\n y: newY + (pt.y - this.resizeStartBounds!.y) * scaleY\n }));\n break;\n }\n }\n\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n // 移动选中对象\n if (this.tool === 'SELECT' && this.selectedId) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (obj) {\n if (obj.type === 'PATH') {\n const p = obj as PathObject;\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n p.points = p.points.map(pt => ({ x: pt.x + dx, y: pt.y + dy }));\n this.dragStart = { x, y };\n } else {\n obj.x = x - this.dragOffset.x;\n obj.y = y - this.dragOffset.y;\n }\n this.renderCanvas();\n this.renderMinimap();\n }\n } else if (this.currentObject) {\n // 更新正在绘制的图形\n if (this.currentObject.type === 'RECTANGLE') {\n (this.currentObject as RectObject).width = x - this.currentObject.x;\n (this.currentObject as RectObject).height = y - this.currentObject.y;\n } else if (this.currentObject.type === 'CIRCLE') {\n const radius = Math.sqrt(Math.pow(x - this.currentObject.x, 2) + Math.pow(y - this.currentObject.y, 2));\n (this.currentObject as CircleObject).radius = radius;\n } else if (this.currentObject.type === 'PATH') {\n (this.currentObject as PathObject).points.push({ x, y });\n }\n this.renderCanvas();\n }\n }\n\n // 画布鼠标抬起\n private handleCanvasPointerUp(): void {\n this.isDragging = false;\n this.dragStart = null;\n this.isResizing = false;\n this.resizeHandle = null;\n this.resizeStartBounds = null;\n this.resizeOriginalObject = null;\n this.isPanning = false;\n\n if (this.currentObject) {\n this.objects.push(this.currentObject);\n this.currentObject = null;\n this.dispatchChangeEvent();\n }\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 双击编辑文本\n private handleCanvasDoubleClick(e: MouseEvent): void {\n e.preventDefault();\n const { x, y } = this.getMousePos(e);\n\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject && clickedObject.type === 'TEXT') {\n const textObj = clickedObject as TextObject;\n this.editingTextId = textObj.id;\n this.textInputPos = { x: textObj.x, y: textObj.y };\n const screenX = textObj.x * this.scale + this.panOffset.x;\n const screenY = textObj.y * this.scale + this.panOffset.y;\n this.showTextInput(screenX, screenY, textObj.text);\n this.setTool('SELECT');\n }\n }\n\n // 渲染画布\n private renderCanvas(): void {\n if (!this.ctx) return;\n\n // 清空画布\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 绘制背景\n this.ctx.fillStyle = '#ffffff';\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 应用缩放和平移\n this.ctx.save();\n this.ctx.translate(this.panOffset.x, this.panOffset.y);\n this.ctx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(this.ctx, obj, false));\n\n // 绘制正在创建的对象\n if (this.currentObject) {\n this.drawObject(this.ctx, this.currentObject, false);\n }\n\n // 绘制选中对象的调整手柄\n if (this.selectedId && this.tool === 'SELECT') {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.drawSelectionHandles(this.ctx, selectedObj);\n }\n }\n\n this.ctx.restore();\n }\n\n // 绘制单个对象\n private drawObject(ctx: CanvasRenderingContext2D, obj: CanvasObject, isMinimap: boolean): void {\n ctx.beginPath();\n ctx.strokeStyle = obj.color;\n ctx.lineWidth = obj.lineWidth;\n ctx.fillStyle = obj.color;\n\n // 选中高亮(仅主画布)\n if (!isMinimap && obj.id === this.selectedId) {\n ctx.shadowColor = 'rgba(0, 100, 255, 0.5)';\n ctx.shadowBlur = 10;\n } else {\n ctx.shadowBlur = 0;\n }\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n ctx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n ctx.beginPath();\n ctx.arc(c.x, c.y, c.radius, 0, 2 * Math.PI);\n ctx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length < 2) break;\n ctx.beginPath();\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(p.points[0].x, p.points[0].y);\n for (let i = 1; i < p.points.length; i++) {\n ctx.lineTo(p.points[i].x, p.points[i].y);\n }\n ctx.stroke();\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n ctx.font = `${t.fontSize}px sans-serif`;\n ctx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const imgObj = obj as ImageObject;\n if (imgObj.imageElement && imgObj.imageElement.complete) {\n ctx.drawImage(imgObj.imageElement, imgObj.x, imgObj.y, imgObj.width, imgObj.height);\n } else if (imgObj.dataUrl) {\n // 加载图片\n const img = new Image();\n img.onload = () => {\n imgObj.imageElement = img;\n this.renderCanvas();\n };\n img.src = imgObj.dataUrl;\n }\n break;\n }\n }\n }\n\n // 绘制选中手柄\n private drawSelectionHandles(ctx: CanvasRenderingContext2D, obj: CanvasObject): void {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n ctx.shadowBlur = 0;\n ctx.fillStyle = '#3b82f6';\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n\n // 绘制角落手柄\n const corners = [\n { x: bounds.x, y: bounds.y },\n { x: bounds.x + bounds.width, y: bounds.y },\n { x: bounds.x, y: bounds.y + bounds.height },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n corners.forEach(corner => {\n ctx.beginPath();\n ctx.rect(corner.x - handleSize / 2, corner.y - handleSize / 2, handleSize, handleSize);\n ctx.fill();\n ctx.stroke();\n });\n\n // 绘制选择边框\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 1;\n ctx.setLineDash([5, 5]);\n ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);\n ctx.setLineDash([]);\n }\n\n // 渲染小地图\n private renderMinimap(): void {\n if (!this.minimapCtx || !this.config.showMinimap) return;\n\n const minimap = this.minimapCanvas;\n const mainCanvas = this.canvas;\n\n this.minimapCtx.clearRect(0, 0, minimap.width, minimap.height);\n\n // 计算缩放比例\n const scaleX = minimap.width / mainCanvas.width;\n const scaleY = minimap.height / mainCanvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = mainCanvas.width * minimapScale;\n const canvasH = mainCanvas.height * minimapScale;\n const offsetX = (minimap.width - canvasW) / 2;\n const offsetY = (minimap.height - canvasH) / 2;\n\n // 绘制画布区域背景\n this.minimapCtx.fillStyle = '#ffffff';\n this.minimapCtx.fillRect(offsetX, offsetY, canvasW, canvasH);\n\n // 应用主画布的缩放和平移变换\n this.minimapCtx.save();\n this.minimapCtx.translate(offsetX, offsetY);\n this.minimapCtx.scale(minimapScale, minimapScale);\n this.minimapCtx.translate(this.panOffset.x, this.panOffset.y);\n this.minimapCtx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n const allObjects = this.currentObject ? [...this.objects, this.currentObject] : this.objects;\n allObjects.forEach(obj => {\n this.minimapCtx.fillStyle = obj.color;\n this.minimapCtx.strokeStyle = obj.color;\n this.minimapCtx.lineWidth = Math.max(obj.lineWidth, 1);\n this.minimapCtx.setLineDash([]);\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n this.minimapCtx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n this.minimapCtx.beginPath();\n this.minimapCtx.arc(c.x, c.y, c.radius, 0, Math.PI * 2);\n this.minimapCtx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length > 0) {\n this.minimapCtx.beginPath();\n this.minimapCtx.lineCap = 'round';\n this.minimapCtx.lineJoin = 'round';\n this.minimapCtx.moveTo(p.points[0].x, p.points[0].y);\n p.points.forEach(pt => this.minimapCtx.lineTo(pt.x, pt.y));\n this.minimapCtx.stroke();\n }\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n this.minimapCtx.font = `${t.fontSize}px sans-serif`;\n this.minimapCtx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n if (img.imageElement) {\n this.minimapCtx.drawImage(img.imageElement, img.x, img.y, img.width, img.height);\n }\n break;\n }\n }\n });\n\n this.minimapCtx.restore();\n\n // 绘制画布边框\n this.minimapCtx.strokeStyle = '#94a3b8';\n this.minimapCtx.lineWidth = 1;\n this.minimapCtx.strokeRect(offsetX, offsetY, canvasW, canvasH);\n }\n\n // 小地图点击定位\n private handleMinimapClick(e: MouseEvent): void {\n const rect = this.minimapCanvas.getBoundingClientRect();\n const clickX = e.clientX - rect.left;\n const clickY = e.clientY - rect.top;\n\n const scaleX = this.minimapCanvas.width / this.canvas.width;\n const scaleY = this.minimapCanvas.height / this.canvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = this.canvas.width * minimapScale;\n const canvasH = this.canvas.height * minimapScale;\n const offsetX = (this.minimapCanvas.width - canvasW) / 2;\n const offsetY = (this.minimapCanvas.height - canvasH) / 2;\n\n const relX = clickX - offsetX;\n const relY = clickY - offsetY;\n\n const canvasX = (relX / minimapScale - this.panOffset.x) / this.scale;\n const canvasY = (relY / minimapScale - this.panOffset.y) / this.scale;\n\n const viewportCenterX = this.canvas.width / 2;\n const viewportCenterY = this.canvas.height / 2;\n\n this.panOffset = {\n x: viewportCenterX / this.scale - canvasX,\n y: viewportCenterY / this.scale - canvasY\n };\n\n this.renderCanvas();\n this.renderMinimap();\n }\n\n // 图片上传处理\n private handleImageUpload(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n const dataUrl = event.target?.result as string;\n const img = new Image();\n img.onload = () => {\n this.saveHistory();\n const maxSize = 300;\n let width = img.width;\n let height = img.height;\n if (width > maxSize || height > maxSize) {\n const ratio = Math.min(maxSize / width, maxSize / height);\n width *= ratio;\n height *= ratio;\n }\n\n const newObj: ImageObject = {\n id: this.generateId(),\n type: 'IMAGE',\n x: 100,\n y: 100,\n width,\n height,\n color: '#000000',\n lineWidth: 1,\n dataUrl,\n imageElement: img\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n };\n img.src = dataUrl;\n };\n\n reader.readAsDataURL(file);\n input.value = '';\n }\n\n // 保存 JSON\n private saveJson(): void {\n const data = {\n version: '1.0',\n objects: this.objects.map(obj => {\n const { imageElement, ...rest } = obj as ImageObject;\n return rest;\n })\n };\n const json = JSON.stringify(data, null, 2);\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-project.json';\n a.click();\n URL.revokeObjectURL(url);\n }\n\n // 加载 JSON\n private loadJson(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n try {\n const data = JSON.parse(event.target?.result as string);\n if (data.objects && Array.isArray(data.objects)) {\n this.saveHistory();\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n } catch (err) {\n console.error('Failed to load JSON:', err);\n }\n };\n\n reader.readAsText(file);\n input.value = '';\n }\n\n // 导出 PNG\n private exportPng(): void {\n // 创建临时画布\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = this.canvas.width;\n tempCanvas.height = this.canvas.height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n // 绘制白色背景\n tempCtx.fillStyle = '#ffffff';\n tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);\n\n // 应用缩放和平移\n tempCtx.translate(this.panOffset.x, this.panOffset.y);\n tempCtx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(tempCtx, obj, true));\n\n // 下载\n const url = tempCanvas.toDataURL('image/png');\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-export.png';\n a.click();\n }\n\n // 更新 UI\n private updateUI(): void {\n // 更新选中状态显示\n const selectionInfo = this.shadow.querySelector('.selection-info');\n if (selectionInfo) {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const typeLabels: Record<string, string> = {\n 'RECTANGLE': '矩形',\n 'CIRCLE': '圆形',\n 'PATH': '画笔',\n 'TEXT': '文本',\n 'IMAGE': '图片'\n };\n const typeLabel = typeLabels[selectedObj.type] || selectedObj.type;\n selectionInfo.innerHTML = `\n <span class=\"selection-label\">已选择: ${typeLabel}</span>\n <button class=\"delete-btn\" title=\"删除\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\"/>\n </svg>\n </button>\n `;\n selectionInfo.classList.add('visible');\n const deleteBtn = selectionInfo.querySelector('.delete-btn');\n if (deleteBtn) {\n deleteBtn.addEventListener('click', () => this.deleteSelected());\n }\n }\n } else {\n selectionInfo.classList.remove('visible');\n selectionInfo.innerHTML = '';\n }\n }\n\n // 更新撤销按钮状态\n const undoBtn = this.shadow.querySelector('.undo-btn') as HTMLButtonElement;\n if (undoBtn) {\n undoBtn.disabled = this.history.length === 0;\n }\n\n // 更新空画布提示显示\n const emptyHint = this.shadow.querySelector('.empty-hint') as HTMLElement;\n if (emptyHint) {\n emptyHint.style.display = this.objects.length === 0 ? 'flex' : 'none';\n }\n }\n\n // 渲染 DOM 结构\n private render(): void {\n this.shadow.innerHTML = `\n <style>${this.getStyles()}</style>\n <div class=\"editor-container\">\n <!-- 左侧工具栏 -->\n <div class=\"toolbar\">\n ${this.createToolButton('SELECT', 'select-icon', '选择 (V)')}\n <div class=\"divider\"></div>\n ${this.config.showPencil ? this.createToolButton('PENCIL', 'pencil-icon', '画笔 (P)') : ''}\n ${this.config.showRectangle ? this.createToolButton('RECTANGLE', 'rect-icon', '矩形 (R)') : ''}\n ${this.config.showCircle ? this.createToolButton('CIRCLE', 'circle-icon', '圆形 (O)') : ''}\n ${this.config.showText ? this.createToolButton('TEXT', 'text-icon', '文本 (T)') : ''}\n ${this.config.showImage ? `\n <label class=\"tool-btn\" title=\"插入图片\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/>\n <path d=\"M21 15l-5-5L5 21\"/>\n </svg>\n <input type=\"file\" accept=\"image/*\" class=\"hidden image-input\" />\n </label>\n ` : ''}\n <div class=\"divider\"></div>\n <button class=\"tool-btn undo-btn\" title=\"撤销 (Ctrl+Z)\" disabled>\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M1 4v6h6\"/>\n <path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>\n </svg>\n </button>\n <div class=\"spacer\"></div>\n ${this.config.showColor ? `\n <input type=\"color\" class=\"color-picker\" value=\"${this.color}\" title=\"颜色\" />\n ` : ''}\n </div>\n\n <!-- 主区域 -->\n <div class=\"main-area\">\n <!-- 顶部栏 -->\n <div class=\"top-bar\">\n <div class=\"top-bar-left\">\n <h2 class=\"title\">${this.config.title}</h2>\n <div class=\"selection-info\"></div>\n </div>\n <div class=\"top-bar-right\">\n ${this.config.showZoom ? `\n <div class=\"zoom-controls\">\n <button class=\"zoom-btn zoom-out-btn\" title=\"缩小\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M8 11h6\"/>\n </svg>\n </button>\n <button class=\"zoom-text\" title=\"重置缩放\">100%</button>\n <button class=\"zoom-btn zoom-in-btn\" title=\"放大\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M11 8v6M8 11h6\"/>\n </svg>\n </button>\n </div>\n ` : ''}\n ${(this.config.showExport || this.config.showImport || this.config.showDownload) ? `\n <div class=\"file-controls\">\n ${this.config.showExport ? `\n <button class=\"file-btn save-json-btn\" title=\"保存项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z\"/>\n <polyline points=\"17 21 17 13 7 13 7 21\"/><polyline points=\"7 3 7 8 15 8\"/>\n </svg>\n </button>\n ` : ''}\n ${this.config.showImport ? `\n <label class=\"file-btn\" title=\"加载项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z\"/>\n </svg>\n <input type=\"file\" accept=\".json\" class=\"hidden load-json-input\" />\n </label>\n ` : ''}\n ${this.config.showDownload ? `\n <button class=\"file-btn export-png-btn\" title=\"导出 PNG\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3\"/>\n </svg>\n </button>\n ` : ''}\n </div>\n ` : ''}\n </div>\n </div>\n\n <!-- 画布容器 -->\n <div class=\"canvas-container\">\n <canvas class=\"main-canvas\"></canvas>\n\n ${this.config.showMinimap ? `\n <div class=\"minimap-wrapper\">\n <div class=\"minimap-header\">\n <svg class=\"minimap-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M3 9h18M9 21V9\"/>\n </svg>\n <span>导航</span>\n </div>\n <canvas class=\"minimap-canvas\" width=\"220\" height=\"140\"></canvas>\n </div>\n ` : ''}\n\n <!-- 文本输入 -->\n <div class=\"text-input-container\" style=\"display: none;\">\n <div class=\"text-input-hint\">按 Enter 确认,Esc 取消</div>\n <input type=\"text\" class=\"text-input\" placeholder=\"输入文本...\" />\n </div>\n\n <!-- 空画布提示 -->\n <div class=\"empty-hint\">\n <h3>开始创作</h3>\n <p>选择左侧的工具开始绘制</p>\n </div>\n </div>\n </div>\n </div>\n `;\n\n // 获取 DOM 引用\n this.container = this.shadow.querySelector('.editor-container')!;\n this.toolbar = this.shadow.querySelector('.toolbar')!;\n this.topBar = this.shadow.querySelector('.top-bar')!;\n this.canvasContainer = this.shadow.querySelector('.canvas-container')!;\n this.canvas = this.shadow.querySelector('.main-canvas')!;\n this.ctx = this.canvas.getContext('2d')!;\n\n if (this.config.showMinimap) {\n this.minimapCanvas = this.shadow.querySelector('.minimap-canvas')!;\n this.minimapCtx = this.minimapCanvas.getContext('2d')!;\n }\n\n this.textInputContainer = this.shadow.querySelector('.text-input-container')!;\n this.textInput = this.shadow.querySelector('.text-input')!;\n\n // 绑定事件\n this.bindEvents();\n }\n\n // 绑定事件\n private bindEvents(): void {\n // 画布事件\n this.canvas.addEventListener('mousedown', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('mousemove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('mouseup', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('mouseleave', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('dblclick', (e) => this.handleCanvasDoubleClick(e));\n this.canvas.addEventListener('touchstart', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('touchmove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('touchend', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('wheel', this.boundHandleWheel, { passive: false });\n\n // 工具按钮\n this.shadow.querySelectorAll('.tool-btn[data-tool]').forEach(btn => {\n btn.addEventListener('mousedown', (e) => {\n // 阻止 blur 事件触发,在点击后手动处理\n e.preventDefault();\n });\n btn.addEventListener('click', () => {\n // 如果有文本输入,先提交\n if (this.isTextInputVisible) {\n this.submitText();\n }\n const tool = btn.getAttribute('data-tool') as ToolType;\n this.setTool(tool);\n });\n });\n\n // 撤销按钮\n const undoBtn = this.shadow.querySelector('.undo-btn');\n if (undoBtn) {\n undoBtn.addEventListener('click', () => this.undo());\n }\n\n // 颜色选择器\n const colorPicker = this.shadow.querySelector('.color-picker') as HTMLInputElement;\n if (colorPicker) {\n colorPicker.addEventListener('input', (e) => {\n this.color = (e.target as HTMLInputElement).value;\n });\n }\n\n // 图片上传\n const imageInput = this.shadow.querySelector('.image-input');\n if (imageInput) {\n imageInput.addEventListener('change', (e) => this.handleImageUpload(e));\n }\n\n // 缩放按钮\n const zoomInBtn = this.shadow.querySelector('.zoom-in-btn');\n const zoomOutBtn = this.shadow.querySelector('.zoom-out-btn');\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomInBtn) zoomInBtn.addEventListener('click', () => this.zoomIn());\n if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => this.zoomOut());\n if (zoomText) zoomText.addEventListener('click', () => this.resetZoom());\n\n // 文件操作\n const saveJsonBtn = this.shadow.querySelector('.save-json-btn');\n const loadJsonInput = this.shadow.querySelector('.load-json-input');\n const exportPngBtn = this.shadow.querySelector('.export-png-btn');\n if (saveJsonBtn) saveJsonBtn.addEventListener('click', () => this.saveJson());\n if (loadJsonInput) loadJsonInput.addEventListener('change', (e) => this.loadJson(e));\n if (exportPngBtn) exportPngBtn.addEventListener('click', () => this.exportPng());\n\n // 小地图点击\n if (this.minimapCanvas) {\n this.minimapCanvas.addEventListener('click', (e) => this.handleMinimapClick(e));\n }\n\n // 文本输入\n if (this.textInput) {\n this.textInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.submitText();\n } else if (e.key === 'Escape') {\n this.hideTextInput();\n }\n });\n this.textInput.addEventListener('blur', () => {\n if (this.isTextInputVisible) {\n this.submitText();\n }\n });\n }\n }\n\n // 创建工具按钮 HTML\n private createToolButton(tool: ToolType, iconClass: string, title: string): string {\n const icons: Record<string, string> = {\n 'select-icon': '<path d=\"M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z\"/><path d=\"M13 13l6 6\"/>',\n 'pencil-icon': '<path d=\"M17 3a2.85 2.85 0 114 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>',\n 'rect-icon': '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>',\n 'circle-icon': '<circle cx=\"12\" cy=\"12\" r=\"10\"/>',\n 'text-icon': '<path d=\"M4 7V4h16v3M9 20h6M12 4v16\"/>',\n };\n const isActive = this.tool === tool;\n return `\n <button class=\"tool-btn ${isActive ? 'active' : ''}\" data-tool=\"${tool}\" title=\"${title}\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n ${icons[iconClass]}\n </svg>\n </button>\n `;\n }\n\n // 获取样式\n private getStyles(): string {\n return `\n :host {\n display: block;\n width: 100%;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n .hidden {\n display: none !important;\n }\n\n .editor-container {\n display: flex;\n width: 100%;\n height: 100%;\n background: #f1f5f9;\n }\n\n /* 工具栏 */\n .toolbar {\n width: 64px;\n background: #ffffff;\n border-right: 1px solid #e2e8f0;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 8px;\n gap: 4px;\n }\n\n .tool-btn {\n width: 44px;\n height: 44px;\n border: none;\n background: transparent;\n border-radius: 12px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #64748b;\n transition: all 0.2s;\n }\n\n .tool-btn:hover {\n background: #f1f5f9;\n color: #4f46e5;\n }\n\n .tool-btn.active {\n background: #4f46e5;\n color: #ffffff;\n box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);\n transform: scale(1.05);\n }\n\n .tool-btn:disabled {\n color: #cbd5e1;\n cursor: not-allowed;\n }\n\n .tool-btn:disabled:hover {\n background: transparent;\n color: #cbd5e1;\n }\n\n .icon {\n width: 20px;\n height: 20px;\n }\n\n .divider {\n width: 32px;\n height: 1px;\n background: #e2e8f0;\n margin: 8px 0;\n }\n\n .spacer {\n flex: 1;\n }\n\n .color-picker {\n width: 32px;\n height: 32px;\n border: 2px solid #e2e8f0;\n border-radius: 50%;\n cursor: pointer;\n padding: 0;\n overflow: hidden;\n -webkit-appearance: none;\n }\n\n .color-picker::-webkit-color-swatch-wrapper {\n padding: 0;\n }\n\n .color-picker::-webkit-color-swatch {\n border: none;\n border-radius: 50%;\n }\n\n /* 主区域 */\n .main-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* 顶部栏 */\n .top-bar {\n height: 56px;\n background: #ffffff;\n border-bottom: 1px solid #e2e8f0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 16px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n }\n\n .top-bar-left, .top-bar-right {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .title {\n font-size: 16px;\n font-weight: 600;\n color: #334155;\n }\n\n .selection-info {\n display: none;\n align-items: center;\n gap: 8px;\n background: #eef2ff;\n padding: 4px 12px;\n border-radius: 20px;\n border: 1px solid #c7d2fe;\n }\n\n .selection-info.visible {\n display: flex;\n }\n\n .selection-label {\n font-size: 12px;\n font-weight: 600;\n color: #4f46e5;\n text-transform: uppercase;\n }\n\n .delete-btn {\n background: none;\n border: none;\n color: #ef4444;\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .delete-btn:hover {\n color: #dc2626;\n }\n\n .zoom-controls, .file-controls {\n display: flex;\n align-items: center;\n background: #f1f5f9;\n border-radius: 8px;\n padding: 4px;\n }\n\n .zoom-btn, .file-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #475569;\n transition: all 0.2s;\n }\n\n .zoom-btn:hover, .file-btn:hover {\n color: #4f46e5;\n }\n\n .zoom-text {\n padding: 4px 8px;\n font-size: 12px;\n font-weight: 500;\n color: #475569;\n background: transparent;\n border: none;\n cursor: pointer;\n min-width: 50px;\n text-align: center;\n }\n\n .zoom-text:hover {\n color: #4f46e5;\n }\n\n /* 画布容器 */\n .canvas-container {\n flex: 1;\n position: relative;\n background: #f1f5f9;\n overflow: hidden;\n }\n\n .main-canvas {\n position: absolute;\n inset: 0;\n display: block;\n cursor: crosshair;\n touch-action: none;\n }\n\n /* 小地图 */\n .minimap-wrapper {\n position: absolute;\n top: 16px;\n right: 16px;\n z-index: 30;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n padding: 2px;\n }\n\n .minimap-wrapper > * {\n background: #ffffff;\n }\n\n .minimap-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: linear-gradient(to right, #f8fafc, #f1f5f9);\n border-bottom: 1px solid #e2e8f0;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n border-radius: 10px 10px 0 0;\n }\n\n .minimap-icon {\n width: 12px;\n height: 12px;\n color: #4f46e5;\n }\n\n .minimap-canvas {\n cursor: pointer;\n background: #f8fafc;\n border-radius: 0 0 10px 10px;\n display: block;\n }\n\n .minimap-canvas:hover {\n filter: brightness(1.05);\n }\n\n /* 文本输入 */\n .text-input-container {\n position: absolute;\n z-index: 20;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n }\n\n .text-input-hint {\n background: rgba(0, 0, 0, 0.75);\n color: #fff;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n margin-bottom: 4px;\n white-space: nowrap;\n }\n\n .text-input {\n padding: 8px 12px;\n border: 2px solid #4f46e5;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n outline: none;\n min-width: 200px;\n font-size: 16px;\n }\n\n /* 空画布提示 */\n .empty-hint {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n opacity: 0.4;\n }\n\n .empty-hint h3 {\n font-size: 24px;\n font-weight: 700;\n color: #94a3b8;\n margin-bottom: 8px;\n }\n\n .empty-hint p {\n color: #94a3b8;\n }\n `;\n }\n}\n\n// 注册 Web Component\nif (typeof window !== 'undefined' && !customElements.get('canvas-drawing-editor')) {\n customElements.define('canvas-drawing-editor', CanvasDrawingEditor);\n}\n"],"names":["defaultConfig","CanvasDrawingEditor","name","oldValue","newValue","initialData","data","obj","img","err","e","rect","clientX","clientY","x","y","r","c","t","width","p","minX","pt","maxX","minY","maxY","bounds","handleSize","handles","handle","previousState","o","selectedObj","newObj","mouseX","mouseY","delta","newScale","centerX","centerY","clampedScale","mouseXBeforeZoom","mouseYBeforeZoom","newPanOffsetX","newPanOffsetY","zoomText","tool","btn","screenX","screenY","text","_a","_b","value","existingObj","screenPos","clickedObject","id","dx","dy","newX","newY","newWidth","newHeight","radius","origT","scaleFactor","origP","scaleX","scaleY","textObj","ctx","isMinimap","i","imgObj","corner","minimap","mainCanvas","minimapScale","canvasW","canvasH","offsetX","offsetY","clickX","clickY","relX","relY","canvasX","canvasY","viewportCenterX","viewportCenterY","input","file","reader","event","dataUrl","maxSize","height","ratio","imageElement","rest","json","blob","url","a","tempCanvas","tempCtx","selectionInfo","typeLabel","deleteBtn","undoBtn","emptyHint","colorPicker","imageInput","zoomInBtn","zoomOutBtn","saveJsonBtn","loadJsonInput","exportPngBtn","iconClass","title","icons"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"canvas-drawing-editor.es.js","sources":["../src/core/CanvasDrawingEditor.ts"],"sourcesContent":["/**\n * Canvas Drawing Editor - 纯 JavaScript Web Component\n * 无任何框架依赖\n */\n\n// 类型定义\nexport type ToolType = 'SELECT' | 'PENCIL' | 'RECTANGLE' | 'CIRCLE' | 'TEXT' | 'IMAGE';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface BaseObject {\n id: string;\n type: string;\n x: number;\n y: number;\n color: string;\n lineWidth: number;\n}\n\nexport interface RectObject extends BaseObject {\n type: 'RECTANGLE';\n width: number;\n height: number;\n}\n\nexport interface CircleObject extends BaseObject {\n type: 'CIRCLE';\n radius: number;\n}\n\nexport interface PathObject extends BaseObject {\n type: 'PATH';\n points: Point[];\n}\n\nexport interface TextObject extends BaseObject {\n type: 'TEXT';\n text: string;\n fontSize: number;\n}\n\nexport interface ImageObject extends BaseObject {\n type: 'IMAGE';\n width: number;\n height: number;\n dataUrl: string;\n imageElement?: HTMLImageElement;\n}\n\nexport type CanvasObject = RectObject | CircleObject | PathObject | TextObject | ImageObject;\n\nexport interface EditorConfig {\n title?: string;\n showPencil?: boolean;\n showRectangle?: boolean;\n showCircle?: boolean;\n showText?: boolean;\n showImage?: boolean;\n showZoom?: boolean;\n showDownload?: boolean;\n showExport?: boolean;\n showImport?: boolean;\n showColor?: boolean;\n showMinimap?: boolean;\n}\n\n// 默认配置\nconst defaultConfig: EditorConfig = {\n title: 'Canvas Editor',\n showPencil: true,\n showRectangle: true,\n showCircle: true,\n showText: true,\n showImage: true,\n showZoom: true,\n showDownload: true,\n showExport: true,\n showImport: true,\n showColor: true,\n showMinimap: true,\n};\n\n/**\n * Canvas Drawing Editor Web Component\n */\nexport class CanvasDrawingEditor extends HTMLElement {\n // Shadow DOM\n private shadow: ShadowRoot;\n\n // DOM 元素\n private container!: HTMLDivElement;\n private toolbar!: HTMLDivElement;\n private topBar!: HTMLDivElement;\n private canvasContainer!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx!: CanvasRenderingContext2D;\n private minimapCanvas!: HTMLCanvasElement;\n private minimapCtx!: CanvasRenderingContext2D;\n private textInput!: HTMLInputElement;\n private textInputContainer!: HTMLDivElement;\n\n // 配置\n private config: EditorConfig = { ...defaultConfig };\n\n // 状态\n private objects: CanvasObject[] = [];\n private selectedId: string | null = null;\n private tool: ToolType = 'SELECT';\n private color: string = '#000000';\n private lineWidth: number = 3;\n\n // 交互状态\n private isDragging: boolean = false;\n private dragStart: Point | null = null;\n private currentObject: CanvasObject | null = null;\n private dragOffset: Point = { x: 0, y: 0 };\n\n // 文本输入状态\n private isTextInputVisible: boolean = false;\n private textInputPos: Point = { x: 0, y: 0 };\n private textInputScreenPos: Point = { x: 0, y: 0 };\n private editingTextId: string | null = null;\n\n // 调整大小状态\n private isResizing: boolean = false;\n private resizeHandle: string | null = null;\n private resizeStartBounds: { x: number; y: number; width: number; height: number } | null = null;\n private resizeOriginalObject: CanvasObject | null = null;\n\n // 历史记录\n private history: CanvasObject[][] = [];\n private clipboard: CanvasObject | null = null;\n\n // 缩放状态\n private scale: number = 1;\n private panOffset: Point = { x: 0, y: 0 };\n\n // 平移状态\n private isPanning: boolean = false;\n private panStart: Point = { x: 0, y: 0 };\n\n // 绑定的事件处理器(用于移除监听)\n private boundHandleResize: () => void;\n private boundHandleKeyDown: (e: KeyboardEvent) => void;\n private boundHandleWheel: (e: WheelEvent) => void;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n \n // 绑定事件处理器\n this.boundHandleResize = this.handleResize.bind(this);\n this.boundHandleKeyDown = this.handleKeyDown.bind(this);\n this.boundHandleWheel = this.handleWheel.bind(this);\n }\n\n // 观察的属性\n static get observedAttributes(): string[] {\n return [\n 'title', 'show-pencil', 'show-rectangle', 'show-circle', 'show-text',\n 'show-image', 'show-zoom', 'show-download', 'show-export', 'show-import',\n 'show-color', 'show-minimap', 'initial-data'\n ];\n }\n\n // 生命周期:连接到 DOM\n connectedCallback(): void {\n this.parseAttributes();\n this.render();\n this.setupEventListeners();\n this.initCanvas(true); // 首次初始化需要加载初始数据\n }\n\n // 生命周期:从 DOM 断开\n disconnectedCallback(): void {\n this.removeEventListeners();\n }\n\n // 生命周期:属性变化\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n // 处理 initial-data 属性变化\n if (name === 'initial-data' && newValue && this.canvas) {\n this.loadInitialData();\n // 动态更新时需要手动触发渲染(画布已初始化完成)\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n this.parseAttributes();\n if (this.container) {\n this.updateUI();\n }\n }\n\n // 解析 HTML 属性\n private parseAttributes(): void {\n this.config = {\n title: this.getAttribute('title') || defaultConfig.title,\n showPencil: this.getAttribute('show-pencil') !== 'false',\n showRectangle: this.getAttribute('show-rectangle') !== 'false',\n showCircle: this.getAttribute('show-circle') !== 'false',\n showText: this.getAttribute('show-text') !== 'false',\n showImage: this.getAttribute('show-image') !== 'false',\n showZoom: this.getAttribute('show-zoom') !== 'false',\n showDownload: this.getAttribute('show-download') !== 'false',\n showExport: this.getAttribute('show-export') !== 'false',\n showImport: this.getAttribute('show-import') !== 'false',\n showColor: this.getAttribute('show-color') !== 'false',\n showMinimap: this.getAttribute('show-minimap') !== 'false',\n };\n }\n\n // 生成唯一 ID\n private generateId(): string {\n return Math.random().toString(36).substr(2, 9);\n }\n\n // 加载初始数据\n private loadInitialData(): void {\n const initialData = this.getAttribute('initial-data');\n if (!initialData) return;\n\n try {\n const data = JSON.parse(initialData);\n if (data.objects && Array.isArray(data.objects)) {\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片(异步加载完成后需要重新渲染)\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n // 更新UI状态(隐藏空画布提示等)\n this.updateUI();\n }\n } catch (err) {\n console.error('Failed to parse initial-data:', err);\n }\n }\n\n // 设置事件监听\n private setupEventListeners(): void {\n window.addEventListener('resize', this.boundHandleResize);\n window.addEventListener('keydown', this.boundHandleKeyDown);\n }\n\n // 移除事件监听\n private removeEventListeners(): void {\n window.removeEventListener('resize', this.boundHandleResize);\n window.removeEventListener('keydown', this.boundHandleKeyDown);\n if (this.canvas) {\n this.canvas.removeEventListener('wheel', this.boundHandleWheel);\n }\n }\n\n // 窗口大小变化处理\n private handleResize(): void {\n this.initCanvas(false);\n }\n\n // 初始化画布\n private initCanvas(loadInitial: boolean = false): void {\n if (!this.canvasContainer || !this.canvas) return;\n\n // 使用 requestAnimationFrame 确保 DOM 已经渲染\n requestAnimationFrame(() => {\n this.canvas.width = this.canvasContainer.clientWidth;\n this.canvas.height = this.canvasContainer.clientHeight;\n\n // 首次初始化时加载初始数据(确保画布尺寸已设置)\n if (loadInitial) {\n this.loadInitialData();\n }\n\n this.renderCanvas();\n this.renderMinimap();\n });\n }\n\n // 获取鼠标在画布上的位置(考虑缩放和平移)\n private getMousePos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n const x = (clientX - rect.left - this.panOffset.x) / this.scale;\n const y = (clientY - rect.top - this.panOffset.y) / this.scale;\n return { x, y };\n }\n\n // 获取屏幕坐标(不考虑缩放和平移)\n private getScreenPos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n return { x: clientX - rect.left, y: clientY - rect.top };\n }\n\n // 获取对象边界\n private getObjectBounds(obj: CanvasObject): { x: number; y: number; width: number; height: number } {\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE': {\n const r = obj as RectObject | ImageObject;\n return { x: r.x, y: r.y, width: r.width, height: r.height };\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n return { x: c.x - c.radius, y: c.y - c.radius, width: c.radius * 2, height: c.radius * 2 };\n }\n case 'TEXT': {\n const t = obj as TextObject;\n const width = t.text.length * t.fontSize * 0.6;\n return { x: t.x, y: t.y - t.fontSize, width, height: t.fontSize };\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return { x: 0, y: 0, width: 0, height: 0 };\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n }\n return { x: 0, y: 0, width: 0, height: 0 };\n }\n\n // 检查调整大小手柄\n private getResizeHandleAtPoint(obj: CanvasObject, x: number, y: number): string | null {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n const handles = [\n { name: 'nw', x: bounds.x, y: bounds.y },\n { name: 'ne', x: bounds.x + bounds.width, y: bounds.y },\n { name: 'sw', x: bounds.x, y: bounds.y + bounds.height },\n { name: 'se', x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n for (const handle of handles) {\n if (Math.abs(x - handle.x) <= handleSize && Math.abs(y - handle.y) <= handleSize) {\n return handle.name;\n }\n }\n return null;\n }\n\n // 碰撞检测\n private isHit(obj: CanvasObject, x: number, y: number): boolean {\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n return x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n const dist = Math.sqrt(Math.pow(x - c.x, 2) + Math.pow(y - c.y, 2));\n return dist <= c.radius;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n return x >= img.x && x <= img.x + img.width && y >= img.y && y <= img.y + img.height;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n return x >= t.x && x <= t.x + (t.text.length * t.fontSize * 0.6) && y >= t.y - t.fontSize && y <= t.y;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return false;\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return x >= minX && x <= maxX && y >= minY && y <= maxY;\n }\n }\n return false;\n }\n\n // 保存历史\n private saveHistory(): void {\n this.history.push(JSON.parse(JSON.stringify(this.objects)));\n }\n\n // 撤销\n private undo(): void {\n if (this.history.length === 0) return;\n const previousState = this.history.pop();\n if (previousState) {\n this.objects = previousState;\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.dispatchChangeEvent();\n }\n }\n\n // 删除选中对象\n private deleteSelected(): void {\n if (this.selectedId) {\n this.saveHistory();\n this.objects = this.objects.filter(o => o.id !== this.selectedId);\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 复制选中对象\n private copySelected(): void {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.clipboard = JSON.parse(JSON.stringify(selectedObj));\n }\n }\n }\n\n // 粘贴对象\n private pasteObject(): void {\n if (this.clipboard) {\n this.saveHistory();\n const newObj = {\n ...JSON.parse(JSON.stringify(this.clipboard)),\n id: this.generateId(),\n x: this.clipboard.x + 20,\n y: this.clipboard.y + 20\n };\n if (newObj.type === 'PATH' && newObj.points) {\n newObj.points = newObj.points.map((pt: Point) => ({\n x: pt.x + 20,\n y: pt.y + 20\n }));\n }\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.clipboard = newObj;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 派发变化事件\n private dispatchChangeEvent(): void {\n this.dispatchEvent(new CustomEvent('editor-change', {\n bubbles: true,\n composed: true,\n detail: { objects: this.objects }\n }));\n }\n\n // 键盘事件处理\n private handleKeyDown(e: KeyboardEvent): void {\n if (this.isTextInputVisible) return;\n\n // Ctrl+Z: 撤销\n if ((e.ctrlKey || e.metaKey) && e.key === 'z') {\n e.preventDefault();\n this.undo();\n return;\n }\n\n // Ctrl+C: 复制\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n if (this.selectedId) {\n e.preventDefault();\n this.copySelected();\n }\n return;\n }\n\n // Ctrl+V: 粘贴\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n if (this.clipboard) {\n e.preventDefault();\n this.pasteObject();\n }\n return;\n }\n\n // Delete/Backspace: 删除\n if ((e.key === 'Delete' || e.key === 'Backspace') && this.selectedId) {\n e.preventDefault();\n this.deleteSelected();\n return;\n }\n\n // 快捷键切换工具\n if (!e.ctrlKey && !e.metaKey) {\n switch (e.key.toLowerCase()) {\n case 'v':\n this.setTool('SELECT');\n break;\n case 'p':\n case 'b':\n this.setTool('PENCIL');\n break;\n case 'r':\n this.setTool('RECTANGLE');\n break;\n case 'o':\n this.setTool('CIRCLE');\n break;\n case 't':\n this.setTool('TEXT');\n break;\n case 'escape':\n this.selectedId = null;\n this.hideTextInput();\n this.renderCanvas();\n this.updateUI();\n break;\n }\n }\n }\n\n // 滚轮缩放\n private handleWheel(e: WheelEvent): void {\n e.preventDefault();\n const rect = this.canvas.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n const delta = e.deltaY > 0 ? 0.9 : 1.1;\n const newScale = this.scale * delta;\n\n this.zoomAtPoint(newScale, mouseX, mouseY);\n }\n\n // 以指定点为中心缩放\n private zoomAtPoint(newScale: number, centerX: number, centerY: number): void {\n const clampedScale = Math.min(Math.max(newScale, 0.2), 5);\n\n const mouseXBeforeZoom = (centerX - this.panOffset.x) / this.scale;\n const mouseYBeforeZoom = (centerY - this.panOffset.y) / this.scale;\n\n const newPanOffsetX = centerX - mouseXBeforeZoom * clampedScale;\n const newPanOffsetY = centerY - mouseYBeforeZoom * clampedScale;\n\n this.scale = clampedScale;\n this.panOffset = { x: newPanOffsetX, y: newPanOffsetY };\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 放大\n private zoomIn(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale * 1.2, centerX, centerY);\n }\n\n // 缩小\n private zoomOut(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale / 1.2, centerX, centerY);\n }\n\n // 重置缩放\n private resetZoom(): void {\n this.scale = 1;\n this.panOffset = { x: 0, y: 0 };\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 更新缩放显示\n private updateZoomDisplay(): void {\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomText) {\n zoomText.textContent = `${Math.round(this.scale * 100)}%`;\n }\n }\n\n // 设置工具\n private setTool(tool: ToolType): void {\n this.tool = tool;\n this.updateToolButtons();\n }\n\n // 更新工具按钮状态\n private updateToolButtons(): void {\n const buttons = this.shadow.querySelectorAll('.tool-btn');\n buttons.forEach(btn => {\n const btnTool = btn.getAttribute('data-tool');\n if (btnTool === this.tool) {\n btn.classList.add('active');\n } else {\n btn.classList.remove('active');\n }\n });\n }\n\n // 隐藏文本输入\n private hideTextInput(): void {\n this.isTextInputVisible = false;\n if (this.textInputContainer) {\n this.textInputContainer.style.display = 'none';\n }\n this.editingTextId = null;\n }\n\n // 显示文本输入\n private showTextInput(screenX: number, screenY: number, text: string = ''): void {\n this.isTextInputVisible = true;\n this.textInputScreenPos = { x: screenX, y: screenY };\n\n if (this.textInputContainer && this.textInput) {\n this.textInputContainer.style.display = 'block';\n this.textInputContainer.style.left = `${screenX}px`;\n this.textInputContainer.style.top = `${screenY - 30}px`;\n this.textInput.value = text;\n this.textInput.style.color = this.color;\n setTimeout(() => {\n this.textInput.focus();\n if (text) this.textInput.select();\n }, 0);\n }\n }\n\n // 提交文本\n private submitText(): void {\n const value = this.textInput?.value?.trim();\n if (value) {\n if (this.editingTextId) {\n const existingObj = this.objects.find(o => o.id === this.editingTextId) as TextObject | undefined;\n if (existingObj && existingObj.text !== value) {\n this.saveHistory();\n existingObj.text = value;\n }\n this.selectedId = this.editingTextId;\n } else {\n this.saveHistory();\n const newObj: TextObject = {\n id: this.generateId(),\n type: 'TEXT',\n x: this.textInputPos.x,\n y: this.textInputPos.y,\n text: value,\n fontSize: 24,\n color: this.color,\n lineWidth: this.lineWidth\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n }\n this.dispatchChangeEvent();\n }\n this.hideTextInput();\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 画布鼠标按下\n private handleCanvasPointerDown(e: MouseEvent | TouchEvent): void {\n const { x, y } = this.getMousePos(e);\n const screenPos = this.getScreenPos(e);\n this.dragStart = { x, y };\n this.isDragging = true;\n\n // 如果文本输入可见且不是文本工具,先保存文本\n if (this.isTextInputVisible && this.tool !== 'TEXT') {\n this.submitText();\n }\n\n if (this.tool === 'SELECT') {\n // 检查是否点击调整大小手柄\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const handle = this.getResizeHandleAtPoint(selectedObj, x, y);\n if (handle) {\n this.saveHistory();\n this.isResizing = true;\n this.resizeHandle = handle;\n this.resizeStartBounds = this.getObjectBounds(selectedObj);\n this.resizeOriginalObject = JSON.parse(JSON.stringify(selectedObj));\n return;\n }\n }\n }\n\n // 查找点击的对象\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject) {\n this.selectedId = clickedObject.id;\n this.dragOffset = { x: x - clickedObject.x, y: y - clickedObject.y };\n this.saveHistory();\n this.updateUI();\n } else {\n // 开始拖拽画布\n this.selectedId = null;\n this.isPanning = true;\n this.panStart = screenPos;\n this.updateUI();\n }\n } else if (this.tool === 'TEXT') {\n // 显示文本输入\n this.textInputPos = { x, y };\n this.showTextInput(screenPos.x, screenPos.y);\n this.isDragging = false;\n } else {\n // 开始绘制图形\n this.saveHistory();\n const id = this.generateId();\n if (this.tool === 'RECTANGLE') {\n this.currentObject = { id, type: 'RECTANGLE', x, y, width: 0, height: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'CIRCLE') {\n this.currentObject = { id, type: 'CIRCLE', x, y, radius: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'PENCIL') {\n this.currentObject = { id, type: 'PATH', x, y, points: [{ x, y }], color: this.color, lineWidth: this.lineWidth };\n }\n }\n\n this.renderCanvas();\n }\n\n // 画布鼠标移动\n private handleCanvasPointerMove(e: MouseEvent | TouchEvent): void {\n // 处理画布拖拽\n if (this.isPanning) {\n const screenPos = this.getScreenPos(e);\n const dx = screenPos.x - this.panStart.x;\n const dy = screenPos.y - this.panStart.y;\n this.panOffset = { x: this.panOffset.x + dx, y: this.panOffset.y + dy };\n this.panStart = screenPos;\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n if (!this.isDragging || !this.dragStart) return;\n const { x, y } = this.getMousePos(e);\n\n // 处理调整大小\n if (this.isResizing && this.selectedId && this.resizeHandle && this.resizeStartBounds && this.resizeOriginalObject) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (!obj) return;\n\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n let newX = this.resizeStartBounds.x;\n let newY = this.resizeStartBounds.y;\n let newWidth = this.resizeStartBounds.width;\n let newHeight = this.resizeStartBounds.height;\n\n if (this.resizeHandle.includes('e')) newWidth = this.resizeStartBounds.width + dx;\n if (this.resizeHandle.includes('w')) {\n newX = this.resizeStartBounds.x + dx;\n newWidth = this.resizeStartBounds.width - dx;\n }\n if (this.resizeHandle.includes('s')) newHeight = this.resizeStartBounds.height + dy;\n if (this.resizeHandle.includes('n')) {\n newY = this.resizeStartBounds.y + dy;\n newHeight = this.resizeStartBounds.height - dy;\n }\n\n newWidth = Math.max(10, newWidth);\n newHeight = Math.max(10, newHeight);\n\n // 根据对象类型应用变化\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE':\n (obj as RectObject | ImageObject).x = newX;\n (obj as RectObject | ImageObject).y = newY;\n (obj as RectObject | ImageObject).width = newWidth;\n (obj as RectObject | ImageObject).height = newHeight;\n break;\n case 'CIRCLE': {\n const radius = Math.max(newWidth, newHeight) / 2;\n (obj as CircleObject).x = newX + radius;\n (obj as CircleObject).y = newY + radius;\n (obj as CircleObject).radius = radius;\n break;\n }\n case 'TEXT': {\n const origT = this.resizeOriginalObject as TextObject;\n const scaleFactor = newWidth / this.resizeStartBounds.width;\n (obj as TextObject).x = newX;\n (obj as TextObject).y = newY + newHeight;\n (obj as TextObject).fontSize = Math.max(8, Math.round(origT.fontSize * scaleFactor));\n break;\n }\n case 'PATH': {\n const origP = this.resizeOriginalObject as PathObject;\n const scaleX = newWidth / this.resizeStartBounds.width;\n const scaleY = newHeight / this.resizeStartBounds.height;\n (obj as PathObject).points = origP.points.map(pt => ({\n x: newX + (pt.x - this.resizeStartBounds!.x) * scaleX,\n y: newY + (pt.y - this.resizeStartBounds!.y) * scaleY\n }));\n break;\n }\n }\n\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n // 移动选中对象\n if (this.tool === 'SELECT' && this.selectedId) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (obj) {\n if (obj.type === 'PATH') {\n const p = obj as PathObject;\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n p.points = p.points.map(pt => ({ x: pt.x + dx, y: pt.y + dy }));\n this.dragStart = { x, y };\n } else {\n obj.x = x - this.dragOffset.x;\n obj.y = y - this.dragOffset.y;\n }\n this.renderCanvas();\n this.renderMinimap();\n }\n } else if (this.currentObject) {\n // 更新正在绘制的图形\n if (this.currentObject.type === 'RECTANGLE') {\n (this.currentObject as RectObject).width = x - this.currentObject.x;\n (this.currentObject as RectObject).height = y - this.currentObject.y;\n } else if (this.currentObject.type === 'CIRCLE') {\n const radius = Math.sqrt(Math.pow(x - this.currentObject.x, 2) + Math.pow(y - this.currentObject.y, 2));\n (this.currentObject as CircleObject).radius = radius;\n } else if (this.currentObject.type === 'PATH') {\n (this.currentObject as PathObject).points.push({ x, y });\n }\n this.renderCanvas();\n }\n }\n\n // 画布鼠标抬起\n private handleCanvasPointerUp(): void {\n this.isDragging = false;\n this.dragStart = null;\n this.isResizing = false;\n this.resizeHandle = null;\n this.resizeStartBounds = null;\n this.resizeOriginalObject = null;\n this.isPanning = false;\n\n if (this.currentObject) {\n this.objects.push(this.currentObject);\n this.currentObject = null;\n this.dispatchChangeEvent();\n }\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 双击编辑文本\n private handleCanvasDoubleClick(e: MouseEvent): void {\n e.preventDefault();\n const { x, y } = this.getMousePos(e);\n\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject && clickedObject.type === 'TEXT') {\n const textObj = clickedObject as TextObject;\n this.editingTextId = textObj.id;\n this.textInputPos = { x: textObj.x, y: textObj.y };\n const screenX = textObj.x * this.scale + this.panOffset.x;\n const screenY = textObj.y * this.scale + this.panOffset.y;\n this.showTextInput(screenX, screenY, textObj.text);\n this.setTool('SELECT');\n }\n }\n\n // 渲染画布\n private renderCanvas(): void {\n if (!this.ctx) return;\n\n // 清空画布\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 绘制背景\n this.ctx.fillStyle = '#ffffff';\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 应用缩放和平移\n this.ctx.save();\n this.ctx.translate(this.panOffset.x, this.panOffset.y);\n this.ctx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(this.ctx, obj, false));\n\n // 绘制正在创建的对象\n if (this.currentObject) {\n this.drawObject(this.ctx, this.currentObject, false);\n }\n\n // 绘制选中对象的调整手柄\n if (this.selectedId && this.tool === 'SELECT') {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.drawSelectionHandles(this.ctx, selectedObj);\n }\n }\n\n this.ctx.restore();\n }\n\n // 绘制单个对象\n private drawObject(ctx: CanvasRenderingContext2D, obj: CanvasObject, isMinimap: boolean): void {\n ctx.beginPath();\n ctx.strokeStyle = obj.color;\n ctx.lineWidth = obj.lineWidth;\n ctx.fillStyle = obj.color;\n\n // 选中高亮(仅主画布)\n if (!isMinimap && obj.id === this.selectedId) {\n ctx.shadowColor = 'rgba(0, 100, 255, 0.5)';\n ctx.shadowBlur = 10;\n } else {\n ctx.shadowBlur = 0;\n }\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n ctx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n ctx.beginPath();\n ctx.arc(c.x, c.y, c.radius, 0, 2 * Math.PI);\n ctx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length < 2) break;\n ctx.beginPath();\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(p.points[0].x, p.points[0].y);\n for (let i = 1; i < p.points.length; i++) {\n ctx.lineTo(p.points[i].x, p.points[i].y);\n }\n ctx.stroke();\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n ctx.font = `${t.fontSize}px sans-serif`;\n ctx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const imgObj = obj as ImageObject;\n if (imgObj.imageElement && imgObj.imageElement.complete) {\n ctx.drawImage(imgObj.imageElement, imgObj.x, imgObj.y, imgObj.width, imgObj.height);\n } else if (imgObj.dataUrl) {\n // 加载图片\n const img = new Image();\n img.onload = () => {\n imgObj.imageElement = img;\n this.renderCanvas();\n };\n img.src = imgObj.dataUrl;\n }\n break;\n }\n }\n }\n\n // 绘制选中手柄\n private drawSelectionHandles(ctx: CanvasRenderingContext2D, obj: CanvasObject): void {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n ctx.shadowBlur = 0;\n ctx.fillStyle = '#3b82f6';\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n\n // 绘制角落手柄\n const corners = [\n { x: bounds.x, y: bounds.y },\n { x: bounds.x + bounds.width, y: bounds.y },\n { x: bounds.x, y: bounds.y + bounds.height },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n corners.forEach(corner => {\n ctx.beginPath();\n ctx.rect(corner.x - handleSize / 2, corner.y - handleSize / 2, handleSize, handleSize);\n ctx.fill();\n ctx.stroke();\n });\n\n // 绘制选择边框\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 1;\n ctx.setLineDash([5, 5]);\n ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);\n ctx.setLineDash([]);\n }\n\n // 渲染小地图\n private renderMinimap(): void {\n if (!this.minimapCtx || !this.config.showMinimap) return;\n\n const minimap = this.minimapCanvas;\n const mainCanvas = this.canvas;\n\n this.minimapCtx.clearRect(0, 0, minimap.width, minimap.height);\n\n // 计算缩放比例\n const scaleX = minimap.width / mainCanvas.width;\n const scaleY = minimap.height / mainCanvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = mainCanvas.width * minimapScale;\n const canvasH = mainCanvas.height * minimapScale;\n const offsetX = (minimap.width - canvasW) / 2;\n const offsetY = (minimap.height - canvasH) / 2;\n\n // 绘制画布区域背景\n this.minimapCtx.fillStyle = '#ffffff';\n this.minimapCtx.fillRect(offsetX, offsetY, canvasW, canvasH);\n\n // 只应用小地图的缩放变换,不应用主画布的 panOffset 和 scale\n // 小地图应该始终显示画布内容的全貌\n this.minimapCtx.save();\n this.minimapCtx.translate(offsetX, offsetY);\n this.minimapCtx.scale(minimapScale, minimapScale);\n\n // 绘制所有对象\n const allObjects = this.currentObject ? [...this.objects, this.currentObject] : this.objects;\n allObjects.forEach(obj => {\n this.minimapCtx.fillStyle = obj.color;\n this.minimapCtx.strokeStyle = obj.color;\n this.minimapCtx.lineWidth = Math.max(obj.lineWidth, 1);\n this.minimapCtx.setLineDash([]);\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n this.minimapCtx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n this.minimapCtx.beginPath();\n this.minimapCtx.arc(c.x, c.y, c.radius, 0, Math.PI * 2);\n this.minimapCtx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length > 0) {\n this.minimapCtx.beginPath();\n this.minimapCtx.lineCap = 'round';\n this.minimapCtx.lineJoin = 'round';\n this.minimapCtx.moveTo(p.points[0].x, p.points[0].y);\n p.points.forEach(pt => this.minimapCtx.lineTo(pt.x, pt.y));\n this.minimapCtx.stroke();\n }\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n this.minimapCtx.font = `${t.fontSize}px sans-serif`;\n this.minimapCtx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n if (img.imageElement) {\n this.minimapCtx.drawImage(img.imageElement, img.x, img.y, img.width, img.height);\n }\n break;\n }\n }\n });\n\n this.minimapCtx.restore();\n\n // 绘制画布边框\n this.minimapCtx.strokeStyle = '#94a3b8';\n this.minimapCtx.lineWidth = 1;\n this.minimapCtx.strokeRect(offsetX, offsetY, canvasW, canvasH);\n }\n\n // 小地图点击定位\n private handleMinimapClick(e: MouseEvent): void {\n const rect = this.minimapCanvas.getBoundingClientRect();\n const clickX = e.clientX - rect.left;\n const clickY = e.clientY - rect.top;\n\n const scaleX = this.minimapCanvas.width / this.canvas.width;\n const scaleY = this.minimapCanvas.height / this.canvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = this.canvas.width * minimapScale;\n const canvasH = this.canvas.height * minimapScale;\n const offsetX = (this.minimapCanvas.width - canvasW) / 2;\n const offsetY = (this.minimapCanvas.height - canvasH) / 2;\n\n const relX = clickX - offsetX;\n const relY = clickY - offsetY;\n\n const canvasX = (relX / minimapScale - this.panOffset.x) / this.scale;\n const canvasY = (relY / minimapScale - this.panOffset.y) / this.scale;\n\n const viewportCenterX = this.canvas.width / 2;\n const viewportCenterY = this.canvas.height / 2;\n\n this.panOffset = {\n x: viewportCenterX / this.scale - canvasX,\n y: viewportCenterY / this.scale - canvasY\n };\n\n this.renderCanvas();\n this.renderMinimap();\n }\n\n // 图片上传处理\n private handleImageUpload(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n const dataUrl = event.target?.result as string;\n const img = new Image();\n img.onload = () => {\n this.saveHistory();\n const maxSize = 300;\n let width = img.width;\n let height = img.height;\n if (width > maxSize || height > maxSize) {\n const ratio = Math.min(maxSize / width, maxSize / height);\n width *= ratio;\n height *= ratio;\n }\n\n const newObj: ImageObject = {\n id: this.generateId(),\n type: 'IMAGE',\n x: 100,\n y: 100,\n width,\n height,\n color: '#000000',\n lineWidth: 1,\n dataUrl,\n imageElement: img\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n };\n img.src = dataUrl;\n };\n\n reader.readAsDataURL(file);\n input.value = '';\n }\n\n // 保存 JSON\n private saveJson(): void {\n const data = {\n version: '1.0',\n objects: this.objects.map(obj => {\n const { imageElement, ...rest } = obj as ImageObject;\n return rest;\n })\n };\n const json = JSON.stringify(data, null, 2);\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-project.json';\n a.click();\n URL.revokeObjectURL(url);\n }\n\n // 加载 JSON\n private loadJson(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n try {\n const data = JSON.parse(event.target?.result as string);\n if (data.objects && Array.isArray(data.objects)) {\n this.saveHistory();\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n } catch (err) {\n console.error('Failed to load JSON:', err);\n }\n };\n\n reader.readAsText(file);\n input.value = '';\n }\n\n // 导出 PNG\n private exportPng(): void {\n // 创建临时画布\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = this.canvas.width;\n tempCanvas.height = this.canvas.height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n // 绘制白色背景\n tempCtx.fillStyle = '#ffffff';\n tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);\n\n // 应用缩放和平移\n tempCtx.translate(this.panOffset.x, this.panOffset.y);\n tempCtx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(tempCtx, obj, true));\n\n // 下载\n const url = tempCanvas.toDataURL('image/png');\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-export.png';\n a.click();\n }\n\n // 更新 UI\n private updateUI(): void {\n // 更新选中状态显示\n const selectionInfo = this.shadow.querySelector('.selection-info');\n if (selectionInfo) {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const typeLabels: Record<string, string> = {\n 'RECTANGLE': '矩形',\n 'CIRCLE': '圆形',\n 'PATH': '画笔',\n 'TEXT': '文本',\n 'IMAGE': '图片'\n };\n const typeLabel = typeLabels[selectedObj.type] || selectedObj.type;\n selectionInfo.innerHTML = `\n <span class=\"selection-label\">已选择: ${typeLabel}</span>\n <button class=\"delete-btn\" title=\"删除\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\"/>\n </svg>\n </button>\n `;\n selectionInfo.classList.add('visible');\n const deleteBtn = selectionInfo.querySelector('.delete-btn');\n if (deleteBtn) {\n deleteBtn.addEventListener('click', () => this.deleteSelected());\n }\n }\n } else {\n selectionInfo.classList.remove('visible');\n selectionInfo.innerHTML = '';\n }\n }\n\n // 更新撤销按钮状态\n const undoBtn = this.shadow.querySelector('.undo-btn') as HTMLButtonElement;\n if (undoBtn) {\n undoBtn.disabled = this.history.length === 0;\n }\n\n // 更新空画布提示显示\n const emptyHint = this.shadow.querySelector('.empty-hint') as HTMLElement;\n if (emptyHint) {\n emptyHint.style.display = this.objects.length === 0 ? 'flex' : 'none';\n }\n }\n\n // 渲染 DOM 结构\n private render(): void {\n this.shadow.innerHTML = `\n <style>${this.getStyles()}</style>\n <div class=\"editor-container\">\n <!-- 左侧工具栏 -->\n <div class=\"toolbar\">\n ${this.createToolButton('SELECT', 'select-icon', '选择 (V)')}\n <div class=\"divider\"></div>\n ${this.config.showPencil ? this.createToolButton('PENCIL', 'pencil-icon', '画笔 (P)') : ''}\n ${this.config.showRectangle ? this.createToolButton('RECTANGLE', 'rect-icon', '矩形 (R)') : ''}\n ${this.config.showCircle ? this.createToolButton('CIRCLE', 'circle-icon', '圆形 (O)') : ''}\n ${this.config.showText ? this.createToolButton('TEXT', 'text-icon', '文本 (T)') : ''}\n ${this.config.showImage ? `\n <label class=\"tool-btn\" title=\"插入图片\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/>\n <path d=\"M21 15l-5-5L5 21\"/>\n </svg>\n <input type=\"file\" accept=\"image/*\" class=\"hidden image-input\" />\n </label>\n ` : ''}\n <div class=\"divider\"></div>\n <button class=\"tool-btn undo-btn\" title=\"撤销 (Ctrl+Z)\" disabled>\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M1 4v6h6\"/>\n <path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>\n </svg>\n </button>\n <div class=\"spacer\"></div>\n ${this.config.showColor ? `\n <input type=\"color\" class=\"color-picker\" value=\"${this.color}\" title=\"颜色\" />\n ` : ''}\n </div>\n\n <!-- 主区域 -->\n <div class=\"main-area\">\n <!-- 顶部栏 -->\n <div class=\"top-bar\">\n <div class=\"top-bar-left\">\n <h2 class=\"title\">${this.config.title}</h2>\n <div class=\"selection-info\"></div>\n </div>\n <div class=\"top-bar-right\">\n ${this.config.showZoom ? `\n <div class=\"zoom-controls\">\n <button class=\"zoom-btn zoom-out-btn\" title=\"缩小\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M8 11h6\"/>\n </svg>\n </button>\n <button class=\"zoom-text\" title=\"重置缩放\">100%</button>\n <button class=\"zoom-btn zoom-in-btn\" title=\"放大\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M11 8v6M8 11h6\"/>\n </svg>\n </button>\n </div>\n ` : ''}\n ${(this.config.showExport || this.config.showImport || this.config.showDownload) ? `\n <div class=\"file-controls\">\n ${this.config.showExport ? `\n <button class=\"file-btn save-json-btn\" title=\"保存项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z\"/>\n <polyline points=\"17 21 17 13 7 13 7 21\"/><polyline points=\"7 3 7 8 15 8\"/>\n </svg>\n </button>\n ` : ''}\n ${this.config.showImport ? `\n <label class=\"file-btn\" title=\"加载项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z\"/>\n </svg>\n <input type=\"file\" accept=\".json\" class=\"hidden load-json-input\" />\n </label>\n ` : ''}\n ${this.config.showDownload ? `\n <button class=\"file-btn export-png-btn\" title=\"导出 PNG\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3\"/>\n </svg>\n </button>\n ` : ''}\n </div>\n ` : ''}\n </div>\n </div>\n\n <!-- 画布容器 -->\n <div class=\"canvas-container\">\n <canvas class=\"main-canvas\"></canvas>\n\n ${this.config.showMinimap ? `\n <div class=\"minimap-wrapper\">\n <div class=\"minimap-header\">\n <svg class=\"minimap-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M3 9h18M9 21V9\"/>\n </svg>\n <span>导航</span>\n </div>\n <canvas class=\"minimap-canvas\" width=\"220\" height=\"140\"></canvas>\n </div>\n ` : ''}\n\n <!-- 文本输入 -->\n <div class=\"text-input-container\" style=\"display: none;\">\n <div class=\"text-input-hint\">按 Enter 确认,Esc 取消</div>\n <input type=\"text\" class=\"text-input\" placeholder=\"输入文本...\" />\n </div>\n\n <!-- 空画布提示 -->\n <div class=\"empty-hint\" style=\"display: ${this.getAttribute('initial-data') ? 'none' : 'flex'};\">\n <h3>开始创作</h3>\n <p>选择左侧的工具开始绘制</p>\n </div>\n </div>\n </div>\n </div>\n `;\n\n // 获取 DOM 引用\n this.container = this.shadow.querySelector('.editor-container')!;\n this.toolbar = this.shadow.querySelector('.toolbar')!;\n this.topBar = this.shadow.querySelector('.top-bar')!;\n this.canvasContainer = this.shadow.querySelector('.canvas-container')!;\n this.canvas = this.shadow.querySelector('.main-canvas')!;\n this.ctx = this.canvas.getContext('2d')!;\n\n if (this.config.showMinimap) {\n this.minimapCanvas = this.shadow.querySelector('.minimap-canvas')!;\n this.minimapCtx = this.minimapCanvas.getContext('2d')!;\n }\n\n this.textInputContainer = this.shadow.querySelector('.text-input-container')!;\n this.textInput = this.shadow.querySelector('.text-input')!;\n\n // 绑定事件\n this.bindEvents();\n }\n\n // 绑定事件\n private bindEvents(): void {\n // 画布事件\n this.canvas.addEventListener('mousedown', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('mousemove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('mouseup', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('mouseleave', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('dblclick', (e) => this.handleCanvasDoubleClick(e));\n this.canvas.addEventListener('touchstart', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('touchmove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('touchend', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('wheel', this.boundHandleWheel, { passive: false });\n\n // 工具按钮\n this.shadow.querySelectorAll('.tool-btn[data-tool]').forEach(btn => {\n btn.addEventListener('mousedown', (e) => {\n // 阻止 blur 事件触发,在点击后手动处理\n e.preventDefault();\n });\n btn.addEventListener('click', () => {\n // 如果有文本输入,先提交\n if (this.isTextInputVisible) {\n this.submitText();\n }\n const tool = btn.getAttribute('data-tool') as ToolType;\n this.setTool(tool);\n });\n });\n\n // 撤销按钮\n const undoBtn = this.shadow.querySelector('.undo-btn');\n if (undoBtn) {\n undoBtn.addEventListener('click', () => this.undo());\n }\n\n // 颜色选择器\n const colorPicker = this.shadow.querySelector('.color-picker') as HTMLInputElement;\n if (colorPicker) {\n colorPicker.addEventListener('input', (e) => {\n this.color = (e.target as HTMLInputElement).value;\n });\n }\n\n // 图片上传\n const imageInput = this.shadow.querySelector('.image-input');\n if (imageInput) {\n imageInput.addEventListener('change', (e) => this.handleImageUpload(e));\n }\n\n // 缩放按钮\n const zoomInBtn = this.shadow.querySelector('.zoom-in-btn');\n const zoomOutBtn = this.shadow.querySelector('.zoom-out-btn');\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomInBtn) zoomInBtn.addEventListener('click', () => this.zoomIn());\n if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => this.zoomOut());\n if (zoomText) zoomText.addEventListener('click', () => this.resetZoom());\n\n // 文件操作\n const saveJsonBtn = this.shadow.querySelector('.save-json-btn');\n const loadJsonInput = this.shadow.querySelector('.load-json-input');\n const exportPngBtn = this.shadow.querySelector('.export-png-btn');\n if (saveJsonBtn) saveJsonBtn.addEventListener('click', () => this.saveJson());\n if (loadJsonInput) loadJsonInput.addEventListener('change', (e) => this.loadJson(e));\n if (exportPngBtn) exportPngBtn.addEventListener('click', () => this.exportPng());\n\n // 小地图点击\n if (this.minimapCanvas) {\n this.minimapCanvas.addEventListener('click', (e) => this.handleMinimapClick(e));\n }\n\n // 文本输入\n if (this.textInput) {\n this.textInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.submitText();\n } else if (e.key === 'Escape') {\n this.hideTextInput();\n }\n });\n this.textInput.addEventListener('blur', () => {\n if (this.isTextInputVisible) {\n this.submitText();\n }\n });\n }\n }\n\n // 创建工具按钮 HTML\n private createToolButton(tool: ToolType, iconClass: string, title: string): string {\n const icons: Record<string, string> = {\n 'select-icon': '<path d=\"M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z\"/><path d=\"M13 13l6 6\"/>',\n 'pencil-icon': '<path d=\"M17 3a2.85 2.85 0 114 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>',\n 'rect-icon': '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>',\n 'circle-icon': '<circle cx=\"12\" cy=\"12\" r=\"10\"/>',\n 'text-icon': '<path d=\"M4 7V4h16v3M9 20h6M12 4v16\"/>',\n };\n const isActive = this.tool === tool;\n return `\n <button class=\"tool-btn ${isActive ? 'active' : ''}\" data-tool=\"${tool}\" title=\"${title}\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n ${icons[iconClass]}\n </svg>\n </button>\n `;\n }\n\n // 获取样式\n private getStyles(): string {\n return `\n :host {\n display: block;\n width: 100%;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n .hidden {\n display: none !important;\n }\n\n .editor-container {\n display: flex;\n width: 100%;\n height: 100%;\n background: #f1f5f9;\n }\n\n /* 工具栏 */\n .toolbar {\n width: 64px;\n background: #ffffff;\n border-right: 1px solid #e2e8f0;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 8px;\n gap: 4px;\n }\n\n .tool-btn {\n width: 44px;\n height: 44px;\n border: none;\n background: transparent;\n border-radius: 12px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #64748b;\n transition: all 0.2s;\n }\n\n .tool-btn:hover {\n background: #f1f5f9;\n color: #4f46e5;\n }\n\n .tool-btn.active {\n background: #4f46e5;\n color: #ffffff;\n box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);\n transform: scale(1.05);\n }\n\n .tool-btn:disabled {\n color: #cbd5e1;\n cursor: not-allowed;\n }\n\n .tool-btn:disabled:hover {\n background: transparent;\n color: #cbd5e1;\n }\n\n .icon {\n width: 20px;\n height: 20px;\n }\n\n .divider {\n width: 32px;\n height: 1px;\n background: #e2e8f0;\n margin: 8px 0;\n }\n\n .spacer {\n flex: 1;\n }\n\n .color-picker {\n width: 32px;\n height: 32px;\n border: 2px solid #e2e8f0;\n border-radius: 50%;\n cursor: pointer;\n padding: 0;\n overflow: hidden;\n -webkit-appearance: none;\n }\n\n .color-picker::-webkit-color-swatch-wrapper {\n padding: 0;\n }\n\n .color-picker::-webkit-color-swatch {\n border: none;\n border-radius: 50%;\n }\n\n /* 主区域 */\n .main-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* 顶部栏 */\n .top-bar {\n height: 56px;\n background: #ffffff;\n border-bottom: 1px solid #e2e8f0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 16px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n }\n\n .top-bar-left, .top-bar-right {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .title {\n font-size: 16px;\n font-weight: 600;\n color: #334155;\n }\n\n .selection-info {\n display: none;\n align-items: center;\n gap: 8px;\n background: #eef2ff;\n padding: 4px 12px;\n border-radius: 20px;\n border: 1px solid #c7d2fe;\n }\n\n .selection-info.visible {\n display: flex;\n }\n\n .selection-label {\n font-size: 12px;\n font-weight: 600;\n color: #4f46e5;\n text-transform: uppercase;\n }\n\n .delete-btn {\n background: none;\n border: none;\n color: #ef4444;\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .delete-btn:hover {\n color: #dc2626;\n }\n\n .zoom-controls, .file-controls {\n display: flex;\n align-items: center;\n background: #f1f5f9;\n border-radius: 8px;\n padding: 4px;\n }\n\n .zoom-btn, .file-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #475569;\n transition: all 0.2s;\n }\n\n .zoom-btn:hover, .file-btn:hover {\n color: #4f46e5;\n }\n\n .zoom-text {\n padding: 4px 8px;\n font-size: 12px;\n font-weight: 500;\n color: #475569;\n background: transparent;\n border: none;\n cursor: pointer;\n min-width: 50px;\n text-align: center;\n }\n\n .zoom-text:hover {\n color: #4f46e5;\n }\n\n /* 画布容器 */\n .canvas-container {\n flex: 1;\n position: relative;\n background: #f1f5f9;\n overflow: hidden;\n }\n\n .main-canvas {\n position: absolute;\n inset: 0;\n display: block;\n cursor: crosshair;\n touch-action: none;\n }\n\n /* 小地图 */\n .minimap-wrapper {\n position: absolute;\n top: 16px;\n right: 16px;\n z-index: 30;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n padding: 2px;\n }\n\n .minimap-wrapper > * {\n background: #ffffff;\n }\n\n .minimap-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: linear-gradient(to right, #f8fafc, #f1f5f9);\n border-bottom: 1px solid #e2e8f0;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n border-radius: 10px 10px 0 0;\n }\n\n .minimap-icon {\n width: 12px;\n height: 12px;\n color: #4f46e5;\n }\n\n .minimap-canvas {\n cursor: pointer;\n background: #f8fafc;\n border-radius: 0 0 10px 10px;\n display: block;\n }\n\n .minimap-canvas:hover {\n filter: brightness(1.05);\n }\n\n /* 文本输入 */\n .text-input-container {\n position: absolute;\n z-index: 20;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n }\n\n .text-input-hint {\n background: rgba(0, 0, 0, 0.75);\n color: #fff;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n margin-bottom: 4px;\n white-space: nowrap;\n }\n\n .text-input {\n padding: 8px 12px;\n border: 2px solid #4f46e5;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n outline: none;\n min-width: 200px;\n font-size: 16px;\n }\n\n /* 空画布提示 */\n .empty-hint {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n opacity: 0.4;\n }\n\n .empty-hint h3 {\n font-size: 24px;\n font-weight: 700;\n color: #94a3b8;\n margin-bottom: 8px;\n }\n\n .empty-hint p {\n color: #94a3b8;\n }\n `;\n }\n}\n\n// 注册 Web Component\nif (typeof window !== 'undefined' && !customElements.get('canvas-drawing-editor')) {\n customElements.define('canvas-drawing-editor', CanvasDrawingEditor);\n}\n"],"names":["defaultConfig","CanvasDrawingEditor","name","oldValue","newValue","initialData","data","obj","img","err","loadInitial","e","rect","clientX","clientY","x","y","r","c","t","width","p","minX","pt","maxX","minY","maxY","bounds","handleSize","handles","handle","previousState","o","selectedObj","newObj","mouseX","mouseY","delta","newScale","centerX","centerY","clampedScale","mouseXBeforeZoom","mouseYBeforeZoom","newPanOffsetX","newPanOffsetY","zoomText","tool","btn","screenX","screenY","text","_a","_b","value","existingObj","screenPos","clickedObject","id","dx","dy","newX","newY","newWidth","newHeight","radius","origT","scaleFactor","origP","scaleX","scaleY","textObj","ctx","isMinimap","i","imgObj","corner","minimap","mainCanvas","minimapScale","canvasW","canvasH","offsetX","offsetY","clickX","clickY","relX","relY","canvasX","canvasY","viewportCenterX","viewportCenterY","input","file","reader","event","dataUrl","maxSize","height","ratio","imageElement","rest","json","blob","url","a","tempCanvas","tempCtx","selectionInfo","typeLabel","deleteBtn","undoBtn","emptyHint","colorPicker","imageInput","zoomInBtn","zoomOutBtn","saveJsonBtn","loadJsonInput","exportPngBtn","iconClass","title","icons"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
(function(u,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(u=typeof globalThis<"u"?globalThis:u||self,f(u.CanvasDrawingEditor={}))})(this,(function(u){"use strict";const f={title:"Canvas Editor",showPencil:!0,showRectangle:!0,showCircle:!0,showText:!0,showImage:!0,showZoom:!0,showDownload:!0,showExport:!0,showImport:!0,showColor:!0,showMinimap:!0};class g extends HTMLElement{constructor(){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)}static get observedAttributes(){return["title","show-pencil","show-rectangle","show-circle","show-text","show-image","show-zoom","show-download","show-export","show-import","show-color","show-minimap","initial-data"]}connectedCallback(){this.parseAttributes(),this.render(),this.setupEventListeners(),this.initCanvas(),this.loadInitialData()}disconnectedCallback(){this.removeEventListeners()}attributeChangedCallback(t,i,s){if(i!==s){if(t==="initial-data"&&s&&this.canvas){this.loadInitialData();return}this.parseAttributes(),this.container&&this.updateUI()}}parseAttributes(){this.config={title:this.getAttribute("title")||f.title,showPencil:this.getAttribute("show-pencil")!=="false",showRectangle:this.getAttribute("show-rectangle")!=="false",showCircle:this.getAttribute("show-circle")!=="false",showText:this.getAttribute("show-text")!=="false",showImage:this.getAttribute("show-image")!=="false",showZoom:this.getAttribute("show-zoom")!=="false",showDownload:this.getAttribute("show-download")!=="false",showExport:this.getAttribute("show-export")!=="false",showImport:this.getAttribute("show-import")!=="false",showColor:this.getAttribute("show-color")!=="false",showMinimap:this.getAttribute("show-minimap")!=="false"}}generateId(){return Math.random().toString(36).substr(2,9)}loadInitialData(){const t=this.getAttribute("initial-data");if(t)try{const i=JSON.parse(t);i.objects&&Array.isArray(i.objects)&&(this.objects=i.objects,this.selectedId=null,this.objects.forEach(s=>{if(s.type==="IMAGE"&&s.dataUrl){const e=new Image;e.onload=()=>{s.imageElement=e,this.renderCanvas(),this.renderMinimap()},e.src=s.dataUrl}}),this.renderCanvas(),this.renderMinimap(),this.updateUI())}catch(i){console.error("Failed to parse initial-data:",i)}}setupEventListeners(){window.addEventListener("resize",this.boundHandleResize),window.addEventListener("keydown",this.boundHandleKeyDown)}removeEventListeners(){window.removeEventListener("resize",this.boundHandleResize),window.removeEventListener("keydown",this.boundHandleKeyDown),this.canvas&&this.canvas.removeEventListener("wheel",this.boundHandleWheel)}handleResize(){this.initCanvas()}initCanvas(){!this.canvasContainer||!this.canvas||requestAnimationFrame(()=>{this.canvas.width=this.canvasContainer.clientWidth,this.canvas.height=this.canvasContainer.clientHeight,this.renderCanvas(),this.renderMinimap()})}getMousePos(t){const i=this.canvas.getBoundingClientRect();let s,e;if("touches"in t&&t.touches.length>0)s=t.touches[0].clientX,e=t.touches[0].clientY;else if("clientX"in t)s=t.clientX,e=t.clientY;else return{x:0,y:0};const n=(s-i.left-this.panOffset.x)/this.scale,o=(e-i.top-this.panOffset.y)/this.scale;return{x:n,y:o}}getScreenPos(t){const i=this.canvas.getBoundingClientRect();let s,e;if("touches"in t&&t.touches.length>0)s=t.touches[0].clientX,e=t.touches[0].clientY;else if("clientX"in t)s=t.clientX,e=t.clientY;else return{x:0,y:0};return{x:s-i.left,y:e-i.top}}getObjectBounds(t){switch(t.type){case"RECTANGLE":case"IMAGE":{const i=t;return{x:i.x,y:i.y,width:i.width,height:i.height}}case"CIRCLE":{const i=t;return{x:i.x-i.radius,y:i.y-i.radius,width:i.radius*2,height:i.radius*2}}case"TEXT":{const i=t,s=i.text.length*i.fontSize*.6;return{x:i.x,y:i.y-i.fontSize,width:s,height:i.fontSize}}case"PATH":{const i=t;if(i.points.length===0)return{x:0,y:0,width:0,height:0};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));return{x:s,y:n,width:e-s,height:o-n}}}return{x:0,y:0,width:0,height:0}}getResizeHandleAtPoint(t,i,s){const e=this.getObjectBounds(t),n=8,o=[{name:"nw",x:e.x,y:e.y},{name:"ne",x:e.x+e.width,y:e.y},{name:"sw",x:e.x,y:e.y+e.height},{name:"se",x:e.x+e.width,y:e.y+e.height}];for(const a of o)if(Math.abs(i-a.x)<=n&&Math.abs(s-a.y)<=n)return a.name;return null}isHit(t,i,s){switch(t.type){case"RECTANGLE":{const e=t;return i>=e.x&&i<=e.x+e.width&&s>=e.y&&s<=e.y+e.height}case"CIRCLE":{const e=t;return Math.sqrt(Math.pow(i-e.x,2)+Math.pow(s-e.y,2))<=e.radius}case"IMAGE":{const e=t;return i>=e.x&&i<=e.x+e.width&&s>=e.y&&s<=e.y+e.height}case"TEXT":{const e=t;return i>=e.x&&i<=e.x+e.text.length*e.fontSize*.6&&s>=e.y-e.fontSize&&s<=e.y}case"PATH":{const e=t;if(e.points.length===0)return!1;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));return i>=n&&i<=o&&s>=a&&s<=l}}return!1}saveHistory(){this.history.push(JSON.parse(JSON.stringify(this.objects)))}undo(){if(this.history.length===0)return;const t=this.history.pop();t&&(this.objects=t,this.selectedId=null,this.renderCanvas(),this.renderMinimap(),this.dispatchChangeEvent())}deleteSelected(){this.selectedId&&(this.saveHistory(),this.objects=this.objects.filter(t=>t.id!==this.selectedId),this.selectedId=null,this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent())}copySelected(){if(this.selectedId){const t=this.objects.find(i=>i.id===this.selectedId);t&&(this.clipboard=JSON.parse(JSON.stringify(t)))}}pasteObject(){if(this.clipboard){this.saveHistory();const t={...JSON.parse(JSON.stringify(this.clipboard)),id:this.generateId(),x:this.clipboard.x+20,y:this.clipboard.y+20};t.type==="PATH"&&t.points&&(t.points=t.points.map(i=>({x:i.x+20,y:i.y+20}))),this.objects.push(t),this.selectedId=t.id,this.clipboard=t,this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent()}}dispatchChangeEvent(){this.dispatchEvent(new CustomEvent("editor-change",{bubbles:!0,composed:!0,detail:{objects:this.objects}}))}handleKeyDown(t){if(!this.isTextInputVisible){if((t.ctrlKey||t.metaKey)&&t.key==="z"){t.preventDefault(),this.undo();return}if((t.ctrlKey||t.metaKey)&&t.key==="c"){this.selectedId&&(t.preventDefault(),this.copySelected());return}if((t.ctrlKey||t.metaKey)&&t.key==="v"){this.clipboard&&(t.preventDefault(),this.pasteObject());return}if((t.key==="Delete"||t.key==="Backspace")&&this.selectedId){t.preventDefault(),this.deleteSelected();return}if(!t.ctrlKey&&!t.metaKey)switch(t.key.toLowerCase()){case"v":this.setTool("SELECT");break;case"p":case"b":this.setTool("PENCIL");break;case"r":this.setTool("RECTANGLE");break;case"o":this.setTool("CIRCLE");break;case"t":this.setTool("TEXT");break;case"escape":this.selectedId=null,this.hideTextInput(),this.renderCanvas(),this.updateUI();break}}}handleWheel(t){t.preventDefault();const i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,e=t.clientY-i.top,n=t.deltaY>0?.9:1.1,o=this.scale*n;this.zoomAtPoint(o,s,e)}zoomAtPoint(t,i,s){const e=Math.min(Math.max(t,.2),5),n=(i-this.panOffset.x)/this.scale,o=(s-this.panOffset.y)/this.scale,a=i-n*e,l=s-o*e;this.scale=e,this.panOffset={x:a,y:l},this.renderCanvas(),this.renderMinimap(),this.updateZoomDisplay()}zoomIn(){const t=this.canvas.width/2,i=this.canvas.height/2;this.zoomAtPoint(this.scale*1.2,t,i)}zoomOut(){const t=this.canvas.width/2,i=this.canvas.height/2;this.zoomAtPoint(this.scale/1.2,t,i)}resetZoom(){this.scale=1,this.panOffset={x:0,y:0},this.renderCanvas(),this.renderMinimap(),this.updateZoomDisplay()}updateZoomDisplay(){const t=this.shadow.querySelector(".zoom-text");t&&(t.textContent=`${Math.round(this.scale*100)}%`)}setTool(t){this.tool=t,this.updateToolButtons()}updateToolButtons(){this.shadow.querySelectorAll(".tool-btn").forEach(i=>{i.getAttribute("data-tool")===this.tool?i.classList.add("active"):i.classList.remove("active")})}hideTextInput(){this.isTextInputVisible=!1,this.textInputContainer&&(this.textInputContainer.style.display="none"),this.editingTextId=null}showTextInput(t,i,s=""){this.isTextInputVisible=!0,this.textInputScreenPos={x:t,y:i},this.textInputContainer&&this.textInput&&(this.textInputContainer.style.display="block",this.textInputContainer.style.left=`${t}px`,this.textInputContainer.style.top=`${i-30}px`,this.textInput.value=s,this.textInput.style.color=this.color,setTimeout(()=>{this.textInput.focus(),s&&this.textInput.select()},0))}submitText(){var i,s;const t=(s=(i=this.textInput)==null?void 0:i.value)==null?void 0:s.trim();if(t){if(this.editingTextId){const e=this.objects.find(n=>n.id===this.editingTextId);e&&e.text!==t&&(this.saveHistory(),e.text=t),this.selectedId=this.editingTextId}else{this.saveHistory();const e={id:this.generateId(),type:"TEXT",x:this.textInputPos.x,y:this.textInputPos.y,text:t,fontSize:24,color:this.color,lineWidth:this.lineWidth};this.objects.push(e),this.selectedId=e.id}this.dispatchChangeEvent()}this.hideTextInput(),this.setTool("SELECT"),this.renderCanvas(),this.renderMinimap(),this.updateUI()}handleCanvasPointerDown(t){const{x:i,y:s}=this.getMousePos(t),e=this.getScreenPos(t);if(this.dragStart={x:i,y:s},this.isDragging=!0,this.isTextInputVisible&&this.tool!=="TEXT"&&this.submitText(),this.tool==="SELECT"){if(this.selectedId){const o=this.objects.find(a=>a.id===this.selectedId);if(o){const a=this.getResizeHandleAtPoint(o,i,s);if(a){this.saveHistory(),this.isResizing=!0,this.resizeHandle=a,this.resizeStartBounds=this.getObjectBounds(o),this.resizeOriginalObject=JSON.parse(JSON.stringify(o));return}}}const n=[...this.objects].reverse().find(o=>this.isHit(o,i,s));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())}else if(this.tool==="TEXT")this.textInputPos={x:i,y:s},this.showTextInput(e.x,e.y),this.isDragging=!1;else{this.saveHistory();const n=this.generateId();this.tool==="RECTANGLE"?this.currentObject={id:n,type:"RECTANGLE",x:i,y:s,width:0,height:0,color:this.color,lineWidth:this.lineWidth}:this.tool==="CIRCLE"?this.currentObject={id:n,type:"CIRCLE",x:i,y:s,radius:0,color:this.color,lineWidth:this.lineWidth}:this.tool==="PENCIL"&&(this.currentObject={id:n,type:"PATH",x:i,y:s,points:[{x:i,y:s}],color:this.color,lineWidth:this.lineWidth})}this.renderCanvas()}handleCanvasPointerMove(t){if(this.isPanning){const e=this.getScreenPos(t),n=e.x-this.panStart.x,o=e.y-this.panStart.y;this.panOffset={x:this.panOffset.x+n,y:this.panOffset.y+o},this.panStart=e,this.renderCanvas(),this.renderMinimap();return}if(!this.isDragging||!this.dragStart)return;const{x:i,y:s}=this.getMousePos(t);if(this.isResizing&&this.selectedId&&this.resizeHandle&&this.resizeStartBounds&&this.resizeOriginalObject){const e=this.objects.find(d=>d.id===this.selectedId);if(!e)return;const n=i-this.dragStart.x,o=s-this.dragStart.y;let a=this.resizeStartBounds.x,l=this.resizeStartBounds.y,r=this.resizeStartBounds.width,h=this.resizeStartBounds.height;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){case"RECTANGLE":case"IMAGE":e.x=a,e.y=l,e.width=r,e.height=h;break;case"CIRCLE":{const d=Math.max(r,h)/2;e.x=a+d,e.y=l+d,e.radius=d;break}case"TEXT":{const d=this.resizeOriginalObject,c=r/this.resizeStartBounds.width;e.x=a,e.y=l+h,e.fontSize=Math.max(8,Math.round(d.fontSize*c));break}case"PATH":{const d=this.resizeOriginalObject,c=r/this.resizeStartBounds.width,p=h/this.resizeStartBounds.height;e.points=d.points.map(m=>({x:a+(m.x-this.resizeStartBounds.x)*c,y:l+(m.y-this.resizeStartBounds.y)*p}));break}}this.renderCanvas(),this.renderMinimap();return}if(this.tool==="SELECT"&&this.selectedId){const e=this.objects.find(n=>n.id===this.selectedId);if(e){if(e.type==="PATH"){const n=e,o=i-this.dragStart.x,a=s-this.dragStart.y;n.points=n.points.map(l=>({x:l.x+o,y:l.y+a})),this.dragStart={x:i,y:s}}else e.x=i-this.dragOffset.x,e.y=s-this.dragOffset.y;this.renderCanvas(),this.renderMinimap()}}else if(this.currentObject){if(this.currentObject.type==="RECTANGLE")this.currentObject.width=i-this.currentObject.x,this.currentObject.height=s-this.currentObject.y;else if(this.currentObject.type==="CIRCLE"){const e=Math.sqrt(Math.pow(i-this.currentObject.x,2)+Math.pow(s-this.currentObject.y,2));this.currentObject.radius=e}else this.currentObject.type==="PATH"&&this.currentObject.points.push({x:i,y:s});this.renderCanvas()}}handleCanvasPointerUp(){this.isDragging=!1,this.dragStart=null,this.isResizing=!1,this.resizeHandle=null,this.resizeStartBounds=null,this.resizeOriginalObject=null,this.isPanning=!1,this.currentObject&&(this.objects.push(this.currentObject),this.currentObject=null,this.dispatchChangeEvent()),this.renderCanvas(),this.renderMinimap(),this.updateUI()}handleCanvasDoubleClick(t){t.preventDefault();const{x:i,y:s}=this.getMousePos(t),e=[...this.objects].reverse().find(n=>this.isHit(n,i,s));if(e&&e.type==="TEXT"){const n=e;this.editingTextId=n.id,this.textInputPos={x:n.x,y:n.y};const o=n.x*this.scale+this.panOffset.x,a=n.y*this.scale+this.panOffset.y;this.showTextInput(o,a,n.text),this.setTool("SELECT")}}renderCanvas(){if(this.ctx){if(this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.fillStyle="#ffffff",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height),this.ctx.save(),this.ctx.translate(this.panOffset.x,this.panOffset.y),this.ctx.scale(this.scale,this.scale),this.objects.forEach(t=>this.drawObject(this.ctx,t,!1)),this.currentObject&&this.drawObject(this.ctx,this.currentObject,!1),this.selectedId&&this.tool==="SELECT"){const t=this.objects.find(i=>i.id===this.selectedId);t&&this.drawSelectionHandles(this.ctx,t)}this.ctx.restore()}}drawObject(t,i,s){switch(t.beginPath(),t.strokeStyle=i.color,t.lineWidth=i.lineWidth,t.fillStyle=i.color,!s&&i.id===this.selectedId?(t.shadowColor="rgba(0, 100, 255, 0.5)",t.shadowBlur=10):t.shadowBlur=0,i.type){case"RECTANGLE":{const e=i;t.strokeRect(e.x,e.y,e.width,e.height);break}case"CIRCLE":{const e=i;t.beginPath(),t.arc(e.x,e.y,e.radius,0,2*Math.PI),t.stroke();break}case"PATH":{const e=i;if(e.points.length<2)break;t.beginPath(),t.lineCap="round",t.lineJoin="round",t.moveTo(e.points[0].x,e.points[0].y);for(let n=1;n<e.points.length;n++)t.lineTo(e.points[n].x,e.points[n].y);t.stroke();break}case"TEXT":{const e=i;t.font=`${e.fontSize}px sans-serif`,t.fillText(e.text,e.x,e.y);break}case"IMAGE":{const e=i;if(e.imageElement&&e.imageElement.complete)t.drawImage(e.imageElement,e.x,e.y,e.width,e.height);else if(e.dataUrl){const n=new Image;n.onload=()=>{e.imageElement=n,this.renderCanvas()},n.src=e.dataUrl}break}}}drawSelectionHandles(t,i){const s=this.getObjectBounds(i),e=8;t.shadowBlur=0,t.fillStyle="#3b82f6",t.strokeStyle="#ffffff",t.lineWidth=2,[{x:s.x,y:s.y},{x:s.x+s.width,y:s.y},{x:s.x,y:s.y+s.height},{x:s.x+s.width,y:s.y+s.height}].forEach(o=>{t.beginPath(),t.rect(o.x-e/2,o.y-e/2,e,e),t.fill(),t.stroke()}),t.strokeStyle="#3b82f6",t.lineWidth=1,t.setLineDash([5,5]),t.strokeRect(s.x,s.y,s.width,s.height),t.setLineDash([])}renderMinimap(){if(!this.minimapCtx||!this.config.showMinimap)return;const t=this.minimapCanvas,i=this.canvas;this.minimapCtx.clearRect(0,0,t.width,t.height);const s=t.width/i.width,e=t.height/i.height,n=Math.min(s,e)*.92,o=i.width*n,a=i.height*n,l=(t.width-o)/2,r=(t.height-a)/2;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=>{switch(this.minimapCtx.fillStyle=d.color,this.minimapCtx.strokeStyle=d.color,this.minimapCtx.lineWidth=Math.max(d.lineWidth,1),this.minimapCtx.setLineDash([]),d.type){case"RECTANGLE":{const c=d;this.minimapCtx.strokeRect(c.x,c.y,c.width,c.height);break}case"CIRCLE":{const c=d;this.minimapCtx.beginPath(),this.minimapCtx.arc(c.x,c.y,c.radius,0,Math.PI*2),this.minimapCtx.stroke();break}case"PATH":{const c=d;c.points.length>0&&(this.minimapCtx.beginPath(),this.minimapCtx.lineCap="round",this.minimapCtx.lineJoin="round",this.minimapCtx.moveTo(c.points[0].x,c.points[0].y),c.points.forEach(p=>this.minimapCtx.lineTo(p.x,p.y)),this.minimapCtx.stroke());break}case"TEXT":{const c=d;this.minimapCtx.font=`${c.fontSize}px sans-serif`,this.minimapCtx.fillText(c.text,c.x,c.y);break}case"IMAGE":{const c=d;c.imageElement&&this.minimapCtx.drawImage(c.imageElement,c.x,c.y,c.width,c.height);break}}}),this.minimapCtx.restore(),this.minimapCtx.strokeStyle="#94a3b8",this.minimapCtx.lineWidth=1,this.minimapCtx.strokeRect(l,r,o,a)}handleMinimapClick(t){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)*.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,m=(c/a-this.panOffset.x)/this.scale,x=(p/a-this.panOffset.y)/this.scale,b=this.canvas.width/2,v=this.canvas.height/2;this.panOffset={x:b/this.scale-m,y:v/this.scale-x},this.renderCanvas(),this.renderMinimap()}handleImageUpload(t){const i=t.target;if(!i.files||i.files.length===0)return;const s=i.files[0],e=new FileReader;e.onload=n=>{var l;const o=(l=n.target)==null?void 0:l.result,a=new Image;a.onload=()=>{this.saveHistory();const r=300;let h=a.width,d=a.height;if(h>r||d>r){const p=Math.min(r/h,r/d);h*=p,d*=p}const c={id:this.generateId(),type:"IMAGE",x:100,y:100,width:h,height:d,color:"#000000",lineWidth:1,dataUrl:o,imageElement:a};this.objects.push(c),this.selectedId=c.id,this.setTool("SELECT"),this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent()},a.src=o},e.readAsDataURL(s),i.value=""}saveJson(){const t={version:"1.0",objects:this.objects.map(o=>{const{imageElement:a,...l}=o;return l})},i=JSON.stringify(t,null,2),s=new Blob([i],{type:"application/json"}),e=URL.createObjectURL(s),n=document.createElement("a");n.href=e,n.download="canvas-project.json",n.click(),URL.revokeObjectURL(e)}loadJson(t){const i=t.target;if(!i.files||i.files.length===0)return;const s=i.files[0],e=new FileReader;e.onload=n=>{var o;try{const a=JSON.parse((o=n.target)==null?void 0:o.result);a.objects&&Array.isArray(a.objects)&&(this.saveHistory(),this.objects=a.objects,this.selectedId=null,this.objects.forEach(l=>{if(l.type==="IMAGE"&&l.dataUrl){const r=new Image;r.onload=()=>{l.imageElement=r,this.renderCanvas(),this.renderMinimap()},r.src=l.dataUrl}}),this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent())}catch(a){console.error("Failed to load JSON:",a)}},e.readAsText(s),i.value=""}exportPng(){const t=document.createElement("canvas");t.width=this.canvas.width,t.height=this.canvas.height;const i=t.getContext("2d");i.fillStyle="#ffffff",i.fillRect(0,0,t.width,t.height),i.translate(this.panOffset.x,this.panOffset.y),i.scale(this.scale,this.scale),this.objects.forEach(n=>this.drawObject(i,n,!0));const s=t.toDataURL("image/png"),e=document.createElement("a");e.href=s,e.download="canvas-export.png",e.click()}updateUI(){const t=this.shadow.querySelector(".selection-info");if(t)if(this.selectedId){const e=this.objects.find(n=>n.id===this.selectedId);if(e){const o={RECTANGLE:"矩形",CIRCLE:"圆形",PATH:"画笔",TEXT:"文本",IMAGE:"图片"}[e.type]||e.type;t.innerHTML=`
|
|
2
|
-
<span class="selection-label">已选择: ${
|
|
1
|
+
(function(u,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(u=typeof globalThis<"u"?globalThis:u||self,f(u.CanvasDrawingEditor={}))})(this,(function(u){"use strict";const f={title:"Canvas Editor",showPencil:!0,showRectangle:!0,showCircle:!0,showText:!0,showImage:!0,showZoom:!0,showDownload:!0,showExport:!0,showImport:!0,showColor:!0,showMinimap:!0};class m extends HTMLElement{constructor(){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)}static get observedAttributes(){return["title","show-pencil","show-rectangle","show-circle","show-text","show-image","show-zoom","show-download","show-export","show-import","show-color","show-minimap","initial-data"]}connectedCallback(){this.parseAttributes(),this.render(),this.setupEventListeners(),this.initCanvas(!0)}disconnectedCallback(){this.removeEventListeners()}attributeChangedCallback(t,i,s){if(i!==s){if(t==="initial-data"&&s&&this.canvas){this.loadInitialData(),this.renderCanvas(),this.renderMinimap();return}this.parseAttributes(),this.container&&this.updateUI()}}parseAttributes(){this.config={title:this.getAttribute("title")||f.title,showPencil:this.getAttribute("show-pencil")!=="false",showRectangle:this.getAttribute("show-rectangle")!=="false",showCircle:this.getAttribute("show-circle")!=="false",showText:this.getAttribute("show-text")!=="false",showImage:this.getAttribute("show-image")!=="false",showZoom:this.getAttribute("show-zoom")!=="false",showDownload:this.getAttribute("show-download")!=="false",showExport:this.getAttribute("show-export")!=="false",showImport:this.getAttribute("show-import")!=="false",showColor:this.getAttribute("show-color")!=="false",showMinimap:this.getAttribute("show-minimap")!=="false"}}generateId(){return Math.random().toString(36).substr(2,9)}loadInitialData(){const t=this.getAttribute("initial-data");if(t)try{const i=JSON.parse(t);i.objects&&Array.isArray(i.objects)&&(this.objects=i.objects,this.selectedId=null,this.objects.forEach(s=>{if(s.type==="IMAGE"&&s.dataUrl){const e=new Image;e.onload=()=>{s.imageElement=e,this.renderCanvas(),this.renderMinimap()},e.src=s.dataUrl}}),this.updateUI())}catch(i){console.error("Failed to parse initial-data:",i)}}setupEventListeners(){window.addEventListener("resize",this.boundHandleResize),window.addEventListener("keydown",this.boundHandleKeyDown)}removeEventListeners(){window.removeEventListener("resize",this.boundHandleResize),window.removeEventListener("keydown",this.boundHandleKeyDown),this.canvas&&this.canvas.removeEventListener("wheel",this.boundHandleWheel)}handleResize(){this.initCanvas(!1)}initCanvas(t=!1){!this.canvasContainer||!this.canvas||requestAnimationFrame(()=>{this.canvas.width=this.canvasContainer.clientWidth,this.canvas.height=this.canvasContainer.clientHeight,t&&this.loadInitialData(),this.renderCanvas(),this.renderMinimap()})}getMousePos(t){const i=this.canvas.getBoundingClientRect();let s,e;if("touches"in t&&t.touches.length>0)s=t.touches[0].clientX,e=t.touches[0].clientY;else if("clientX"in t)s=t.clientX,e=t.clientY;else return{x:0,y:0};const n=(s-i.left-this.panOffset.x)/this.scale,a=(e-i.top-this.panOffset.y)/this.scale;return{x:n,y:a}}getScreenPos(t){const i=this.canvas.getBoundingClientRect();let s,e;if("touches"in t&&t.touches.length>0)s=t.touches[0].clientX,e=t.touches[0].clientY;else if("clientX"in t)s=t.clientX,e=t.clientY;else return{x:0,y:0};return{x:s-i.left,y:e-i.top}}getObjectBounds(t){switch(t.type){case"RECTANGLE":case"IMAGE":{const i=t;return{x:i.x,y:i.y,width:i.width,height:i.height}}case"CIRCLE":{const i=t;return{x:i.x-i.radius,y:i.y-i.radius,width:i.radius*2,height:i.radius*2}}case"TEXT":{const i=t,s=i.text.length*i.fontSize*.6;return{x:i.x,y:i.y-i.fontSize,width:s,height:i.fontSize}}case"PATH":{const i=t;if(i.points.length===0)return{x:0,y:0,width:0,height:0};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));return{x:s,y:n,width:e-s,height:a-n}}}return{x:0,y:0,width:0,height:0}}getResizeHandleAtPoint(t,i,s){const e=this.getObjectBounds(t),n=8,a=[{name:"nw",x:e.x,y:e.y},{name:"ne",x:e.x+e.width,y:e.y},{name:"sw",x:e.x,y:e.y+e.height},{name:"se",x:e.x+e.width,y:e.y+e.height}];for(const o of a)if(Math.abs(i-o.x)<=n&&Math.abs(s-o.y)<=n)return o.name;return null}isHit(t,i,s){switch(t.type){case"RECTANGLE":{const e=t;return i>=e.x&&i<=e.x+e.width&&s>=e.y&&s<=e.y+e.height}case"CIRCLE":{const e=t;return Math.sqrt(Math.pow(i-e.x,2)+Math.pow(s-e.y,2))<=e.radius}case"IMAGE":{const e=t;return i>=e.x&&i<=e.x+e.width&&s>=e.y&&s<=e.y+e.height}case"TEXT":{const e=t;return i>=e.x&&i<=e.x+e.text.length*e.fontSize*.6&&s>=e.y-e.fontSize&&s<=e.y}case"PATH":{const e=t;if(e.points.length===0)return!1;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));return i>=n&&i<=a&&s>=o&&s<=l}}return!1}saveHistory(){this.history.push(JSON.parse(JSON.stringify(this.objects)))}undo(){if(this.history.length===0)return;const t=this.history.pop();t&&(this.objects=t,this.selectedId=null,this.renderCanvas(),this.renderMinimap(),this.dispatchChangeEvent())}deleteSelected(){this.selectedId&&(this.saveHistory(),this.objects=this.objects.filter(t=>t.id!==this.selectedId),this.selectedId=null,this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent())}copySelected(){if(this.selectedId){const t=this.objects.find(i=>i.id===this.selectedId);t&&(this.clipboard=JSON.parse(JSON.stringify(t)))}}pasteObject(){if(this.clipboard){this.saveHistory();const t={...JSON.parse(JSON.stringify(this.clipboard)),id:this.generateId(),x:this.clipboard.x+20,y:this.clipboard.y+20};t.type==="PATH"&&t.points&&(t.points=t.points.map(i=>({x:i.x+20,y:i.y+20}))),this.objects.push(t),this.selectedId=t.id,this.clipboard=t,this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent()}}dispatchChangeEvent(){this.dispatchEvent(new CustomEvent("editor-change",{bubbles:!0,composed:!0,detail:{objects:this.objects}}))}handleKeyDown(t){if(!this.isTextInputVisible){if((t.ctrlKey||t.metaKey)&&t.key==="z"){t.preventDefault(),this.undo();return}if((t.ctrlKey||t.metaKey)&&t.key==="c"){this.selectedId&&(t.preventDefault(),this.copySelected());return}if((t.ctrlKey||t.metaKey)&&t.key==="v"){this.clipboard&&(t.preventDefault(),this.pasteObject());return}if((t.key==="Delete"||t.key==="Backspace")&&this.selectedId){t.preventDefault(),this.deleteSelected();return}if(!t.ctrlKey&&!t.metaKey)switch(t.key.toLowerCase()){case"v":this.setTool("SELECT");break;case"p":case"b":this.setTool("PENCIL");break;case"r":this.setTool("RECTANGLE");break;case"o":this.setTool("CIRCLE");break;case"t":this.setTool("TEXT");break;case"escape":this.selectedId=null,this.hideTextInput(),this.renderCanvas(),this.updateUI();break}}}handleWheel(t){t.preventDefault();const i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,e=t.clientY-i.top,n=t.deltaY>0?.9:1.1,a=this.scale*n;this.zoomAtPoint(a,s,e)}zoomAtPoint(t,i,s){const e=Math.min(Math.max(t,.2),5),n=(i-this.panOffset.x)/this.scale,a=(s-this.panOffset.y)/this.scale,o=i-n*e,l=s-a*e;this.scale=e,this.panOffset={x:o,y:l},this.renderCanvas(),this.renderMinimap(),this.updateZoomDisplay()}zoomIn(){const t=this.canvas.width/2,i=this.canvas.height/2;this.zoomAtPoint(this.scale*1.2,t,i)}zoomOut(){const t=this.canvas.width/2,i=this.canvas.height/2;this.zoomAtPoint(this.scale/1.2,t,i)}resetZoom(){this.scale=1,this.panOffset={x:0,y:0},this.renderCanvas(),this.renderMinimap(),this.updateZoomDisplay()}updateZoomDisplay(){const t=this.shadow.querySelector(".zoom-text");t&&(t.textContent=`${Math.round(this.scale*100)}%`)}setTool(t){this.tool=t,this.updateToolButtons()}updateToolButtons(){this.shadow.querySelectorAll(".tool-btn").forEach(i=>{i.getAttribute("data-tool")===this.tool?i.classList.add("active"):i.classList.remove("active")})}hideTextInput(){this.isTextInputVisible=!1,this.textInputContainer&&(this.textInputContainer.style.display="none"),this.editingTextId=null}showTextInput(t,i,s=""){this.isTextInputVisible=!0,this.textInputScreenPos={x:t,y:i},this.textInputContainer&&this.textInput&&(this.textInputContainer.style.display="block",this.textInputContainer.style.left=`${t}px`,this.textInputContainer.style.top=`${i-30}px`,this.textInput.value=s,this.textInput.style.color=this.color,setTimeout(()=>{this.textInput.focus(),s&&this.textInput.select()},0))}submitText(){var i,s;const t=(s=(i=this.textInput)==null?void 0:i.value)==null?void 0:s.trim();if(t){if(this.editingTextId){const e=this.objects.find(n=>n.id===this.editingTextId);e&&e.text!==t&&(this.saveHistory(),e.text=t),this.selectedId=this.editingTextId}else{this.saveHistory();const e={id:this.generateId(),type:"TEXT",x:this.textInputPos.x,y:this.textInputPos.y,text:t,fontSize:24,color:this.color,lineWidth:this.lineWidth};this.objects.push(e),this.selectedId=e.id}this.dispatchChangeEvent()}this.hideTextInput(),this.setTool("SELECT"),this.renderCanvas(),this.renderMinimap(),this.updateUI()}handleCanvasPointerDown(t){const{x:i,y:s}=this.getMousePos(t),e=this.getScreenPos(t);if(this.dragStart={x:i,y:s},this.isDragging=!0,this.isTextInputVisible&&this.tool!=="TEXT"&&this.submitText(),this.tool==="SELECT"){if(this.selectedId){const a=this.objects.find(o=>o.id===this.selectedId);if(a){const o=this.getResizeHandleAtPoint(a,i,s);if(o){this.saveHistory(),this.isResizing=!0,this.resizeHandle=o,this.resizeStartBounds=this.getObjectBounds(a),this.resizeOriginalObject=JSON.parse(JSON.stringify(a));return}}}const n=[...this.objects].reverse().find(a=>this.isHit(a,i,s));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())}else if(this.tool==="TEXT")this.textInputPos={x:i,y:s},this.showTextInput(e.x,e.y),this.isDragging=!1;else{this.saveHistory();const n=this.generateId();this.tool==="RECTANGLE"?this.currentObject={id:n,type:"RECTANGLE",x:i,y:s,width:0,height:0,color:this.color,lineWidth:this.lineWidth}:this.tool==="CIRCLE"?this.currentObject={id:n,type:"CIRCLE",x:i,y:s,radius:0,color:this.color,lineWidth:this.lineWidth}:this.tool==="PENCIL"&&(this.currentObject={id:n,type:"PATH",x:i,y:s,points:[{x:i,y:s}],color:this.color,lineWidth:this.lineWidth})}this.renderCanvas()}handleCanvasPointerMove(t){if(this.isPanning){const e=this.getScreenPos(t),n=e.x-this.panStart.x,a=e.y-this.panStart.y;this.panOffset={x:this.panOffset.x+n,y:this.panOffset.y+a},this.panStart=e,this.renderCanvas(),this.renderMinimap();return}if(!this.isDragging||!this.dragStart)return;const{x:i,y:s}=this.getMousePos(t);if(this.isResizing&&this.selectedId&&this.resizeHandle&&this.resizeStartBounds&&this.resizeOriginalObject){const e=this.objects.find(d=>d.id===this.selectedId);if(!e)return;const n=i-this.dragStart.x,a=s-this.dragStart.y;let o=this.resizeStartBounds.x,l=this.resizeStartBounds.y,r=this.resizeStartBounds.width,h=this.resizeStartBounds.height;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){case"RECTANGLE":case"IMAGE":e.x=o,e.y=l,e.width=r,e.height=h;break;case"CIRCLE":{const d=Math.max(r,h)/2;e.x=o+d,e.y=l+d,e.radius=d;break}case"TEXT":{const d=this.resizeOriginalObject,c=r/this.resizeStartBounds.width;e.x=o,e.y=l+h,e.fontSize=Math.max(8,Math.round(d.fontSize*c));break}case"PATH":{const d=this.resizeOriginalObject,c=r/this.resizeStartBounds.width,p=h/this.resizeStartBounds.height;e.points=d.points.map(g=>({x:o+(g.x-this.resizeStartBounds.x)*c,y:l+(g.y-this.resizeStartBounds.y)*p}));break}}this.renderCanvas(),this.renderMinimap();return}if(this.tool==="SELECT"&&this.selectedId){const e=this.objects.find(n=>n.id===this.selectedId);if(e){if(e.type==="PATH"){const n=e,a=i-this.dragStart.x,o=s-this.dragStart.y;n.points=n.points.map(l=>({x:l.x+a,y:l.y+o})),this.dragStart={x:i,y:s}}else e.x=i-this.dragOffset.x,e.y=s-this.dragOffset.y;this.renderCanvas(),this.renderMinimap()}}else if(this.currentObject){if(this.currentObject.type==="RECTANGLE")this.currentObject.width=i-this.currentObject.x,this.currentObject.height=s-this.currentObject.y;else if(this.currentObject.type==="CIRCLE"){const e=Math.sqrt(Math.pow(i-this.currentObject.x,2)+Math.pow(s-this.currentObject.y,2));this.currentObject.radius=e}else this.currentObject.type==="PATH"&&this.currentObject.points.push({x:i,y:s});this.renderCanvas()}}handleCanvasPointerUp(){this.isDragging=!1,this.dragStart=null,this.isResizing=!1,this.resizeHandle=null,this.resizeStartBounds=null,this.resizeOriginalObject=null,this.isPanning=!1,this.currentObject&&(this.objects.push(this.currentObject),this.currentObject=null,this.dispatchChangeEvent()),this.renderCanvas(),this.renderMinimap(),this.updateUI()}handleCanvasDoubleClick(t){t.preventDefault();const{x:i,y:s}=this.getMousePos(t),e=[...this.objects].reverse().find(n=>this.isHit(n,i,s));if(e&&e.type==="TEXT"){const n=e;this.editingTextId=n.id,this.textInputPos={x:n.x,y:n.y};const a=n.x*this.scale+this.panOffset.x,o=n.y*this.scale+this.panOffset.y;this.showTextInput(a,o,n.text),this.setTool("SELECT")}}renderCanvas(){if(this.ctx){if(this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.fillStyle="#ffffff",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height),this.ctx.save(),this.ctx.translate(this.panOffset.x,this.panOffset.y),this.ctx.scale(this.scale,this.scale),this.objects.forEach(t=>this.drawObject(this.ctx,t,!1)),this.currentObject&&this.drawObject(this.ctx,this.currentObject,!1),this.selectedId&&this.tool==="SELECT"){const t=this.objects.find(i=>i.id===this.selectedId);t&&this.drawSelectionHandles(this.ctx,t)}this.ctx.restore()}}drawObject(t,i,s){switch(t.beginPath(),t.strokeStyle=i.color,t.lineWidth=i.lineWidth,t.fillStyle=i.color,!s&&i.id===this.selectedId?(t.shadowColor="rgba(0, 100, 255, 0.5)",t.shadowBlur=10):t.shadowBlur=0,i.type){case"RECTANGLE":{const e=i;t.strokeRect(e.x,e.y,e.width,e.height);break}case"CIRCLE":{const e=i;t.beginPath(),t.arc(e.x,e.y,e.radius,0,2*Math.PI),t.stroke();break}case"PATH":{const e=i;if(e.points.length<2)break;t.beginPath(),t.lineCap="round",t.lineJoin="round",t.moveTo(e.points[0].x,e.points[0].y);for(let n=1;n<e.points.length;n++)t.lineTo(e.points[n].x,e.points[n].y);t.stroke();break}case"TEXT":{const e=i;t.font=`${e.fontSize}px sans-serif`,t.fillText(e.text,e.x,e.y);break}case"IMAGE":{const e=i;if(e.imageElement&&e.imageElement.complete)t.drawImage(e.imageElement,e.x,e.y,e.width,e.height);else if(e.dataUrl){const n=new Image;n.onload=()=>{e.imageElement=n,this.renderCanvas()},n.src=e.dataUrl}break}}}drawSelectionHandles(t,i){const s=this.getObjectBounds(i),e=8;t.shadowBlur=0,t.fillStyle="#3b82f6",t.strokeStyle="#ffffff",t.lineWidth=2,[{x:s.x,y:s.y},{x:s.x+s.width,y:s.y},{x:s.x,y:s.y+s.height},{x:s.x+s.width,y:s.y+s.height}].forEach(a=>{t.beginPath(),t.rect(a.x-e/2,a.y-e/2,e,e),t.fill(),t.stroke()}),t.strokeStyle="#3b82f6",t.lineWidth=1,t.setLineDash([5,5]),t.strokeRect(s.x,s.y,s.width,s.height),t.setLineDash([])}renderMinimap(){if(!this.minimapCtx||!this.config.showMinimap)return;const t=this.minimapCanvas,i=this.canvas;this.minimapCtx.clearRect(0,0,t.width,t.height);const s=t.width/i.width,e=t.height/i.height,n=Math.min(s,e)*.92,a=i.width*n,o=i.height*n,l=(t.width-a)/2,r=(t.height-o)/2;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.currentObject?[...this.objects,this.currentObject]:this.objects).forEach(d=>{switch(this.minimapCtx.fillStyle=d.color,this.minimapCtx.strokeStyle=d.color,this.minimapCtx.lineWidth=Math.max(d.lineWidth,1),this.minimapCtx.setLineDash([]),d.type){case"RECTANGLE":{const c=d;this.minimapCtx.strokeRect(c.x,c.y,c.width,c.height);break}case"CIRCLE":{const c=d;this.minimapCtx.beginPath(),this.minimapCtx.arc(c.x,c.y,c.radius,0,Math.PI*2),this.minimapCtx.stroke();break}case"PATH":{const c=d;c.points.length>0&&(this.minimapCtx.beginPath(),this.minimapCtx.lineCap="round",this.minimapCtx.lineJoin="round",this.minimapCtx.moveTo(c.points[0].x,c.points[0].y),c.points.forEach(p=>this.minimapCtx.lineTo(p.x,p.y)),this.minimapCtx.stroke());break}case"TEXT":{const c=d;this.minimapCtx.font=`${c.fontSize}px sans-serif`,this.minimapCtx.fillText(c.text,c.x,c.y);break}case"IMAGE":{const c=d;c.imageElement&&this.minimapCtx.drawImage(c.imageElement,c.x,c.y,c.width,c.height);break}}}),this.minimapCtx.restore(),this.minimapCtx.strokeStyle="#94a3b8",this.minimapCtx.lineWidth=1,this.minimapCtx.strokeRect(l,r,a,o)}handleMinimapClick(t){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)*.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,g=(c/o-this.panOffset.x)/this.scale,x=(p/o-this.panOffset.y)/this.scale,b=this.canvas.width/2,v=this.canvas.height/2;this.panOffset={x:b/this.scale-g,y:v/this.scale-x},this.renderCanvas(),this.renderMinimap()}handleImageUpload(t){const i=t.target;if(!i.files||i.files.length===0)return;const s=i.files[0],e=new FileReader;e.onload=n=>{var l;const a=(l=n.target)==null?void 0:l.result,o=new Image;o.onload=()=>{this.saveHistory();const r=300;let h=o.width,d=o.height;if(h>r||d>r){const p=Math.min(r/h,r/d);h*=p,d*=p}const c={id:this.generateId(),type:"IMAGE",x:100,y:100,width:h,height:d,color:"#000000",lineWidth:1,dataUrl:a,imageElement:o};this.objects.push(c),this.selectedId=c.id,this.setTool("SELECT"),this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent()},o.src=a},e.readAsDataURL(s),i.value=""}saveJson(){const t={version:"1.0",objects:this.objects.map(a=>{const{imageElement:o,...l}=a;return l})},i=JSON.stringify(t,null,2),s=new Blob([i],{type:"application/json"}),e=URL.createObjectURL(s),n=document.createElement("a");n.href=e,n.download="canvas-project.json",n.click(),URL.revokeObjectURL(e)}loadJson(t){const i=t.target;if(!i.files||i.files.length===0)return;const s=i.files[0],e=new FileReader;e.onload=n=>{var a;try{const o=JSON.parse((a=n.target)==null?void 0:a.result);o.objects&&Array.isArray(o.objects)&&(this.saveHistory(),this.objects=o.objects,this.selectedId=null,this.objects.forEach(l=>{if(l.type==="IMAGE"&&l.dataUrl){const r=new Image;r.onload=()=>{l.imageElement=r,this.renderCanvas(),this.renderMinimap()},r.src=l.dataUrl}}),this.renderCanvas(),this.renderMinimap(),this.updateUI(),this.dispatchChangeEvent())}catch(o){console.error("Failed to load JSON:",o)}},e.readAsText(s),i.value=""}exportPng(){const t=document.createElement("canvas");t.width=this.canvas.width,t.height=this.canvas.height;const i=t.getContext("2d");i.fillStyle="#ffffff",i.fillRect(0,0,t.width,t.height),i.translate(this.panOffset.x,this.panOffset.y),i.scale(this.scale,this.scale),this.objects.forEach(n=>this.drawObject(i,n,!0));const s=t.toDataURL("image/png"),e=document.createElement("a");e.href=s,e.download="canvas-export.png",e.click()}updateUI(){const t=this.shadow.querySelector(".selection-info");if(t)if(this.selectedId){const e=this.objects.find(n=>n.id===this.selectedId);if(e){const a={RECTANGLE:"矩形",CIRCLE:"圆形",PATH:"画笔",TEXT:"文本",IMAGE:"图片"}[e.type]||e.type;t.innerHTML=`
|
|
2
|
+
<span class="selection-label">已选择: ${a}</span>
|
|
3
3
|
<button class="delete-btn" title="删除">
|
|
4
4
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
5
5
|
<path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/>
|
|
6
6
|
</svg>
|
|
7
7
|
</button>
|
|
8
|
-
`,t.classList.add("visible");const
|
|
8
|
+
`,t.classList.add("visible");const o=t.querySelector(".delete-btn");o&&o.addEventListener("click",()=>this.deleteSelected())}}else t.classList.remove("visible"),t.innerHTML="";const i=this.shadow.querySelector(".undo-btn");i&&(i.disabled=this.history.length===0);const s=this.shadow.querySelector(".empty-hint");s&&(s.style.display=this.objects.length===0?"flex":"none")}render(){this.shadow.innerHTML=`
|
|
9
9
|
<style>${this.getStyles()}</style>
|
|
10
10
|
<div class="editor-container">
|
|
11
11
|
<!-- 左侧工具栏 -->
|
|
@@ -117,14 +117,14 @@
|
|
|
117
117
|
</div>
|
|
118
118
|
|
|
119
119
|
<!-- 空画布提示 -->
|
|
120
|
-
<div class="empty-hint">
|
|
120
|
+
<div class="empty-hint" style="display: ${this.getAttribute("initial-data")?"none":"flex"};">
|
|
121
121
|
<h3>开始创作</h3>
|
|
122
122
|
<p>选择左侧的工具开始绘制</p>
|
|
123
123
|
</div>
|
|
124
124
|
</div>
|
|
125
125
|
</div>
|
|
126
126
|
</div>
|
|
127
|
-
`,this.container=this.shadow.querySelector(".editor-container"),this.toolbar=this.shadow.querySelector(".toolbar"),this.topBar=this.shadow.querySelector(".top-bar"),this.canvasContainer=this.shadow.querySelector(".canvas-container"),this.canvas=this.shadow.querySelector(".main-canvas"),this.ctx=this.canvas.getContext("2d"),this.config.showMinimap&&(this.minimapCanvas=this.shadow.querySelector(".minimap-canvas"),this.minimapCtx=this.minimapCanvas.getContext("2d")),this.textInputContainer=this.shadow.querySelector(".text-input-container"),this.textInput=this.shadow.querySelector(".text-input"),this.bindEvents()}bindEvents(){this.canvas.addEventListener("mousedown",h=>this.handleCanvasPointerDown(h)),this.canvas.addEventListener("mousemove",h=>this.handleCanvasPointerMove(h)),this.canvas.addEventListener("mouseup",()=>this.handleCanvasPointerUp()),this.canvas.addEventListener("mouseleave",()=>this.handleCanvasPointerUp()),this.canvas.addEventListener("dblclick",h=>this.handleCanvasDoubleClick(h)),this.canvas.addEventListener("touchstart",h=>this.handleCanvasPointerDown(h)),this.canvas.addEventListener("touchmove",h=>this.handleCanvasPointerMove(h)),this.canvas.addEventListener("touchend",()=>this.handleCanvasPointerUp()),this.canvas.addEventListener("wheel",this.boundHandleWheel,{passive:!1}),this.shadow.querySelectorAll(".tool-btn[data-tool]").forEach(h=>{h.addEventListener("mousedown",d=>{d.preventDefault()}),h.addEventListener("click",()=>{this.isTextInputVisible&&this.submitText();const d=h.getAttribute("data-tool");this.setTool(d)})});const t=this.shadow.querySelector(".undo-btn");t&&t.addEventListener("click",()=>this.undo());const i=this.shadow.querySelector(".color-picker");i&&i.addEventListener("input",h=>{this.color=h.target.value});const s=this.shadow.querySelector(".image-input");s&&s.addEventListener("change",h=>this.handleImageUpload(h));const e=this.shadow.querySelector(".zoom-in-btn"),n=this.shadow.querySelector(".zoom-out-btn"),
|
|
127
|
+
`,this.container=this.shadow.querySelector(".editor-container"),this.toolbar=this.shadow.querySelector(".toolbar"),this.topBar=this.shadow.querySelector(".top-bar"),this.canvasContainer=this.shadow.querySelector(".canvas-container"),this.canvas=this.shadow.querySelector(".main-canvas"),this.ctx=this.canvas.getContext("2d"),this.config.showMinimap&&(this.minimapCanvas=this.shadow.querySelector(".minimap-canvas"),this.minimapCtx=this.minimapCanvas.getContext("2d")),this.textInputContainer=this.shadow.querySelector(".text-input-container"),this.textInput=this.shadow.querySelector(".text-input"),this.bindEvents()}bindEvents(){this.canvas.addEventListener("mousedown",h=>this.handleCanvasPointerDown(h)),this.canvas.addEventListener("mousemove",h=>this.handleCanvasPointerMove(h)),this.canvas.addEventListener("mouseup",()=>this.handleCanvasPointerUp()),this.canvas.addEventListener("mouseleave",()=>this.handleCanvasPointerUp()),this.canvas.addEventListener("dblclick",h=>this.handleCanvasDoubleClick(h)),this.canvas.addEventListener("touchstart",h=>this.handleCanvasPointerDown(h)),this.canvas.addEventListener("touchmove",h=>this.handleCanvasPointerMove(h)),this.canvas.addEventListener("touchend",()=>this.handleCanvasPointerUp()),this.canvas.addEventListener("wheel",this.boundHandleWheel,{passive:!1}),this.shadow.querySelectorAll(".tool-btn[data-tool]").forEach(h=>{h.addEventListener("mousedown",d=>{d.preventDefault()}),h.addEventListener("click",()=>{this.isTextInputVisible&&this.submitText();const d=h.getAttribute("data-tool");this.setTool(d)})});const t=this.shadow.querySelector(".undo-btn");t&&t.addEventListener("click",()=>this.undo());const i=this.shadow.querySelector(".color-picker");i&&i.addEventListener("input",h=>{this.color=h.target.value});const s=this.shadow.querySelector(".image-input");s&&s.addEventListener("change",h=>this.handleImageUpload(h));const e=this.shadow.querySelector(".zoom-in-btn"),n=this.shadow.querySelector(".zoom-out-btn"),a=this.shadow.querySelector(".zoom-text");e&&e.addEventListener("click",()=>this.zoomIn()),n&&n.addEventListener("click",()=>this.zoomOut()),a&&a.addEventListener("click",()=>this.resetZoom());const o=this.shadow.querySelector(".save-json-btn"),l=this.shadow.querySelector(".load-json-input"),r=this.shadow.querySelector(".export-png-btn");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=>{h.key==="Enter"?(h.preventDefault(),this.submitText()):h.key==="Escape"&&this.hideTextInput()}),this.textInput.addEventListener("blur",()=>{this.isTextInputVisible&&this.submitText()}))}createToolButton(t,i,s){const e={"select-icon":'<path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"/><path d="M13 13l6 6"/>',"pencil-icon":'<path d="M17 3a2.85 2.85 0 114 4L7.5 20.5 2 22l1.5-5.5L17 3z"/>',"rect-icon":'<rect x="3" y="3" width="18" height="18" rx="2"/>',"circle-icon":'<circle cx="12" cy="12" r="10"/>',"text-icon":'<path d="M4 7V4h16v3M9 20h6M12 4v16"/>'};return`
|
|
128
128
|
<button class="tool-btn ${this.tool===t?"active":""}" data-tool="${t}" title="${s}">
|
|
129
129
|
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
130
130
|
${e[i]}
|
|
@@ -463,5 +463,5 @@
|
|
|
463
463
|
.empty-hint p {
|
|
464
464
|
color: #94a3b8;
|
|
465
465
|
}
|
|
466
|
-
`}}typeof window<"u"&&!customElements.get("canvas-drawing-editor")&&customElements.define("canvas-drawing-editor",
|
|
466
|
+
`}}typeof window<"u"&&!customElements.get("canvas-drawing-editor")&&customElements.define("canvas-drawing-editor",m),u.CanvasDrawingEditor=m,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
|
|
467
467
|
//# sourceMappingURL=canvas-drawing-editor.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-drawing-editor.umd.js","sources":["../src/core/CanvasDrawingEditor.ts"],"sourcesContent":["/**\n * Canvas Drawing Editor - 纯 JavaScript Web Component\n * 无任何框架依赖\n */\n\n// 类型定义\nexport type ToolType = 'SELECT' | 'PENCIL' | 'RECTANGLE' | 'CIRCLE' | 'TEXT' | 'IMAGE';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface BaseObject {\n id: string;\n type: string;\n x: number;\n y: number;\n color: string;\n lineWidth: number;\n}\n\nexport interface RectObject extends BaseObject {\n type: 'RECTANGLE';\n width: number;\n height: number;\n}\n\nexport interface CircleObject extends BaseObject {\n type: 'CIRCLE';\n radius: number;\n}\n\nexport interface PathObject extends BaseObject {\n type: 'PATH';\n points: Point[];\n}\n\nexport interface TextObject extends BaseObject {\n type: 'TEXT';\n text: string;\n fontSize: number;\n}\n\nexport interface ImageObject extends BaseObject {\n type: 'IMAGE';\n width: number;\n height: number;\n dataUrl: string;\n imageElement?: HTMLImageElement;\n}\n\nexport type CanvasObject = RectObject | CircleObject | PathObject | TextObject | ImageObject;\n\nexport interface EditorConfig {\n title?: string;\n showPencil?: boolean;\n showRectangle?: boolean;\n showCircle?: boolean;\n showText?: boolean;\n showImage?: boolean;\n showZoom?: boolean;\n showDownload?: boolean;\n showExport?: boolean;\n showImport?: boolean;\n showColor?: boolean;\n showMinimap?: boolean;\n}\n\n// 默认配置\nconst defaultConfig: EditorConfig = {\n title: 'Canvas Editor',\n showPencil: true,\n showRectangle: true,\n showCircle: true,\n showText: true,\n showImage: true,\n showZoom: true,\n showDownload: true,\n showExport: true,\n showImport: true,\n showColor: true,\n showMinimap: true,\n};\n\n/**\n * Canvas Drawing Editor Web Component\n */\nexport class CanvasDrawingEditor extends HTMLElement {\n // Shadow DOM\n private shadow: ShadowRoot;\n\n // DOM 元素\n private container!: HTMLDivElement;\n private toolbar!: HTMLDivElement;\n private topBar!: HTMLDivElement;\n private canvasContainer!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx!: CanvasRenderingContext2D;\n private minimapCanvas!: HTMLCanvasElement;\n private minimapCtx!: CanvasRenderingContext2D;\n private textInput!: HTMLInputElement;\n private textInputContainer!: HTMLDivElement;\n\n // 配置\n private config: EditorConfig = { ...defaultConfig };\n\n // 状态\n private objects: CanvasObject[] = [];\n private selectedId: string | null = null;\n private tool: ToolType = 'SELECT';\n private color: string = '#000000';\n private lineWidth: number = 3;\n\n // 交互状态\n private isDragging: boolean = false;\n private dragStart: Point | null = null;\n private currentObject: CanvasObject | null = null;\n private dragOffset: Point = { x: 0, y: 0 };\n\n // 文本输入状态\n private isTextInputVisible: boolean = false;\n private textInputPos: Point = { x: 0, y: 0 };\n private textInputScreenPos: Point = { x: 0, y: 0 };\n private editingTextId: string | null = null;\n\n // 调整大小状态\n private isResizing: boolean = false;\n private resizeHandle: string | null = null;\n private resizeStartBounds: { x: number; y: number; width: number; height: number } | null = null;\n private resizeOriginalObject: CanvasObject | null = null;\n\n // 历史记录\n private history: CanvasObject[][] = [];\n private clipboard: CanvasObject | null = null;\n\n // 缩放状态\n private scale: number = 1;\n private panOffset: Point = { x: 0, y: 0 };\n\n // 平移状态\n private isPanning: boolean = false;\n private panStart: Point = { x: 0, y: 0 };\n\n // 绑定的事件处理器(用于移除监听)\n private boundHandleResize: () => void;\n private boundHandleKeyDown: (e: KeyboardEvent) => void;\n private boundHandleWheel: (e: WheelEvent) => void;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n \n // 绑定事件处理器\n this.boundHandleResize = this.handleResize.bind(this);\n this.boundHandleKeyDown = this.handleKeyDown.bind(this);\n this.boundHandleWheel = this.handleWheel.bind(this);\n }\n\n // 观察的属性\n static get observedAttributes(): string[] {\n return [\n 'title', 'show-pencil', 'show-rectangle', 'show-circle', 'show-text',\n 'show-image', 'show-zoom', 'show-download', 'show-export', 'show-import',\n 'show-color', 'show-minimap', 'initial-data'\n ];\n }\n\n // 生命周期:连接到 DOM\n connectedCallback(): void {\n this.parseAttributes();\n this.render();\n this.setupEventListeners();\n this.initCanvas();\n this.loadInitialData();\n }\n\n // 生命周期:从 DOM 断开\n disconnectedCallback(): void {\n this.removeEventListeners();\n }\n\n // 生命周期:属性变化\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n // 处理 initial-data 属性变化\n if (name === 'initial-data' && newValue && this.canvas) {\n this.loadInitialData();\n return;\n }\n\n this.parseAttributes();\n if (this.container) {\n this.updateUI();\n }\n }\n\n // 解析 HTML 属性\n private parseAttributes(): void {\n this.config = {\n title: this.getAttribute('title') || defaultConfig.title,\n showPencil: this.getAttribute('show-pencil') !== 'false',\n showRectangle: this.getAttribute('show-rectangle') !== 'false',\n showCircle: this.getAttribute('show-circle') !== 'false',\n showText: this.getAttribute('show-text') !== 'false',\n showImage: this.getAttribute('show-image') !== 'false',\n showZoom: this.getAttribute('show-zoom') !== 'false',\n showDownload: this.getAttribute('show-download') !== 'false',\n showExport: this.getAttribute('show-export') !== 'false',\n showImport: this.getAttribute('show-import') !== 'false',\n showColor: this.getAttribute('show-color') !== 'false',\n showMinimap: this.getAttribute('show-minimap') !== 'false',\n };\n }\n\n // 生成唯一 ID\n private generateId(): string {\n return Math.random().toString(36).substr(2, 9);\n }\n\n // 加载初始数据\n private loadInitialData(): void {\n const initialData = this.getAttribute('initial-data');\n if (!initialData) return;\n\n try {\n const data = JSON.parse(initialData);\n if (data.objects && Array.isArray(data.objects)) {\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n } catch (err) {\n console.error('Failed to parse initial-data:', err);\n }\n }\n\n // 设置事件监听\n private setupEventListeners(): void {\n window.addEventListener('resize', this.boundHandleResize);\n window.addEventListener('keydown', this.boundHandleKeyDown);\n }\n\n // 移除事件监听\n private removeEventListeners(): void {\n window.removeEventListener('resize', this.boundHandleResize);\n window.removeEventListener('keydown', this.boundHandleKeyDown);\n if (this.canvas) {\n this.canvas.removeEventListener('wheel', this.boundHandleWheel);\n }\n }\n\n // 窗口大小变化处理\n private handleResize(): void {\n this.initCanvas();\n }\n\n // 初始化画布\n private initCanvas(): void {\n if (!this.canvasContainer || !this.canvas) return;\n\n // 使用 requestAnimationFrame 确保 DOM 已经渲染\n requestAnimationFrame(() => {\n this.canvas.width = this.canvasContainer.clientWidth;\n this.canvas.height = this.canvasContainer.clientHeight;\n this.renderCanvas();\n this.renderMinimap();\n });\n }\n\n // 获取鼠标在画布上的位置(考虑缩放和平移)\n private getMousePos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n const x = (clientX - rect.left - this.panOffset.x) / this.scale;\n const y = (clientY - rect.top - this.panOffset.y) / this.scale;\n return { x, y };\n }\n\n // 获取屏幕坐标(不考虑缩放和平移)\n private getScreenPos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n return { x: clientX - rect.left, y: clientY - rect.top };\n }\n\n // 获取对象边界\n private getObjectBounds(obj: CanvasObject): { x: number; y: number; width: number; height: number } {\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE': {\n const r = obj as RectObject | ImageObject;\n return { x: r.x, y: r.y, width: r.width, height: r.height };\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n return { x: c.x - c.radius, y: c.y - c.radius, width: c.radius * 2, height: c.radius * 2 };\n }\n case 'TEXT': {\n const t = obj as TextObject;\n const width = t.text.length * t.fontSize * 0.6;\n return { x: t.x, y: t.y - t.fontSize, width, height: t.fontSize };\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return { x: 0, y: 0, width: 0, height: 0 };\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n }\n return { x: 0, y: 0, width: 0, height: 0 };\n }\n\n // 检查调整大小手柄\n private getResizeHandleAtPoint(obj: CanvasObject, x: number, y: number): string | null {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n const handles = [\n { name: 'nw', x: bounds.x, y: bounds.y },\n { name: 'ne', x: bounds.x + bounds.width, y: bounds.y },\n { name: 'sw', x: bounds.x, y: bounds.y + bounds.height },\n { name: 'se', x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n for (const handle of handles) {\n if (Math.abs(x - handle.x) <= handleSize && Math.abs(y - handle.y) <= handleSize) {\n return handle.name;\n }\n }\n return null;\n }\n\n // 碰撞检测\n private isHit(obj: CanvasObject, x: number, y: number): boolean {\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n return x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n const dist = Math.sqrt(Math.pow(x - c.x, 2) + Math.pow(y - c.y, 2));\n return dist <= c.radius;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n return x >= img.x && x <= img.x + img.width && y >= img.y && y <= img.y + img.height;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n return x >= t.x && x <= t.x + (t.text.length * t.fontSize * 0.6) && y >= t.y - t.fontSize && y <= t.y;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return false;\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return x >= minX && x <= maxX && y >= minY && y <= maxY;\n }\n }\n return false;\n }\n\n // 保存历史\n private saveHistory(): void {\n this.history.push(JSON.parse(JSON.stringify(this.objects)));\n }\n\n // 撤销\n private undo(): void {\n if (this.history.length === 0) return;\n const previousState = this.history.pop();\n if (previousState) {\n this.objects = previousState;\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.dispatchChangeEvent();\n }\n }\n\n // 删除选中对象\n private deleteSelected(): void {\n if (this.selectedId) {\n this.saveHistory();\n this.objects = this.objects.filter(o => o.id !== this.selectedId);\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 复制选中对象\n private copySelected(): void {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.clipboard = JSON.parse(JSON.stringify(selectedObj));\n }\n }\n }\n\n // 粘贴对象\n private pasteObject(): void {\n if (this.clipboard) {\n this.saveHistory();\n const newObj = {\n ...JSON.parse(JSON.stringify(this.clipboard)),\n id: this.generateId(),\n x: this.clipboard.x + 20,\n y: this.clipboard.y + 20\n };\n if (newObj.type === 'PATH' && newObj.points) {\n newObj.points = newObj.points.map((pt: Point) => ({\n x: pt.x + 20,\n y: pt.y + 20\n }));\n }\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.clipboard = newObj;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 派发变化事件\n private dispatchChangeEvent(): void {\n this.dispatchEvent(new CustomEvent('editor-change', {\n bubbles: true,\n composed: true,\n detail: { objects: this.objects }\n }));\n }\n\n // 键盘事件处理\n private handleKeyDown(e: KeyboardEvent): void {\n if (this.isTextInputVisible) return;\n\n // Ctrl+Z: 撤销\n if ((e.ctrlKey || e.metaKey) && e.key === 'z') {\n e.preventDefault();\n this.undo();\n return;\n }\n\n // Ctrl+C: 复制\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n if (this.selectedId) {\n e.preventDefault();\n this.copySelected();\n }\n return;\n }\n\n // Ctrl+V: 粘贴\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n if (this.clipboard) {\n e.preventDefault();\n this.pasteObject();\n }\n return;\n }\n\n // Delete/Backspace: 删除\n if ((e.key === 'Delete' || e.key === 'Backspace') && this.selectedId) {\n e.preventDefault();\n this.deleteSelected();\n return;\n }\n\n // 快捷键切换工具\n if (!e.ctrlKey && !e.metaKey) {\n switch (e.key.toLowerCase()) {\n case 'v':\n this.setTool('SELECT');\n break;\n case 'p':\n case 'b':\n this.setTool('PENCIL');\n break;\n case 'r':\n this.setTool('RECTANGLE');\n break;\n case 'o':\n this.setTool('CIRCLE');\n break;\n case 't':\n this.setTool('TEXT');\n break;\n case 'escape':\n this.selectedId = null;\n this.hideTextInput();\n this.renderCanvas();\n this.updateUI();\n break;\n }\n }\n }\n\n // 滚轮缩放\n private handleWheel(e: WheelEvent): void {\n e.preventDefault();\n const rect = this.canvas.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n const delta = e.deltaY > 0 ? 0.9 : 1.1;\n const newScale = this.scale * delta;\n\n this.zoomAtPoint(newScale, mouseX, mouseY);\n }\n\n // 以指定点为中心缩放\n private zoomAtPoint(newScale: number, centerX: number, centerY: number): void {\n const clampedScale = Math.min(Math.max(newScale, 0.2), 5);\n\n const mouseXBeforeZoom = (centerX - this.panOffset.x) / this.scale;\n const mouseYBeforeZoom = (centerY - this.panOffset.y) / this.scale;\n\n const newPanOffsetX = centerX - mouseXBeforeZoom * clampedScale;\n const newPanOffsetY = centerY - mouseYBeforeZoom * clampedScale;\n\n this.scale = clampedScale;\n this.panOffset = { x: newPanOffsetX, y: newPanOffsetY };\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 放大\n private zoomIn(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale * 1.2, centerX, centerY);\n }\n\n // 缩小\n private zoomOut(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale / 1.2, centerX, centerY);\n }\n\n // 重置缩放\n private resetZoom(): void {\n this.scale = 1;\n this.panOffset = { x: 0, y: 0 };\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 更新缩放显示\n private updateZoomDisplay(): void {\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomText) {\n zoomText.textContent = `${Math.round(this.scale * 100)}%`;\n }\n }\n\n // 设置工具\n private setTool(tool: ToolType): void {\n this.tool = tool;\n this.updateToolButtons();\n }\n\n // 更新工具按钮状态\n private updateToolButtons(): void {\n const buttons = this.shadow.querySelectorAll('.tool-btn');\n buttons.forEach(btn => {\n const btnTool = btn.getAttribute('data-tool');\n if (btnTool === this.tool) {\n btn.classList.add('active');\n } else {\n btn.classList.remove('active');\n }\n });\n }\n\n // 隐藏文本输入\n private hideTextInput(): void {\n this.isTextInputVisible = false;\n if (this.textInputContainer) {\n this.textInputContainer.style.display = 'none';\n }\n this.editingTextId = null;\n }\n\n // 显示文本输入\n private showTextInput(screenX: number, screenY: number, text: string = ''): void {\n this.isTextInputVisible = true;\n this.textInputScreenPos = { x: screenX, y: screenY };\n\n if (this.textInputContainer && this.textInput) {\n this.textInputContainer.style.display = 'block';\n this.textInputContainer.style.left = `${screenX}px`;\n this.textInputContainer.style.top = `${screenY - 30}px`;\n this.textInput.value = text;\n this.textInput.style.color = this.color;\n setTimeout(() => {\n this.textInput.focus();\n if (text) this.textInput.select();\n }, 0);\n }\n }\n\n // 提交文本\n private submitText(): void {\n const value = this.textInput?.value?.trim();\n if (value) {\n if (this.editingTextId) {\n const existingObj = this.objects.find(o => o.id === this.editingTextId) as TextObject | undefined;\n if (existingObj && existingObj.text !== value) {\n this.saveHistory();\n existingObj.text = value;\n }\n this.selectedId = this.editingTextId;\n } else {\n this.saveHistory();\n const newObj: TextObject = {\n id: this.generateId(),\n type: 'TEXT',\n x: this.textInputPos.x,\n y: this.textInputPos.y,\n text: value,\n fontSize: 24,\n color: this.color,\n lineWidth: this.lineWidth\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n }\n this.dispatchChangeEvent();\n }\n this.hideTextInput();\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 画布鼠标按下\n private handleCanvasPointerDown(e: MouseEvent | TouchEvent): void {\n const { x, y } = this.getMousePos(e);\n const screenPos = this.getScreenPos(e);\n this.dragStart = { x, y };\n this.isDragging = true;\n\n // 如果文本输入可见且不是文本工具,先保存文本\n if (this.isTextInputVisible && this.tool !== 'TEXT') {\n this.submitText();\n }\n\n if (this.tool === 'SELECT') {\n // 检查是否点击调整大小手柄\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const handle = this.getResizeHandleAtPoint(selectedObj, x, y);\n if (handle) {\n this.saveHistory();\n this.isResizing = true;\n this.resizeHandle = handle;\n this.resizeStartBounds = this.getObjectBounds(selectedObj);\n this.resizeOriginalObject = JSON.parse(JSON.stringify(selectedObj));\n return;\n }\n }\n }\n\n // 查找点击的对象\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject) {\n this.selectedId = clickedObject.id;\n this.dragOffset = { x: x - clickedObject.x, y: y - clickedObject.y };\n this.saveHistory();\n this.updateUI();\n } else {\n // 开始拖拽画布\n this.selectedId = null;\n this.isPanning = true;\n this.panStart = screenPos;\n this.updateUI();\n }\n } else if (this.tool === 'TEXT') {\n // 显示文本输入\n this.textInputPos = { x, y };\n this.showTextInput(screenPos.x, screenPos.y);\n this.isDragging = false;\n } else {\n // 开始绘制图形\n this.saveHistory();\n const id = this.generateId();\n if (this.tool === 'RECTANGLE') {\n this.currentObject = { id, type: 'RECTANGLE', x, y, width: 0, height: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'CIRCLE') {\n this.currentObject = { id, type: 'CIRCLE', x, y, radius: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'PENCIL') {\n this.currentObject = { id, type: 'PATH', x, y, points: [{ x, y }], color: this.color, lineWidth: this.lineWidth };\n }\n }\n\n this.renderCanvas();\n }\n\n // 画布鼠标移动\n private handleCanvasPointerMove(e: MouseEvent | TouchEvent): void {\n // 处理画布拖拽\n if (this.isPanning) {\n const screenPos = this.getScreenPos(e);\n const dx = screenPos.x - this.panStart.x;\n const dy = screenPos.y - this.panStart.y;\n this.panOffset = { x: this.panOffset.x + dx, y: this.panOffset.y + dy };\n this.panStart = screenPos;\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n if (!this.isDragging || !this.dragStart) return;\n const { x, y } = this.getMousePos(e);\n\n // 处理调整大小\n if (this.isResizing && this.selectedId && this.resizeHandle && this.resizeStartBounds && this.resizeOriginalObject) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (!obj) return;\n\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n let newX = this.resizeStartBounds.x;\n let newY = this.resizeStartBounds.y;\n let newWidth = this.resizeStartBounds.width;\n let newHeight = this.resizeStartBounds.height;\n\n if (this.resizeHandle.includes('e')) newWidth = this.resizeStartBounds.width + dx;\n if (this.resizeHandle.includes('w')) {\n newX = this.resizeStartBounds.x + dx;\n newWidth = this.resizeStartBounds.width - dx;\n }\n if (this.resizeHandle.includes('s')) newHeight = this.resizeStartBounds.height + dy;\n if (this.resizeHandle.includes('n')) {\n newY = this.resizeStartBounds.y + dy;\n newHeight = this.resizeStartBounds.height - dy;\n }\n\n newWidth = Math.max(10, newWidth);\n newHeight = Math.max(10, newHeight);\n\n // 根据对象类型应用变化\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE':\n (obj as RectObject | ImageObject).x = newX;\n (obj as RectObject | ImageObject).y = newY;\n (obj as RectObject | ImageObject).width = newWidth;\n (obj as RectObject | ImageObject).height = newHeight;\n break;\n case 'CIRCLE': {\n const radius = Math.max(newWidth, newHeight) / 2;\n (obj as CircleObject).x = newX + radius;\n (obj as CircleObject).y = newY + radius;\n (obj as CircleObject).radius = radius;\n break;\n }\n case 'TEXT': {\n const origT = this.resizeOriginalObject as TextObject;\n const scaleFactor = newWidth / this.resizeStartBounds.width;\n (obj as TextObject).x = newX;\n (obj as TextObject).y = newY + newHeight;\n (obj as TextObject).fontSize = Math.max(8, Math.round(origT.fontSize * scaleFactor));\n break;\n }\n case 'PATH': {\n const origP = this.resizeOriginalObject as PathObject;\n const scaleX = newWidth / this.resizeStartBounds.width;\n const scaleY = newHeight / this.resizeStartBounds.height;\n (obj as PathObject).points = origP.points.map(pt => ({\n x: newX + (pt.x - this.resizeStartBounds!.x) * scaleX,\n y: newY + (pt.y - this.resizeStartBounds!.y) * scaleY\n }));\n break;\n }\n }\n\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n // 移动选中对象\n if (this.tool === 'SELECT' && this.selectedId) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (obj) {\n if (obj.type === 'PATH') {\n const p = obj as PathObject;\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n p.points = p.points.map(pt => ({ x: pt.x + dx, y: pt.y + dy }));\n this.dragStart = { x, y };\n } else {\n obj.x = x - this.dragOffset.x;\n obj.y = y - this.dragOffset.y;\n }\n this.renderCanvas();\n this.renderMinimap();\n }\n } else if (this.currentObject) {\n // 更新正在绘制的图形\n if (this.currentObject.type === 'RECTANGLE') {\n (this.currentObject as RectObject).width = x - this.currentObject.x;\n (this.currentObject as RectObject).height = y - this.currentObject.y;\n } else if (this.currentObject.type === 'CIRCLE') {\n const radius = Math.sqrt(Math.pow(x - this.currentObject.x, 2) + Math.pow(y - this.currentObject.y, 2));\n (this.currentObject as CircleObject).radius = radius;\n } else if (this.currentObject.type === 'PATH') {\n (this.currentObject as PathObject).points.push({ x, y });\n }\n this.renderCanvas();\n }\n }\n\n // 画布鼠标抬起\n private handleCanvasPointerUp(): void {\n this.isDragging = false;\n this.dragStart = null;\n this.isResizing = false;\n this.resizeHandle = null;\n this.resizeStartBounds = null;\n this.resizeOriginalObject = null;\n this.isPanning = false;\n\n if (this.currentObject) {\n this.objects.push(this.currentObject);\n this.currentObject = null;\n this.dispatchChangeEvent();\n }\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 双击编辑文本\n private handleCanvasDoubleClick(e: MouseEvent): void {\n e.preventDefault();\n const { x, y } = this.getMousePos(e);\n\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject && clickedObject.type === 'TEXT') {\n const textObj = clickedObject as TextObject;\n this.editingTextId = textObj.id;\n this.textInputPos = { x: textObj.x, y: textObj.y };\n const screenX = textObj.x * this.scale + this.panOffset.x;\n const screenY = textObj.y * this.scale + this.panOffset.y;\n this.showTextInput(screenX, screenY, textObj.text);\n this.setTool('SELECT');\n }\n }\n\n // 渲染画布\n private renderCanvas(): void {\n if (!this.ctx) return;\n\n // 清空画布\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 绘制背景\n this.ctx.fillStyle = '#ffffff';\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 应用缩放和平移\n this.ctx.save();\n this.ctx.translate(this.panOffset.x, this.panOffset.y);\n this.ctx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(this.ctx, obj, false));\n\n // 绘制正在创建的对象\n if (this.currentObject) {\n this.drawObject(this.ctx, this.currentObject, false);\n }\n\n // 绘制选中对象的调整手柄\n if (this.selectedId && this.tool === 'SELECT') {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.drawSelectionHandles(this.ctx, selectedObj);\n }\n }\n\n this.ctx.restore();\n }\n\n // 绘制单个对象\n private drawObject(ctx: CanvasRenderingContext2D, obj: CanvasObject, isMinimap: boolean): void {\n ctx.beginPath();\n ctx.strokeStyle = obj.color;\n ctx.lineWidth = obj.lineWidth;\n ctx.fillStyle = obj.color;\n\n // 选中高亮(仅主画布)\n if (!isMinimap && obj.id === this.selectedId) {\n ctx.shadowColor = 'rgba(0, 100, 255, 0.5)';\n ctx.shadowBlur = 10;\n } else {\n ctx.shadowBlur = 0;\n }\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n ctx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n ctx.beginPath();\n ctx.arc(c.x, c.y, c.radius, 0, 2 * Math.PI);\n ctx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length < 2) break;\n ctx.beginPath();\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(p.points[0].x, p.points[0].y);\n for (let i = 1; i < p.points.length; i++) {\n ctx.lineTo(p.points[i].x, p.points[i].y);\n }\n ctx.stroke();\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n ctx.font = `${t.fontSize}px sans-serif`;\n ctx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const imgObj = obj as ImageObject;\n if (imgObj.imageElement && imgObj.imageElement.complete) {\n ctx.drawImage(imgObj.imageElement, imgObj.x, imgObj.y, imgObj.width, imgObj.height);\n } else if (imgObj.dataUrl) {\n // 加载图片\n const img = new Image();\n img.onload = () => {\n imgObj.imageElement = img;\n this.renderCanvas();\n };\n img.src = imgObj.dataUrl;\n }\n break;\n }\n }\n }\n\n // 绘制选中手柄\n private drawSelectionHandles(ctx: CanvasRenderingContext2D, obj: CanvasObject): void {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n ctx.shadowBlur = 0;\n ctx.fillStyle = '#3b82f6';\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n\n // 绘制角落手柄\n const corners = [\n { x: bounds.x, y: bounds.y },\n { x: bounds.x + bounds.width, y: bounds.y },\n { x: bounds.x, y: bounds.y + bounds.height },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n corners.forEach(corner => {\n ctx.beginPath();\n ctx.rect(corner.x - handleSize / 2, corner.y - handleSize / 2, handleSize, handleSize);\n ctx.fill();\n ctx.stroke();\n });\n\n // 绘制选择边框\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 1;\n ctx.setLineDash([5, 5]);\n ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);\n ctx.setLineDash([]);\n }\n\n // 渲染小地图\n private renderMinimap(): void {\n if (!this.minimapCtx || !this.config.showMinimap) return;\n\n const minimap = this.minimapCanvas;\n const mainCanvas = this.canvas;\n\n this.minimapCtx.clearRect(0, 0, minimap.width, minimap.height);\n\n // 计算缩放比例\n const scaleX = minimap.width / mainCanvas.width;\n const scaleY = minimap.height / mainCanvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = mainCanvas.width * minimapScale;\n const canvasH = mainCanvas.height * minimapScale;\n const offsetX = (minimap.width - canvasW) / 2;\n const offsetY = (minimap.height - canvasH) / 2;\n\n // 绘制画布区域背景\n this.minimapCtx.fillStyle = '#ffffff';\n this.minimapCtx.fillRect(offsetX, offsetY, canvasW, canvasH);\n\n // 应用主画布的缩放和平移变换\n this.minimapCtx.save();\n this.minimapCtx.translate(offsetX, offsetY);\n this.minimapCtx.scale(minimapScale, minimapScale);\n this.minimapCtx.translate(this.panOffset.x, this.panOffset.y);\n this.minimapCtx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n const allObjects = this.currentObject ? [...this.objects, this.currentObject] : this.objects;\n allObjects.forEach(obj => {\n this.minimapCtx.fillStyle = obj.color;\n this.minimapCtx.strokeStyle = obj.color;\n this.minimapCtx.lineWidth = Math.max(obj.lineWidth, 1);\n this.minimapCtx.setLineDash([]);\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n this.minimapCtx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n this.minimapCtx.beginPath();\n this.minimapCtx.arc(c.x, c.y, c.radius, 0, Math.PI * 2);\n this.minimapCtx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length > 0) {\n this.minimapCtx.beginPath();\n this.minimapCtx.lineCap = 'round';\n this.minimapCtx.lineJoin = 'round';\n this.minimapCtx.moveTo(p.points[0].x, p.points[0].y);\n p.points.forEach(pt => this.minimapCtx.lineTo(pt.x, pt.y));\n this.minimapCtx.stroke();\n }\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n this.minimapCtx.font = `${t.fontSize}px sans-serif`;\n this.minimapCtx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n if (img.imageElement) {\n this.minimapCtx.drawImage(img.imageElement, img.x, img.y, img.width, img.height);\n }\n break;\n }\n }\n });\n\n this.minimapCtx.restore();\n\n // 绘制画布边框\n this.minimapCtx.strokeStyle = '#94a3b8';\n this.minimapCtx.lineWidth = 1;\n this.minimapCtx.strokeRect(offsetX, offsetY, canvasW, canvasH);\n }\n\n // 小地图点击定位\n private handleMinimapClick(e: MouseEvent): void {\n const rect = this.minimapCanvas.getBoundingClientRect();\n const clickX = e.clientX - rect.left;\n const clickY = e.clientY - rect.top;\n\n const scaleX = this.minimapCanvas.width / this.canvas.width;\n const scaleY = this.minimapCanvas.height / this.canvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = this.canvas.width * minimapScale;\n const canvasH = this.canvas.height * minimapScale;\n const offsetX = (this.minimapCanvas.width - canvasW) / 2;\n const offsetY = (this.minimapCanvas.height - canvasH) / 2;\n\n const relX = clickX - offsetX;\n const relY = clickY - offsetY;\n\n const canvasX = (relX / minimapScale - this.panOffset.x) / this.scale;\n const canvasY = (relY / minimapScale - this.panOffset.y) / this.scale;\n\n const viewportCenterX = this.canvas.width / 2;\n const viewportCenterY = this.canvas.height / 2;\n\n this.panOffset = {\n x: viewportCenterX / this.scale - canvasX,\n y: viewportCenterY / this.scale - canvasY\n };\n\n this.renderCanvas();\n this.renderMinimap();\n }\n\n // 图片上传处理\n private handleImageUpload(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n const dataUrl = event.target?.result as string;\n const img = new Image();\n img.onload = () => {\n this.saveHistory();\n const maxSize = 300;\n let width = img.width;\n let height = img.height;\n if (width > maxSize || height > maxSize) {\n const ratio = Math.min(maxSize / width, maxSize / height);\n width *= ratio;\n height *= ratio;\n }\n\n const newObj: ImageObject = {\n id: this.generateId(),\n type: 'IMAGE',\n x: 100,\n y: 100,\n width,\n height,\n color: '#000000',\n lineWidth: 1,\n dataUrl,\n imageElement: img\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n };\n img.src = dataUrl;\n };\n\n reader.readAsDataURL(file);\n input.value = '';\n }\n\n // 保存 JSON\n private saveJson(): void {\n const data = {\n version: '1.0',\n objects: this.objects.map(obj => {\n const { imageElement, ...rest } = obj as ImageObject;\n return rest;\n })\n };\n const json = JSON.stringify(data, null, 2);\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-project.json';\n a.click();\n URL.revokeObjectURL(url);\n }\n\n // 加载 JSON\n private loadJson(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n try {\n const data = JSON.parse(event.target?.result as string);\n if (data.objects && Array.isArray(data.objects)) {\n this.saveHistory();\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n } catch (err) {\n console.error('Failed to load JSON:', err);\n }\n };\n\n reader.readAsText(file);\n input.value = '';\n }\n\n // 导出 PNG\n private exportPng(): void {\n // 创建临时画布\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = this.canvas.width;\n tempCanvas.height = this.canvas.height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n // 绘制白色背景\n tempCtx.fillStyle = '#ffffff';\n tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);\n\n // 应用缩放和平移\n tempCtx.translate(this.panOffset.x, this.panOffset.y);\n tempCtx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(tempCtx, obj, true));\n\n // 下载\n const url = tempCanvas.toDataURL('image/png');\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-export.png';\n a.click();\n }\n\n // 更新 UI\n private updateUI(): void {\n // 更新选中状态显示\n const selectionInfo = this.shadow.querySelector('.selection-info');\n if (selectionInfo) {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const typeLabels: Record<string, string> = {\n 'RECTANGLE': '矩形',\n 'CIRCLE': '圆形',\n 'PATH': '画笔',\n 'TEXT': '文本',\n 'IMAGE': '图片'\n };\n const typeLabel = typeLabels[selectedObj.type] || selectedObj.type;\n selectionInfo.innerHTML = `\n <span class=\"selection-label\">已选择: ${typeLabel}</span>\n <button class=\"delete-btn\" title=\"删除\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\"/>\n </svg>\n </button>\n `;\n selectionInfo.classList.add('visible');\n const deleteBtn = selectionInfo.querySelector('.delete-btn');\n if (deleteBtn) {\n deleteBtn.addEventListener('click', () => this.deleteSelected());\n }\n }\n } else {\n selectionInfo.classList.remove('visible');\n selectionInfo.innerHTML = '';\n }\n }\n\n // 更新撤销按钮状态\n const undoBtn = this.shadow.querySelector('.undo-btn') as HTMLButtonElement;\n if (undoBtn) {\n undoBtn.disabled = this.history.length === 0;\n }\n\n // 更新空画布提示显示\n const emptyHint = this.shadow.querySelector('.empty-hint') as HTMLElement;\n if (emptyHint) {\n emptyHint.style.display = this.objects.length === 0 ? 'flex' : 'none';\n }\n }\n\n // 渲染 DOM 结构\n private render(): void {\n this.shadow.innerHTML = `\n <style>${this.getStyles()}</style>\n <div class=\"editor-container\">\n <!-- 左侧工具栏 -->\n <div class=\"toolbar\">\n ${this.createToolButton('SELECT', 'select-icon', '选择 (V)')}\n <div class=\"divider\"></div>\n ${this.config.showPencil ? this.createToolButton('PENCIL', 'pencil-icon', '画笔 (P)') : ''}\n ${this.config.showRectangle ? this.createToolButton('RECTANGLE', 'rect-icon', '矩形 (R)') : ''}\n ${this.config.showCircle ? this.createToolButton('CIRCLE', 'circle-icon', '圆形 (O)') : ''}\n ${this.config.showText ? this.createToolButton('TEXT', 'text-icon', '文本 (T)') : ''}\n ${this.config.showImage ? `\n <label class=\"tool-btn\" title=\"插入图片\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/>\n <path d=\"M21 15l-5-5L5 21\"/>\n </svg>\n <input type=\"file\" accept=\"image/*\" class=\"hidden image-input\" />\n </label>\n ` : ''}\n <div class=\"divider\"></div>\n <button class=\"tool-btn undo-btn\" title=\"撤销 (Ctrl+Z)\" disabled>\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M1 4v6h6\"/>\n <path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>\n </svg>\n </button>\n <div class=\"spacer\"></div>\n ${this.config.showColor ? `\n <input type=\"color\" class=\"color-picker\" value=\"${this.color}\" title=\"颜色\" />\n ` : ''}\n </div>\n\n <!-- 主区域 -->\n <div class=\"main-area\">\n <!-- 顶部栏 -->\n <div class=\"top-bar\">\n <div class=\"top-bar-left\">\n <h2 class=\"title\">${this.config.title}</h2>\n <div class=\"selection-info\"></div>\n </div>\n <div class=\"top-bar-right\">\n ${this.config.showZoom ? `\n <div class=\"zoom-controls\">\n <button class=\"zoom-btn zoom-out-btn\" title=\"缩小\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M8 11h6\"/>\n </svg>\n </button>\n <button class=\"zoom-text\" title=\"重置缩放\">100%</button>\n <button class=\"zoom-btn zoom-in-btn\" title=\"放大\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M11 8v6M8 11h6\"/>\n </svg>\n </button>\n </div>\n ` : ''}\n ${(this.config.showExport || this.config.showImport || this.config.showDownload) ? `\n <div class=\"file-controls\">\n ${this.config.showExport ? `\n <button class=\"file-btn save-json-btn\" title=\"保存项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z\"/>\n <polyline points=\"17 21 17 13 7 13 7 21\"/><polyline points=\"7 3 7 8 15 8\"/>\n </svg>\n </button>\n ` : ''}\n ${this.config.showImport ? `\n <label class=\"file-btn\" title=\"加载项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z\"/>\n </svg>\n <input type=\"file\" accept=\".json\" class=\"hidden load-json-input\" />\n </label>\n ` : ''}\n ${this.config.showDownload ? `\n <button class=\"file-btn export-png-btn\" title=\"导出 PNG\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3\"/>\n </svg>\n </button>\n ` : ''}\n </div>\n ` : ''}\n </div>\n </div>\n\n <!-- 画布容器 -->\n <div class=\"canvas-container\">\n <canvas class=\"main-canvas\"></canvas>\n\n ${this.config.showMinimap ? `\n <div class=\"minimap-wrapper\">\n <div class=\"minimap-header\">\n <svg class=\"minimap-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M3 9h18M9 21V9\"/>\n </svg>\n <span>导航</span>\n </div>\n <canvas class=\"minimap-canvas\" width=\"220\" height=\"140\"></canvas>\n </div>\n ` : ''}\n\n <!-- 文本输入 -->\n <div class=\"text-input-container\" style=\"display: none;\">\n <div class=\"text-input-hint\">按 Enter 确认,Esc 取消</div>\n <input type=\"text\" class=\"text-input\" placeholder=\"输入文本...\" />\n </div>\n\n <!-- 空画布提示 -->\n <div class=\"empty-hint\">\n <h3>开始创作</h3>\n <p>选择左侧的工具开始绘制</p>\n </div>\n </div>\n </div>\n </div>\n `;\n\n // 获取 DOM 引用\n this.container = this.shadow.querySelector('.editor-container')!;\n this.toolbar = this.shadow.querySelector('.toolbar')!;\n this.topBar = this.shadow.querySelector('.top-bar')!;\n this.canvasContainer = this.shadow.querySelector('.canvas-container')!;\n this.canvas = this.shadow.querySelector('.main-canvas')!;\n this.ctx = this.canvas.getContext('2d')!;\n\n if (this.config.showMinimap) {\n this.minimapCanvas = this.shadow.querySelector('.minimap-canvas')!;\n this.minimapCtx = this.minimapCanvas.getContext('2d')!;\n }\n\n this.textInputContainer = this.shadow.querySelector('.text-input-container')!;\n this.textInput = this.shadow.querySelector('.text-input')!;\n\n // 绑定事件\n this.bindEvents();\n }\n\n // 绑定事件\n private bindEvents(): void {\n // 画布事件\n this.canvas.addEventListener('mousedown', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('mousemove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('mouseup', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('mouseleave', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('dblclick', (e) => this.handleCanvasDoubleClick(e));\n this.canvas.addEventListener('touchstart', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('touchmove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('touchend', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('wheel', this.boundHandleWheel, { passive: false });\n\n // 工具按钮\n this.shadow.querySelectorAll('.tool-btn[data-tool]').forEach(btn => {\n btn.addEventListener('mousedown', (e) => {\n // 阻止 blur 事件触发,在点击后手动处理\n e.preventDefault();\n });\n btn.addEventListener('click', () => {\n // 如果有文本输入,先提交\n if (this.isTextInputVisible) {\n this.submitText();\n }\n const tool = btn.getAttribute('data-tool') as ToolType;\n this.setTool(tool);\n });\n });\n\n // 撤销按钮\n const undoBtn = this.shadow.querySelector('.undo-btn');\n if (undoBtn) {\n undoBtn.addEventListener('click', () => this.undo());\n }\n\n // 颜色选择器\n const colorPicker = this.shadow.querySelector('.color-picker') as HTMLInputElement;\n if (colorPicker) {\n colorPicker.addEventListener('input', (e) => {\n this.color = (e.target as HTMLInputElement).value;\n });\n }\n\n // 图片上传\n const imageInput = this.shadow.querySelector('.image-input');\n if (imageInput) {\n imageInput.addEventListener('change', (e) => this.handleImageUpload(e));\n }\n\n // 缩放按钮\n const zoomInBtn = this.shadow.querySelector('.zoom-in-btn');\n const zoomOutBtn = this.shadow.querySelector('.zoom-out-btn');\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomInBtn) zoomInBtn.addEventListener('click', () => this.zoomIn());\n if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => this.zoomOut());\n if (zoomText) zoomText.addEventListener('click', () => this.resetZoom());\n\n // 文件操作\n const saveJsonBtn = this.shadow.querySelector('.save-json-btn');\n const loadJsonInput = this.shadow.querySelector('.load-json-input');\n const exportPngBtn = this.shadow.querySelector('.export-png-btn');\n if (saveJsonBtn) saveJsonBtn.addEventListener('click', () => this.saveJson());\n if (loadJsonInput) loadJsonInput.addEventListener('change', (e) => this.loadJson(e));\n if (exportPngBtn) exportPngBtn.addEventListener('click', () => this.exportPng());\n\n // 小地图点击\n if (this.minimapCanvas) {\n this.minimapCanvas.addEventListener('click', (e) => this.handleMinimapClick(e));\n }\n\n // 文本输入\n if (this.textInput) {\n this.textInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.submitText();\n } else if (e.key === 'Escape') {\n this.hideTextInput();\n }\n });\n this.textInput.addEventListener('blur', () => {\n if (this.isTextInputVisible) {\n this.submitText();\n }\n });\n }\n }\n\n // 创建工具按钮 HTML\n private createToolButton(tool: ToolType, iconClass: string, title: string): string {\n const icons: Record<string, string> = {\n 'select-icon': '<path d=\"M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z\"/><path d=\"M13 13l6 6\"/>',\n 'pencil-icon': '<path d=\"M17 3a2.85 2.85 0 114 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>',\n 'rect-icon': '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>',\n 'circle-icon': '<circle cx=\"12\" cy=\"12\" r=\"10\"/>',\n 'text-icon': '<path d=\"M4 7V4h16v3M9 20h6M12 4v16\"/>',\n };\n const isActive = this.tool === tool;\n return `\n <button class=\"tool-btn ${isActive ? 'active' : ''}\" data-tool=\"${tool}\" title=\"${title}\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n ${icons[iconClass]}\n </svg>\n </button>\n `;\n }\n\n // 获取样式\n private getStyles(): string {\n return `\n :host {\n display: block;\n width: 100%;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n .hidden {\n display: none !important;\n }\n\n .editor-container {\n display: flex;\n width: 100%;\n height: 100%;\n background: #f1f5f9;\n }\n\n /* 工具栏 */\n .toolbar {\n width: 64px;\n background: #ffffff;\n border-right: 1px solid #e2e8f0;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 8px;\n gap: 4px;\n }\n\n .tool-btn {\n width: 44px;\n height: 44px;\n border: none;\n background: transparent;\n border-radius: 12px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #64748b;\n transition: all 0.2s;\n }\n\n .tool-btn:hover {\n background: #f1f5f9;\n color: #4f46e5;\n }\n\n .tool-btn.active {\n background: #4f46e5;\n color: #ffffff;\n box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);\n transform: scale(1.05);\n }\n\n .tool-btn:disabled {\n color: #cbd5e1;\n cursor: not-allowed;\n }\n\n .tool-btn:disabled:hover {\n background: transparent;\n color: #cbd5e1;\n }\n\n .icon {\n width: 20px;\n height: 20px;\n }\n\n .divider {\n width: 32px;\n height: 1px;\n background: #e2e8f0;\n margin: 8px 0;\n }\n\n .spacer {\n flex: 1;\n }\n\n .color-picker {\n width: 32px;\n height: 32px;\n border: 2px solid #e2e8f0;\n border-radius: 50%;\n cursor: pointer;\n padding: 0;\n overflow: hidden;\n -webkit-appearance: none;\n }\n\n .color-picker::-webkit-color-swatch-wrapper {\n padding: 0;\n }\n\n .color-picker::-webkit-color-swatch {\n border: none;\n border-radius: 50%;\n }\n\n /* 主区域 */\n .main-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* 顶部栏 */\n .top-bar {\n height: 56px;\n background: #ffffff;\n border-bottom: 1px solid #e2e8f0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 16px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n }\n\n .top-bar-left, .top-bar-right {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .title {\n font-size: 16px;\n font-weight: 600;\n color: #334155;\n }\n\n .selection-info {\n display: none;\n align-items: center;\n gap: 8px;\n background: #eef2ff;\n padding: 4px 12px;\n border-radius: 20px;\n border: 1px solid #c7d2fe;\n }\n\n .selection-info.visible {\n display: flex;\n }\n\n .selection-label {\n font-size: 12px;\n font-weight: 600;\n color: #4f46e5;\n text-transform: uppercase;\n }\n\n .delete-btn {\n background: none;\n border: none;\n color: #ef4444;\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .delete-btn:hover {\n color: #dc2626;\n }\n\n .zoom-controls, .file-controls {\n display: flex;\n align-items: center;\n background: #f1f5f9;\n border-radius: 8px;\n padding: 4px;\n }\n\n .zoom-btn, .file-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #475569;\n transition: all 0.2s;\n }\n\n .zoom-btn:hover, .file-btn:hover {\n color: #4f46e5;\n }\n\n .zoom-text {\n padding: 4px 8px;\n font-size: 12px;\n font-weight: 500;\n color: #475569;\n background: transparent;\n border: none;\n cursor: pointer;\n min-width: 50px;\n text-align: center;\n }\n\n .zoom-text:hover {\n color: #4f46e5;\n }\n\n /* 画布容器 */\n .canvas-container {\n flex: 1;\n position: relative;\n background: #f1f5f9;\n overflow: hidden;\n }\n\n .main-canvas {\n position: absolute;\n inset: 0;\n display: block;\n cursor: crosshair;\n touch-action: none;\n }\n\n /* 小地图 */\n .minimap-wrapper {\n position: absolute;\n top: 16px;\n right: 16px;\n z-index: 30;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n padding: 2px;\n }\n\n .minimap-wrapper > * {\n background: #ffffff;\n }\n\n .minimap-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: linear-gradient(to right, #f8fafc, #f1f5f9);\n border-bottom: 1px solid #e2e8f0;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n border-radius: 10px 10px 0 0;\n }\n\n .minimap-icon {\n width: 12px;\n height: 12px;\n color: #4f46e5;\n }\n\n .minimap-canvas {\n cursor: pointer;\n background: #f8fafc;\n border-radius: 0 0 10px 10px;\n display: block;\n }\n\n .minimap-canvas:hover {\n filter: brightness(1.05);\n }\n\n /* 文本输入 */\n .text-input-container {\n position: absolute;\n z-index: 20;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n }\n\n .text-input-hint {\n background: rgba(0, 0, 0, 0.75);\n color: #fff;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n margin-bottom: 4px;\n white-space: nowrap;\n }\n\n .text-input {\n padding: 8px 12px;\n border: 2px solid #4f46e5;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n outline: none;\n min-width: 200px;\n font-size: 16px;\n }\n\n /* 空画布提示 */\n .empty-hint {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n opacity: 0.4;\n }\n\n .empty-hint h3 {\n font-size: 24px;\n font-weight: 700;\n color: #94a3b8;\n margin-bottom: 8px;\n }\n\n .empty-hint p {\n color: #94a3b8;\n }\n `;\n }\n}\n\n// 注册 Web Component\nif (typeof window !== 'undefined' && !customElements.get('canvas-drawing-editor')) {\n customElements.define('canvas-drawing-editor', CanvasDrawingEditor);\n}\n"],"names":["defaultConfig","CanvasDrawingEditor","name","oldValue","newValue","initialData","data","obj","img","err","e","rect","clientX","clientY","x","y","r","c","t","width","p","minX","pt","maxX","minY","maxY","bounds","handleSize","handles","handle","previousState","o","selectedObj","newObj","mouseX","mouseY","delta","newScale","centerX","centerY","clampedScale","mouseXBeforeZoom","mouseYBeforeZoom","newPanOffsetX","newPanOffsetY","zoomText","tool","btn","screenX","screenY","text","value","_b","_a","existingObj","screenPos","clickedObject","id","dx","dy","newX","newY","newWidth","newHeight","radius","origT","scaleFactor","origP","scaleX","scaleY","textObj","ctx","isMinimap","i","imgObj","corner","minimap","mainCanvas","minimapScale","canvasW","canvasH","offsetX","offsetY","clickX","clickY","relX","relY","canvasX","canvasY","viewportCenterX","viewportCenterY","input","file","reader","event","dataUrl","maxSize","height","ratio","imageElement","rest","json","blob","url","a","tempCanvas","tempCtx","selectionInfo","typeLabel","deleteBtn","undoBtn","emptyHint","colorPicker","imageInput","zoomInBtn","zoomOutBtn","saveJsonBtn","loadJsonInput","exportPngBtn","iconClass","title","icons"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"canvas-drawing-editor.umd.js","sources":["../src/core/CanvasDrawingEditor.ts"],"sourcesContent":["/**\n * Canvas Drawing Editor - 纯 JavaScript Web Component\n * 无任何框架依赖\n */\n\n// 类型定义\nexport type ToolType = 'SELECT' | 'PENCIL' | 'RECTANGLE' | 'CIRCLE' | 'TEXT' | 'IMAGE';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface BaseObject {\n id: string;\n type: string;\n x: number;\n y: number;\n color: string;\n lineWidth: number;\n}\n\nexport interface RectObject extends BaseObject {\n type: 'RECTANGLE';\n width: number;\n height: number;\n}\n\nexport interface CircleObject extends BaseObject {\n type: 'CIRCLE';\n radius: number;\n}\n\nexport interface PathObject extends BaseObject {\n type: 'PATH';\n points: Point[];\n}\n\nexport interface TextObject extends BaseObject {\n type: 'TEXT';\n text: string;\n fontSize: number;\n}\n\nexport interface ImageObject extends BaseObject {\n type: 'IMAGE';\n width: number;\n height: number;\n dataUrl: string;\n imageElement?: HTMLImageElement;\n}\n\nexport type CanvasObject = RectObject | CircleObject | PathObject | TextObject | ImageObject;\n\nexport interface EditorConfig {\n title?: string;\n showPencil?: boolean;\n showRectangle?: boolean;\n showCircle?: boolean;\n showText?: boolean;\n showImage?: boolean;\n showZoom?: boolean;\n showDownload?: boolean;\n showExport?: boolean;\n showImport?: boolean;\n showColor?: boolean;\n showMinimap?: boolean;\n}\n\n// 默认配置\nconst defaultConfig: EditorConfig = {\n title: 'Canvas Editor',\n showPencil: true,\n showRectangle: true,\n showCircle: true,\n showText: true,\n showImage: true,\n showZoom: true,\n showDownload: true,\n showExport: true,\n showImport: true,\n showColor: true,\n showMinimap: true,\n};\n\n/**\n * Canvas Drawing Editor Web Component\n */\nexport class CanvasDrawingEditor extends HTMLElement {\n // Shadow DOM\n private shadow: ShadowRoot;\n\n // DOM 元素\n private container!: HTMLDivElement;\n private toolbar!: HTMLDivElement;\n private topBar!: HTMLDivElement;\n private canvasContainer!: HTMLDivElement;\n private canvas!: HTMLCanvasElement;\n private ctx!: CanvasRenderingContext2D;\n private minimapCanvas!: HTMLCanvasElement;\n private minimapCtx!: CanvasRenderingContext2D;\n private textInput!: HTMLInputElement;\n private textInputContainer!: HTMLDivElement;\n\n // 配置\n private config: EditorConfig = { ...defaultConfig };\n\n // 状态\n private objects: CanvasObject[] = [];\n private selectedId: string | null = null;\n private tool: ToolType = 'SELECT';\n private color: string = '#000000';\n private lineWidth: number = 3;\n\n // 交互状态\n private isDragging: boolean = false;\n private dragStart: Point | null = null;\n private currentObject: CanvasObject | null = null;\n private dragOffset: Point = { x: 0, y: 0 };\n\n // 文本输入状态\n private isTextInputVisible: boolean = false;\n private textInputPos: Point = { x: 0, y: 0 };\n private textInputScreenPos: Point = { x: 0, y: 0 };\n private editingTextId: string | null = null;\n\n // 调整大小状态\n private isResizing: boolean = false;\n private resizeHandle: string | null = null;\n private resizeStartBounds: { x: number; y: number; width: number; height: number } | null = null;\n private resizeOriginalObject: CanvasObject | null = null;\n\n // 历史记录\n private history: CanvasObject[][] = [];\n private clipboard: CanvasObject | null = null;\n\n // 缩放状态\n private scale: number = 1;\n private panOffset: Point = { x: 0, y: 0 };\n\n // 平移状态\n private isPanning: boolean = false;\n private panStart: Point = { x: 0, y: 0 };\n\n // 绑定的事件处理器(用于移除监听)\n private boundHandleResize: () => void;\n private boundHandleKeyDown: (e: KeyboardEvent) => void;\n private boundHandleWheel: (e: WheelEvent) => void;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n \n // 绑定事件处理器\n this.boundHandleResize = this.handleResize.bind(this);\n this.boundHandleKeyDown = this.handleKeyDown.bind(this);\n this.boundHandleWheel = this.handleWheel.bind(this);\n }\n\n // 观察的属性\n static get observedAttributes(): string[] {\n return [\n 'title', 'show-pencil', 'show-rectangle', 'show-circle', 'show-text',\n 'show-image', 'show-zoom', 'show-download', 'show-export', 'show-import',\n 'show-color', 'show-minimap', 'initial-data'\n ];\n }\n\n // 生命周期:连接到 DOM\n connectedCallback(): void {\n this.parseAttributes();\n this.render();\n this.setupEventListeners();\n this.initCanvas(true); // 首次初始化需要加载初始数据\n }\n\n // 生命周期:从 DOM 断开\n disconnectedCallback(): void {\n this.removeEventListeners();\n }\n\n // 生命周期:属性变化\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n\n // 处理 initial-data 属性变化\n if (name === 'initial-data' && newValue && this.canvas) {\n this.loadInitialData();\n // 动态更新时需要手动触发渲染(画布已初始化完成)\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n this.parseAttributes();\n if (this.container) {\n this.updateUI();\n }\n }\n\n // 解析 HTML 属性\n private parseAttributes(): void {\n this.config = {\n title: this.getAttribute('title') || defaultConfig.title,\n showPencil: this.getAttribute('show-pencil') !== 'false',\n showRectangle: this.getAttribute('show-rectangle') !== 'false',\n showCircle: this.getAttribute('show-circle') !== 'false',\n showText: this.getAttribute('show-text') !== 'false',\n showImage: this.getAttribute('show-image') !== 'false',\n showZoom: this.getAttribute('show-zoom') !== 'false',\n showDownload: this.getAttribute('show-download') !== 'false',\n showExport: this.getAttribute('show-export') !== 'false',\n showImport: this.getAttribute('show-import') !== 'false',\n showColor: this.getAttribute('show-color') !== 'false',\n showMinimap: this.getAttribute('show-minimap') !== 'false',\n };\n }\n\n // 生成唯一 ID\n private generateId(): string {\n return Math.random().toString(36).substr(2, 9);\n }\n\n // 加载初始数据\n private loadInitialData(): void {\n const initialData = this.getAttribute('initial-data');\n if (!initialData) return;\n\n try {\n const data = JSON.parse(initialData);\n if (data.objects && Array.isArray(data.objects)) {\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片(异步加载完成后需要重新渲染)\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n // 更新UI状态(隐藏空画布提示等)\n this.updateUI();\n }\n } catch (err) {\n console.error('Failed to parse initial-data:', err);\n }\n }\n\n // 设置事件监听\n private setupEventListeners(): void {\n window.addEventListener('resize', this.boundHandleResize);\n window.addEventListener('keydown', this.boundHandleKeyDown);\n }\n\n // 移除事件监听\n private removeEventListeners(): void {\n window.removeEventListener('resize', this.boundHandleResize);\n window.removeEventListener('keydown', this.boundHandleKeyDown);\n if (this.canvas) {\n this.canvas.removeEventListener('wheel', this.boundHandleWheel);\n }\n }\n\n // 窗口大小变化处理\n private handleResize(): void {\n this.initCanvas(false);\n }\n\n // 初始化画布\n private initCanvas(loadInitial: boolean = false): void {\n if (!this.canvasContainer || !this.canvas) return;\n\n // 使用 requestAnimationFrame 确保 DOM 已经渲染\n requestAnimationFrame(() => {\n this.canvas.width = this.canvasContainer.clientWidth;\n this.canvas.height = this.canvasContainer.clientHeight;\n\n // 首次初始化时加载初始数据(确保画布尺寸已设置)\n if (loadInitial) {\n this.loadInitialData();\n }\n\n this.renderCanvas();\n this.renderMinimap();\n });\n }\n\n // 获取鼠标在画布上的位置(考虑缩放和平移)\n private getMousePos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n const x = (clientX - rect.left - this.panOffset.x) / this.scale;\n const y = (clientY - rect.top - this.panOffset.y) / this.scale;\n return { x, y };\n }\n\n // 获取屏幕坐标(不考虑缩放和平移)\n private getScreenPos(e: MouseEvent | TouchEvent): Point {\n const rect = this.canvas.getBoundingClientRect();\n let clientX: number, clientY: number;\n\n if ('touches' in e && e.touches.length > 0) {\n clientX = e.touches[0].clientX;\n clientY = e.touches[0].clientY;\n } else if ('clientX' in e) {\n clientX = e.clientX;\n clientY = e.clientY;\n } else {\n return { x: 0, y: 0 };\n }\n\n return { x: clientX - rect.left, y: clientY - rect.top };\n }\n\n // 获取对象边界\n private getObjectBounds(obj: CanvasObject): { x: number; y: number; width: number; height: number } {\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE': {\n const r = obj as RectObject | ImageObject;\n return { x: r.x, y: r.y, width: r.width, height: r.height };\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n return { x: c.x - c.radius, y: c.y - c.radius, width: c.radius * 2, height: c.radius * 2 };\n }\n case 'TEXT': {\n const t = obj as TextObject;\n const width = t.text.length * t.fontSize * 0.6;\n return { x: t.x, y: t.y - t.fontSize, width, height: t.fontSize };\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return { x: 0, y: 0, width: 0, height: 0 };\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n }\n return { x: 0, y: 0, width: 0, height: 0 };\n }\n\n // 检查调整大小手柄\n private getResizeHandleAtPoint(obj: CanvasObject, x: number, y: number): string | null {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n const handles = [\n { name: 'nw', x: bounds.x, y: bounds.y },\n { name: 'ne', x: bounds.x + bounds.width, y: bounds.y },\n { name: 'sw', x: bounds.x, y: bounds.y + bounds.height },\n { name: 'se', x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n for (const handle of handles) {\n if (Math.abs(x - handle.x) <= handleSize && Math.abs(y - handle.y) <= handleSize) {\n return handle.name;\n }\n }\n return null;\n }\n\n // 碰撞检测\n private isHit(obj: CanvasObject, x: number, y: number): boolean {\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n return x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n const dist = Math.sqrt(Math.pow(x - c.x, 2) + Math.pow(y - c.y, 2));\n return dist <= c.radius;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n return x >= img.x && x <= img.x + img.width && y >= img.y && y <= img.y + img.height;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n return x >= t.x && x <= t.x + (t.text.length * t.fontSize * 0.6) && y >= t.y - t.fontSize && y <= t.y;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length === 0) return false;\n const minX = Math.min(...p.points.map(pt => pt.x));\n const maxX = Math.max(...p.points.map(pt => pt.x));\n const minY = Math.min(...p.points.map(pt => pt.y));\n const maxY = Math.max(...p.points.map(pt => pt.y));\n return x >= minX && x <= maxX && y >= minY && y <= maxY;\n }\n }\n return false;\n }\n\n // 保存历史\n private saveHistory(): void {\n this.history.push(JSON.parse(JSON.stringify(this.objects)));\n }\n\n // 撤销\n private undo(): void {\n if (this.history.length === 0) return;\n const previousState = this.history.pop();\n if (previousState) {\n this.objects = previousState;\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.dispatchChangeEvent();\n }\n }\n\n // 删除选中对象\n private deleteSelected(): void {\n if (this.selectedId) {\n this.saveHistory();\n this.objects = this.objects.filter(o => o.id !== this.selectedId);\n this.selectedId = null;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 复制选中对象\n private copySelected(): void {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.clipboard = JSON.parse(JSON.stringify(selectedObj));\n }\n }\n }\n\n // 粘贴对象\n private pasteObject(): void {\n if (this.clipboard) {\n this.saveHistory();\n const newObj = {\n ...JSON.parse(JSON.stringify(this.clipboard)),\n id: this.generateId(),\n x: this.clipboard.x + 20,\n y: this.clipboard.y + 20\n };\n if (newObj.type === 'PATH' && newObj.points) {\n newObj.points = newObj.points.map((pt: Point) => ({\n x: pt.x + 20,\n y: pt.y + 20\n }));\n }\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.clipboard = newObj;\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n }\n\n // 派发变化事件\n private dispatchChangeEvent(): void {\n this.dispatchEvent(new CustomEvent('editor-change', {\n bubbles: true,\n composed: true,\n detail: { objects: this.objects }\n }));\n }\n\n // 键盘事件处理\n private handleKeyDown(e: KeyboardEvent): void {\n if (this.isTextInputVisible) return;\n\n // Ctrl+Z: 撤销\n if ((e.ctrlKey || e.metaKey) && e.key === 'z') {\n e.preventDefault();\n this.undo();\n return;\n }\n\n // Ctrl+C: 复制\n if ((e.ctrlKey || e.metaKey) && e.key === 'c') {\n if (this.selectedId) {\n e.preventDefault();\n this.copySelected();\n }\n return;\n }\n\n // Ctrl+V: 粘贴\n if ((e.ctrlKey || e.metaKey) && e.key === 'v') {\n if (this.clipboard) {\n e.preventDefault();\n this.pasteObject();\n }\n return;\n }\n\n // Delete/Backspace: 删除\n if ((e.key === 'Delete' || e.key === 'Backspace') && this.selectedId) {\n e.preventDefault();\n this.deleteSelected();\n return;\n }\n\n // 快捷键切换工具\n if (!e.ctrlKey && !e.metaKey) {\n switch (e.key.toLowerCase()) {\n case 'v':\n this.setTool('SELECT');\n break;\n case 'p':\n case 'b':\n this.setTool('PENCIL');\n break;\n case 'r':\n this.setTool('RECTANGLE');\n break;\n case 'o':\n this.setTool('CIRCLE');\n break;\n case 't':\n this.setTool('TEXT');\n break;\n case 'escape':\n this.selectedId = null;\n this.hideTextInput();\n this.renderCanvas();\n this.updateUI();\n break;\n }\n }\n }\n\n // 滚轮缩放\n private handleWheel(e: WheelEvent): void {\n e.preventDefault();\n const rect = this.canvas.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n const delta = e.deltaY > 0 ? 0.9 : 1.1;\n const newScale = this.scale * delta;\n\n this.zoomAtPoint(newScale, mouseX, mouseY);\n }\n\n // 以指定点为中心缩放\n private zoomAtPoint(newScale: number, centerX: number, centerY: number): void {\n const clampedScale = Math.min(Math.max(newScale, 0.2), 5);\n\n const mouseXBeforeZoom = (centerX - this.panOffset.x) / this.scale;\n const mouseYBeforeZoom = (centerY - this.panOffset.y) / this.scale;\n\n const newPanOffsetX = centerX - mouseXBeforeZoom * clampedScale;\n const newPanOffsetY = centerY - mouseYBeforeZoom * clampedScale;\n\n this.scale = clampedScale;\n this.panOffset = { x: newPanOffsetX, y: newPanOffsetY };\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 放大\n private zoomIn(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale * 1.2, centerX, centerY);\n }\n\n // 缩小\n private zoomOut(): void {\n const centerX = this.canvas.width / 2;\n const centerY = this.canvas.height / 2;\n this.zoomAtPoint(this.scale / 1.2, centerX, centerY);\n }\n\n // 重置缩放\n private resetZoom(): void {\n this.scale = 1;\n this.panOffset = { x: 0, y: 0 };\n this.renderCanvas();\n this.renderMinimap();\n this.updateZoomDisplay();\n }\n\n // 更新缩放显示\n private updateZoomDisplay(): void {\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomText) {\n zoomText.textContent = `${Math.round(this.scale * 100)}%`;\n }\n }\n\n // 设置工具\n private setTool(tool: ToolType): void {\n this.tool = tool;\n this.updateToolButtons();\n }\n\n // 更新工具按钮状态\n private updateToolButtons(): void {\n const buttons = this.shadow.querySelectorAll('.tool-btn');\n buttons.forEach(btn => {\n const btnTool = btn.getAttribute('data-tool');\n if (btnTool === this.tool) {\n btn.classList.add('active');\n } else {\n btn.classList.remove('active');\n }\n });\n }\n\n // 隐藏文本输入\n private hideTextInput(): void {\n this.isTextInputVisible = false;\n if (this.textInputContainer) {\n this.textInputContainer.style.display = 'none';\n }\n this.editingTextId = null;\n }\n\n // 显示文本输入\n private showTextInput(screenX: number, screenY: number, text: string = ''): void {\n this.isTextInputVisible = true;\n this.textInputScreenPos = { x: screenX, y: screenY };\n\n if (this.textInputContainer && this.textInput) {\n this.textInputContainer.style.display = 'block';\n this.textInputContainer.style.left = `${screenX}px`;\n this.textInputContainer.style.top = `${screenY - 30}px`;\n this.textInput.value = text;\n this.textInput.style.color = this.color;\n setTimeout(() => {\n this.textInput.focus();\n if (text) this.textInput.select();\n }, 0);\n }\n }\n\n // 提交文本\n private submitText(): void {\n const value = this.textInput?.value?.trim();\n if (value) {\n if (this.editingTextId) {\n const existingObj = this.objects.find(o => o.id === this.editingTextId) as TextObject | undefined;\n if (existingObj && existingObj.text !== value) {\n this.saveHistory();\n existingObj.text = value;\n }\n this.selectedId = this.editingTextId;\n } else {\n this.saveHistory();\n const newObj: TextObject = {\n id: this.generateId(),\n type: 'TEXT',\n x: this.textInputPos.x,\n y: this.textInputPos.y,\n text: value,\n fontSize: 24,\n color: this.color,\n lineWidth: this.lineWidth\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n }\n this.dispatchChangeEvent();\n }\n this.hideTextInput();\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 画布鼠标按下\n private handleCanvasPointerDown(e: MouseEvent | TouchEvent): void {\n const { x, y } = this.getMousePos(e);\n const screenPos = this.getScreenPos(e);\n this.dragStart = { x, y };\n this.isDragging = true;\n\n // 如果文本输入可见且不是文本工具,先保存文本\n if (this.isTextInputVisible && this.tool !== 'TEXT') {\n this.submitText();\n }\n\n if (this.tool === 'SELECT') {\n // 检查是否点击调整大小手柄\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const handle = this.getResizeHandleAtPoint(selectedObj, x, y);\n if (handle) {\n this.saveHistory();\n this.isResizing = true;\n this.resizeHandle = handle;\n this.resizeStartBounds = this.getObjectBounds(selectedObj);\n this.resizeOriginalObject = JSON.parse(JSON.stringify(selectedObj));\n return;\n }\n }\n }\n\n // 查找点击的对象\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject) {\n this.selectedId = clickedObject.id;\n this.dragOffset = { x: x - clickedObject.x, y: y - clickedObject.y };\n this.saveHistory();\n this.updateUI();\n } else {\n // 开始拖拽画布\n this.selectedId = null;\n this.isPanning = true;\n this.panStart = screenPos;\n this.updateUI();\n }\n } else if (this.tool === 'TEXT') {\n // 显示文本输入\n this.textInputPos = { x, y };\n this.showTextInput(screenPos.x, screenPos.y);\n this.isDragging = false;\n } else {\n // 开始绘制图形\n this.saveHistory();\n const id = this.generateId();\n if (this.tool === 'RECTANGLE') {\n this.currentObject = { id, type: 'RECTANGLE', x, y, width: 0, height: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'CIRCLE') {\n this.currentObject = { id, type: 'CIRCLE', x, y, radius: 0, color: this.color, lineWidth: this.lineWidth };\n } else if (this.tool === 'PENCIL') {\n this.currentObject = { id, type: 'PATH', x, y, points: [{ x, y }], color: this.color, lineWidth: this.lineWidth };\n }\n }\n\n this.renderCanvas();\n }\n\n // 画布鼠标移动\n private handleCanvasPointerMove(e: MouseEvent | TouchEvent): void {\n // 处理画布拖拽\n if (this.isPanning) {\n const screenPos = this.getScreenPos(e);\n const dx = screenPos.x - this.panStart.x;\n const dy = screenPos.y - this.panStart.y;\n this.panOffset = { x: this.panOffset.x + dx, y: this.panOffset.y + dy };\n this.panStart = screenPos;\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n if (!this.isDragging || !this.dragStart) return;\n const { x, y } = this.getMousePos(e);\n\n // 处理调整大小\n if (this.isResizing && this.selectedId && this.resizeHandle && this.resizeStartBounds && this.resizeOriginalObject) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (!obj) return;\n\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n let newX = this.resizeStartBounds.x;\n let newY = this.resizeStartBounds.y;\n let newWidth = this.resizeStartBounds.width;\n let newHeight = this.resizeStartBounds.height;\n\n if (this.resizeHandle.includes('e')) newWidth = this.resizeStartBounds.width + dx;\n if (this.resizeHandle.includes('w')) {\n newX = this.resizeStartBounds.x + dx;\n newWidth = this.resizeStartBounds.width - dx;\n }\n if (this.resizeHandle.includes('s')) newHeight = this.resizeStartBounds.height + dy;\n if (this.resizeHandle.includes('n')) {\n newY = this.resizeStartBounds.y + dy;\n newHeight = this.resizeStartBounds.height - dy;\n }\n\n newWidth = Math.max(10, newWidth);\n newHeight = Math.max(10, newHeight);\n\n // 根据对象类型应用变化\n switch (obj.type) {\n case 'RECTANGLE':\n case 'IMAGE':\n (obj as RectObject | ImageObject).x = newX;\n (obj as RectObject | ImageObject).y = newY;\n (obj as RectObject | ImageObject).width = newWidth;\n (obj as RectObject | ImageObject).height = newHeight;\n break;\n case 'CIRCLE': {\n const radius = Math.max(newWidth, newHeight) / 2;\n (obj as CircleObject).x = newX + radius;\n (obj as CircleObject).y = newY + radius;\n (obj as CircleObject).radius = radius;\n break;\n }\n case 'TEXT': {\n const origT = this.resizeOriginalObject as TextObject;\n const scaleFactor = newWidth / this.resizeStartBounds.width;\n (obj as TextObject).x = newX;\n (obj as TextObject).y = newY + newHeight;\n (obj as TextObject).fontSize = Math.max(8, Math.round(origT.fontSize * scaleFactor));\n break;\n }\n case 'PATH': {\n const origP = this.resizeOriginalObject as PathObject;\n const scaleX = newWidth / this.resizeStartBounds.width;\n const scaleY = newHeight / this.resizeStartBounds.height;\n (obj as PathObject).points = origP.points.map(pt => ({\n x: newX + (pt.x - this.resizeStartBounds!.x) * scaleX,\n y: newY + (pt.y - this.resizeStartBounds!.y) * scaleY\n }));\n break;\n }\n }\n\n this.renderCanvas();\n this.renderMinimap();\n return;\n }\n\n // 移动选中对象\n if (this.tool === 'SELECT' && this.selectedId) {\n const obj = this.objects.find(o => o.id === this.selectedId);\n if (obj) {\n if (obj.type === 'PATH') {\n const p = obj as PathObject;\n const dx = x - this.dragStart.x;\n const dy = y - this.dragStart.y;\n p.points = p.points.map(pt => ({ x: pt.x + dx, y: pt.y + dy }));\n this.dragStart = { x, y };\n } else {\n obj.x = x - this.dragOffset.x;\n obj.y = y - this.dragOffset.y;\n }\n this.renderCanvas();\n this.renderMinimap();\n }\n } else if (this.currentObject) {\n // 更新正在绘制的图形\n if (this.currentObject.type === 'RECTANGLE') {\n (this.currentObject as RectObject).width = x - this.currentObject.x;\n (this.currentObject as RectObject).height = y - this.currentObject.y;\n } else if (this.currentObject.type === 'CIRCLE') {\n const radius = Math.sqrt(Math.pow(x - this.currentObject.x, 2) + Math.pow(y - this.currentObject.y, 2));\n (this.currentObject as CircleObject).radius = radius;\n } else if (this.currentObject.type === 'PATH') {\n (this.currentObject as PathObject).points.push({ x, y });\n }\n this.renderCanvas();\n }\n }\n\n // 画布鼠标抬起\n private handleCanvasPointerUp(): void {\n this.isDragging = false;\n this.dragStart = null;\n this.isResizing = false;\n this.resizeHandle = null;\n this.resizeStartBounds = null;\n this.resizeOriginalObject = null;\n this.isPanning = false;\n\n if (this.currentObject) {\n this.objects.push(this.currentObject);\n this.currentObject = null;\n this.dispatchChangeEvent();\n }\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n }\n\n // 双击编辑文本\n private handleCanvasDoubleClick(e: MouseEvent): void {\n e.preventDefault();\n const { x, y } = this.getMousePos(e);\n\n const clickedObject = [...this.objects].reverse().find(obj => this.isHit(obj, x, y));\n\n if (clickedObject && clickedObject.type === 'TEXT') {\n const textObj = clickedObject as TextObject;\n this.editingTextId = textObj.id;\n this.textInputPos = { x: textObj.x, y: textObj.y };\n const screenX = textObj.x * this.scale + this.panOffset.x;\n const screenY = textObj.y * this.scale + this.panOffset.y;\n this.showTextInput(screenX, screenY, textObj.text);\n this.setTool('SELECT');\n }\n }\n\n // 渲染画布\n private renderCanvas(): void {\n if (!this.ctx) return;\n\n // 清空画布\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 绘制背景\n this.ctx.fillStyle = '#ffffff';\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\n // 应用缩放和平移\n this.ctx.save();\n this.ctx.translate(this.panOffset.x, this.panOffset.y);\n this.ctx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(this.ctx, obj, false));\n\n // 绘制正在创建的对象\n if (this.currentObject) {\n this.drawObject(this.ctx, this.currentObject, false);\n }\n\n // 绘制选中对象的调整手柄\n if (this.selectedId && this.tool === 'SELECT') {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n this.drawSelectionHandles(this.ctx, selectedObj);\n }\n }\n\n this.ctx.restore();\n }\n\n // 绘制单个对象\n private drawObject(ctx: CanvasRenderingContext2D, obj: CanvasObject, isMinimap: boolean): void {\n ctx.beginPath();\n ctx.strokeStyle = obj.color;\n ctx.lineWidth = obj.lineWidth;\n ctx.fillStyle = obj.color;\n\n // 选中高亮(仅主画布)\n if (!isMinimap && obj.id === this.selectedId) {\n ctx.shadowColor = 'rgba(0, 100, 255, 0.5)';\n ctx.shadowBlur = 10;\n } else {\n ctx.shadowBlur = 0;\n }\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n ctx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n ctx.beginPath();\n ctx.arc(c.x, c.y, c.radius, 0, 2 * Math.PI);\n ctx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length < 2) break;\n ctx.beginPath();\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(p.points[0].x, p.points[0].y);\n for (let i = 1; i < p.points.length; i++) {\n ctx.lineTo(p.points[i].x, p.points[i].y);\n }\n ctx.stroke();\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n ctx.font = `${t.fontSize}px sans-serif`;\n ctx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const imgObj = obj as ImageObject;\n if (imgObj.imageElement && imgObj.imageElement.complete) {\n ctx.drawImage(imgObj.imageElement, imgObj.x, imgObj.y, imgObj.width, imgObj.height);\n } else if (imgObj.dataUrl) {\n // 加载图片\n const img = new Image();\n img.onload = () => {\n imgObj.imageElement = img;\n this.renderCanvas();\n };\n img.src = imgObj.dataUrl;\n }\n break;\n }\n }\n }\n\n // 绘制选中手柄\n private drawSelectionHandles(ctx: CanvasRenderingContext2D, obj: CanvasObject): void {\n const bounds = this.getObjectBounds(obj);\n const handleSize = 8;\n\n ctx.shadowBlur = 0;\n ctx.fillStyle = '#3b82f6';\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n\n // 绘制角落手柄\n const corners = [\n { x: bounds.x, y: bounds.y },\n { x: bounds.x + bounds.width, y: bounds.y },\n { x: bounds.x, y: bounds.y + bounds.height },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n\n corners.forEach(corner => {\n ctx.beginPath();\n ctx.rect(corner.x - handleSize / 2, corner.y - handleSize / 2, handleSize, handleSize);\n ctx.fill();\n ctx.stroke();\n });\n\n // 绘制选择边框\n ctx.strokeStyle = '#3b82f6';\n ctx.lineWidth = 1;\n ctx.setLineDash([5, 5]);\n ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);\n ctx.setLineDash([]);\n }\n\n // 渲染小地图\n private renderMinimap(): void {\n if (!this.minimapCtx || !this.config.showMinimap) return;\n\n const minimap = this.minimapCanvas;\n const mainCanvas = this.canvas;\n\n this.minimapCtx.clearRect(0, 0, minimap.width, minimap.height);\n\n // 计算缩放比例\n const scaleX = minimap.width / mainCanvas.width;\n const scaleY = minimap.height / mainCanvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = mainCanvas.width * minimapScale;\n const canvasH = mainCanvas.height * minimapScale;\n const offsetX = (minimap.width - canvasW) / 2;\n const offsetY = (minimap.height - canvasH) / 2;\n\n // 绘制画布区域背景\n this.minimapCtx.fillStyle = '#ffffff';\n this.minimapCtx.fillRect(offsetX, offsetY, canvasW, canvasH);\n\n // 只应用小地图的缩放变换,不应用主画布的 panOffset 和 scale\n // 小地图应该始终显示画布内容的全貌\n this.minimapCtx.save();\n this.minimapCtx.translate(offsetX, offsetY);\n this.minimapCtx.scale(minimapScale, minimapScale);\n\n // 绘制所有对象\n const allObjects = this.currentObject ? [...this.objects, this.currentObject] : this.objects;\n allObjects.forEach(obj => {\n this.minimapCtx.fillStyle = obj.color;\n this.minimapCtx.strokeStyle = obj.color;\n this.minimapCtx.lineWidth = Math.max(obj.lineWidth, 1);\n this.minimapCtx.setLineDash([]);\n\n switch (obj.type) {\n case 'RECTANGLE': {\n const r = obj as RectObject;\n this.minimapCtx.strokeRect(r.x, r.y, r.width, r.height);\n break;\n }\n case 'CIRCLE': {\n const c = obj as CircleObject;\n this.minimapCtx.beginPath();\n this.minimapCtx.arc(c.x, c.y, c.radius, 0, Math.PI * 2);\n this.minimapCtx.stroke();\n break;\n }\n case 'PATH': {\n const p = obj as PathObject;\n if (p.points.length > 0) {\n this.minimapCtx.beginPath();\n this.minimapCtx.lineCap = 'round';\n this.minimapCtx.lineJoin = 'round';\n this.minimapCtx.moveTo(p.points[0].x, p.points[0].y);\n p.points.forEach(pt => this.minimapCtx.lineTo(pt.x, pt.y));\n this.minimapCtx.stroke();\n }\n break;\n }\n case 'TEXT': {\n const t = obj as TextObject;\n this.minimapCtx.font = `${t.fontSize}px sans-serif`;\n this.minimapCtx.fillText(t.text, t.x, t.y);\n break;\n }\n case 'IMAGE': {\n const img = obj as ImageObject;\n if (img.imageElement) {\n this.minimapCtx.drawImage(img.imageElement, img.x, img.y, img.width, img.height);\n }\n break;\n }\n }\n });\n\n this.minimapCtx.restore();\n\n // 绘制画布边框\n this.minimapCtx.strokeStyle = '#94a3b8';\n this.minimapCtx.lineWidth = 1;\n this.minimapCtx.strokeRect(offsetX, offsetY, canvasW, canvasH);\n }\n\n // 小地图点击定位\n private handleMinimapClick(e: MouseEvent): void {\n const rect = this.minimapCanvas.getBoundingClientRect();\n const clickX = e.clientX - rect.left;\n const clickY = e.clientY - rect.top;\n\n const scaleX = this.minimapCanvas.width / this.canvas.width;\n const scaleY = this.minimapCanvas.height / this.canvas.height;\n const minimapScale = Math.min(scaleX, scaleY) * 0.92;\n\n const canvasW = this.canvas.width * minimapScale;\n const canvasH = this.canvas.height * minimapScale;\n const offsetX = (this.minimapCanvas.width - canvasW) / 2;\n const offsetY = (this.minimapCanvas.height - canvasH) / 2;\n\n const relX = clickX - offsetX;\n const relY = clickY - offsetY;\n\n const canvasX = (relX / minimapScale - this.panOffset.x) / this.scale;\n const canvasY = (relY / minimapScale - this.panOffset.y) / this.scale;\n\n const viewportCenterX = this.canvas.width / 2;\n const viewportCenterY = this.canvas.height / 2;\n\n this.panOffset = {\n x: viewportCenterX / this.scale - canvasX,\n y: viewportCenterY / this.scale - canvasY\n };\n\n this.renderCanvas();\n this.renderMinimap();\n }\n\n // 图片上传处理\n private handleImageUpload(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n const dataUrl = event.target?.result as string;\n const img = new Image();\n img.onload = () => {\n this.saveHistory();\n const maxSize = 300;\n let width = img.width;\n let height = img.height;\n if (width > maxSize || height > maxSize) {\n const ratio = Math.min(maxSize / width, maxSize / height);\n width *= ratio;\n height *= ratio;\n }\n\n const newObj: ImageObject = {\n id: this.generateId(),\n type: 'IMAGE',\n x: 100,\n y: 100,\n width,\n height,\n color: '#000000',\n lineWidth: 1,\n dataUrl,\n imageElement: img\n };\n this.objects.push(newObj);\n this.selectedId = newObj.id;\n this.setTool('SELECT');\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n };\n img.src = dataUrl;\n };\n\n reader.readAsDataURL(file);\n input.value = '';\n }\n\n // 保存 JSON\n private saveJson(): void {\n const data = {\n version: '1.0',\n objects: this.objects.map(obj => {\n const { imageElement, ...rest } = obj as ImageObject;\n return rest;\n })\n };\n const json = JSON.stringify(data, null, 2);\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-project.json';\n a.click();\n URL.revokeObjectURL(url);\n }\n\n // 加载 JSON\n private loadJson(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (!input.files || input.files.length === 0) return;\n\n const file = input.files[0];\n const reader = new FileReader();\n\n reader.onload = (event) => {\n try {\n const data = JSON.parse(event.target?.result as string);\n if (data.objects && Array.isArray(data.objects)) {\n this.saveHistory();\n this.objects = data.objects;\n this.selectedId = null;\n\n // 重新加载图片\n this.objects.forEach(obj => {\n if (obj.type === 'IMAGE' && (obj as ImageObject).dataUrl) {\n const img = new Image();\n img.onload = () => {\n (obj as ImageObject).imageElement = img;\n this.renderCanvas();\n this.renderMinimap();\n };\n img.src = (obj as ImageObject).dataUrl;\n }\n });\n\n this.renderCanvas();\n this.renderMinimap();\n this.updateUI();\n this.dispatchChangeEvent();\n }\n } catch (err) {\n console.error('Failed to load JSON:', err);\n }\n };\n\n reader.readAsText(file);\n input.value = '';\n }\n\n // 导出 PNG\n private exportPng(): void {\n // 创建临时画布\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = this.canvas.width;\n tempCanvas.height = this.canvas.height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n // 绘制白色背景\n tempCtx.fillStyle = '#ffffff';\n tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);\n\n // 应用缩放和平移\n tempCtx.translate(this.panOffset.x, this.panOffset.y);\n tempCtx.scale(this.scale, this.scale);\n\n // 绘制所有对象\n this.objects.forEach(obj => this.drawObject(tempCtx, obj, true));\n\n // 下载\n const url = tempCanvas.toDataURL('image/png');\n const a = document.createElement('a');\n a.href = url;\n a.download = 'canvas-export.png';\n a.click();\n }\n\n // 更新 UI\n private updateUI(): void {\n // 更新选中状态显示\n const selectionInfo = this.shadow.querySelector('.selection-info');\n if (selectionInfo) {\n if (this.selectedId) {\n const selectedObj = this.objects.find(o => o.id === this.selectedId);\n if (selectedObj) {\n const typeLabels: Record<string, string> = {\n 'RECTANGLE': '矩形',\n 'CIRCLE': '圆形',\n 'PATH': '画笔',\n 'TEXT': '文本',\n 'IMAGE': '图片'\n };\n const typeLabel = typeLabels[selectedObj.type] || selectedObj.type;\n selectionInfo.innerHTML = `\n <span class=\"selection-label\">已选择: ${typeLabel}</span>\n <button class=\"delete-btn\" title=\"删除\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\"/>\n </svg>\n </button>\n `;\n selectionInfo.classList.add('visible');\n const deleteBtn = selectionInfo.querySelector('.delete-btn');\n if (deleteBtn) {\n deleteBtn.addEventListener('click', () => this.deleteSelected());\n }\n }\n } else {\n selectionInfo.classList.remove('visible');\n selectionInfo.innerHTML = '';\n }\n }\n\n // 更新撤销按钮状态\n const undoBtn = this.shadow.querySelector('.undo-btn') as HTMLButtonElement;\n if (undoBtn) {\n undoBtn.disabled = this.history.length === 0;\n }\n\n // 更新空画布提示显示\n const emptyHint = this.shadow.querySelector('.empty-hint') as HTMLElement;\n if (emptyHint) {\n emptyHint.style.display = this.objects.length === 0 ? 'flex' : 'none';\n }\n }\n\n // 渲染 DOM 结构\n private render(): void {\n this.shadow.innerHTML = `\n <style>${this.getStyles()}</style>\n <div class=\"editor-container\">\n <!-- 左侧工具栏 -->\n <div class=\"toolbar\">\n ${this.createToolButton('SELECT', 'select-icon', '选择 (V)')}\n <div class=\"divider\"></div>\n ${this.config.showPencil ? this.createToolButton('PENCIL', 'pencil-icon', '画笔 (P)') : ''}\n ${this.config.showRectangle ? this.createToolButton('RECTANGLE', 'rect-icon', '矩形 (R)') : ''}\n ${this.config.showCircle ? this.createToolButton('CIRCLE', 'circle-icon', '圆形 (O)') : ''}\n ${this.config.showText ? this.createToolButton('TEXT', 'text-icon', '文本 (T)') : ''}\n ${this.config.showImage ? `\n <label class=\"tool-btn\" title=\"插入图片\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/>\n <path d=\"M21 15l-5-5L5 21\"/>\n </svg>\n <input type=\"file\" accept=\"image/*\" class=\"hidden image-input\" />\n </label>\n ` : ''}\n <div class=\"divider\"></div>\n <button class=\"tool-btn undo-btn\" title=\"撤销 (Ctrl+Z)\" disabled>\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M1 4v6h6\"/>\n <path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/>\n </svg>\n </button>\n <div class=\"spacer\"></div>\n ${this.config.showColor ? `\n <input type=\"color\" class=\"color-picker\" value=\"${this.color}\" title=\"颜色\" />\n ` : ''}\n </div>\n\n <!-- 主区域 -->\n <div class=\"main-area\">\n <!-- 顶部栏 -->\n <div class=\"top-bar\">\n <div class=\"top-bar-left\">\n <h2 class=\"title\">${this.config.title}</h2>\n <div class=\"selection-info\"></div>\n </div>\n <div class=\"top-bar-right\">\n ${this.config.showZoom ? `\n <div class=\"zoom-controls\">\n <button class=\"zoom-btn zoom-out-btn\" title=\"缩小\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M8 11h6\"/>\n </svg>\n </button>\n <button class=\"zoom-text\" title=\"重置缩放\">100%</button>\n <button class=\"zoom-btn zoom-in-btn\" title=\"放大\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35M11 8v6M8 11h6\"/>\n </svg>\n </button>\n </div>\n ` : ''}\n ${(this.config.showExport || this.config.showImport || this.config.showDownload) ? `\n <div class=\"file-controls\">\n ${this.config.showExport ? `\n <button class=\"file-btn save-json-btn\" title=\"保存项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z\"/>\n <polyline points=\"17 21 17 13 7 13 7 21\"/><polyline points=\"7 3 7 8 15 8\"/>\n </svg>\n </button>\n ` : ''}\n ${this.config.showImport ? `\n <label class=\"file-btn\" title=\"加载项目 (JSON)\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z\"/>\n </svg>\n <input type=\"file\" accept=\".json\" class=\"hidden load-json-input\" />\n </label>\n ` : ''}\n ${this.config.showDownload ? `\n <button class=\"file-btn export-png-btn\" title=\"导出 PNG\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3\"/>\n </svg>\n </button>\n ` : ''}\n </div>\n ` : ''}\n </div>\n </div>\n\n <!-- 画布容器 -->\n <div class=\"canvas-container\">\n <canvas class=\"main-canvas\"></canvas>\n\n ${this.config.showMinimap ? `\n <div class=\"minimap-wrapper\">\n <div class=\"minimap-header\">\n <svg class=\"minimap-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M3 9h18M9 21V9\"/>\n </svg>\n <span>导航</span>\n </div>\n <canvas class=\"minimap-canvas\" width=\"220\" height=\"140\"></canvas>\n </div>\n ` : ''}\n\n <!-- 文本输入 -->\n <div class=\"text-input-container\" style=\"display: none;\">\n <div class=\"text-input-hint\">按 Enter 确认,Esc 取消</div>\n <input type=\"text\" class=\"text-input\" placeholder=\"输入文本...\" />\n </div>\n\n <!-- 空画布提示 -->\n <div class=\"empty-hint\" style=\"display: ${this.getAttribute('initial-data') ? 'none' : 'flex'};\">\n <h3>开始创作</h3>\n <p>选择左侧的工具开始绘制</p>\n </div>\n </div>\n </div>\n </div>\n `;\n\n // 获取 DOM 引用\n this.container = this.shadow.querySelector('.editor-container')!;\n this.toolbar = this.shadow.querySelector('.toolbar')!;\n this.topBar = this.shadow.querySelector('.top-bar')!;\n this.canvasContainer = this.shadow.querySelector('.canvas-container')!;\n this.canvas = this.shadow.querySelector('.main-canvas')!;\n this.ctx = this.canvas.getContext('2d')!;\n\n if (this.config.showMinimap) {\n this.minimapCanvas = this.shadow.querySelector('.minimap-canvas')!;\n this.minimapCtx = this.minimapCanvas.getContext('2d')!;\n }\n\n this.textInputContainer = this.shadow.querySelector('.text-input-container')!;\n this.textInput = this.shadow.querySelector('.text-input')!;\n\n // 绑定事件\n this.bindEvents();\n }\n\n // 绑定事件\n private bindEvents(): void {\n // 画布事件\n this.canvas.addEventListener('mousedown', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('mousemove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('mouseup', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('mouseleave', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('dblclick', (e) => this.handleCanvasDoubleClick(e));\n this.canvas.addEventListener('touchstart', (e) => this.handleCanvasPointerDown(e));\n this.canvas.addEventListener('touchmove', (e) => this.handleCanvasPointerMove(e));\n this.canvas.addEventListener('touchend', () => this.handleCanvasPointerUp());\n this.canvas.addEventListener('wheel', this.boundHandleWheel, { passive: false });\n\n // 工具按钮\n this.shadow.querySelectorAll('.tool-btn[data-tool]').forEach(btn => {\n btn.addEventListener('mousedown', (e) => {\n // 阻止 blur 事件触发,在点击后手动处理\n e.preventDefault();\n });\n btn.addEventListener('click', () => {\n // 如果有文本输入,先提交\n if (this.isTextInputVisible) {\n this.submitText();\n }\n const tool = btn.getAttribute('data-tool') as ToolType;\n this.setTool(tool);\n });\n });\n\n // 撤销按钮\n const undoBtn = this.shadow.querySelector('.undo-btn');\n if (undoBtn) {\n undoBtn.addEventListener('click', () => this.undo());\n }\n\n // 颜色选择器\n const colorPicker = this.shadow.querySelector('.color-picker') as HTMLInputElement;\n if (colorPicker) {\n colorPicker.addEventListener('input', (e) => {\n this.color = (e.target as HTMLInputElement).value;\n });\n }\n\n // 图片上传\n const imageInput = this.shadow.querySelector('.image-input');\n if (imageInput) {\n imageInput.addEventListener('change', (e) => this.handleImageUpload(e));\n }\n\n // 缩放按钮\n const zoomInBtn = this.shadow.querySelector('.zoom-in-btn');\n const zoomOutBtn = this.shadow.querySelector('.zoom-out-btn');\n const zoomText = this.shadow.querySelector('.zoom-text');\n if (zoomInBtn) zoomInBtn.addEventListener('click', () => this.zoomIn());\n if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => this.zoomOut());\n if (zoomText) zoomText.addEventListener('click', () => this.resetZoom());\n\n // 文件操作\n const saveJsonBtn = this.shadow.querySelector('.save-json-btn');\n const loadJsonInput = this.shadow.querySelector('.load-json-input');\n const exportPngBtn = this.shadow.querySelector('.export-png-btn');\n if (saveJsonBtn) saveJsonBtn.addEventListener('click', () => this.saveJson());\n if (loadJsonInput) loadJsonInput.addEventListener('change', (e) => this.loadJson(e));\n if (exportPngBtn) exportPngBtn.addEventListener('click', () => this.exportPng());\n\n // 小地图点击\n if (this.minimapCanvas) {\n this.minimapCanvas.addEventListener('click', (e) => this.handleMinimapClick(e));\n }\n\n // 文本输入\n if (this.textInput) {\n this.textInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.submitText();\n } else if (e.key === 'Escape') {\n this.hideTextInput();\n }\n });\n this.textInput.addEventListener('blur', () => {\n if (this.isTextInputVisible) {\n this.submitText();\n }\n });\n }\n }\n\n // 创建工具按钮 HTML\n private createToolButton(tool: ToolType, iconClass: string, title: string): string {\n const icons: Record<string, string> = {\n 'select-icon': '<path d=\"M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z\"/><path d=\"M13 13l6 6\"/>',\n 'pencil-icon': '<path d=\"M17 3a2.85 2.85 0 114 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/>',\n 'rect-icon': '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>',\n 'circle-icon': '<circle cx=\"12\" cy=\"12\" r=\"10\"/>',\n 'text-icon': '<path d=\"M4 7V4h16v3M9 20h6M12 4v16\"/>',\n };\n const isActive = this.tool === tool;\n return `\n <button class=\"tool-btn ${isActive ? 'active' : ''}\" data-tool=\"${tool}\" title=\"${title}\">\n <svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n ${icons[iconClass]}\n </svg>\n </button>\n `;\n }\n\n // 获取样式\n private getStyles(): string {\n return `\n :host {\n display: block;\n width: 100%;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n .hidden {\n display: none !important;\n }\n\n .editor-container {\n display: flex;\n width: 100%;\n height: 100%;\n background: #f1f5f9;\n }\n\n /* 工具栏 */\n .toolbar {\n width: 64px;\n background: #ffffff;\n border-right: 1px solid #e2e8f0;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 8px;\n gap: 4px;\n }\n\n .tool-btn {\n width: 44px;\n height: 44px;\n border: none;\n background: transparent;\n border-radius: 12px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #64748b;\n transition: all 0.2s;\n }\n\n .tool-btn:hover {\n background: #f1f5f9;\n color: #4f46e5;\n }\n\n .tool-btn.active {\n background: #4f46e5;\n color: #ffffff;\n box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);\n transform: scale(1.05);\n }\n\n .tool-btn:disabled {\n color: #cbd5e1;\n cursor: not-allowed;\n }\n\n .tool-btn:disabled:hover {\n background: transparent;\n color: #cbd5e1;\n }\n\n .icon {\n width: 20px;\n height: 20px;\n }\n\n .divider {\n width: 32px;\n height: 1px;\n background: #e2e8f0;\n margin: 8px 0;\n }\n\n .spacer {\n flex: 1;\n }\n\n .color-picker {\n width: 32px;\n height: 32px;\n border: 2px solid #e2e8f0;\n border-radius: 50%;\n cursor: pointer;\n padding: 0;\n overflow: hidden;\n -webkit-appearance: none;\n }\n\n .color-picker::-webkit-color-swatch-wrapper {\n padding: 0;\n }\n\n .color-picker::-webkit-color-swatch {\n border: none;\n border-radius: 50%;\n }\n\n /* 主区域 */\n .main-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* 顶部栏 */\n .top-bar {\n height: 56px;\n background: #ffffff;\n border-bottom: 1px solid #e2e8f0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 16px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n }\n\n .top-bar-left, .top-bar-right {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .title {\n font-size: 16px;\n font-weight: 600;\n color: #334155;\n }\n\n .selection-info {\n display: none;\n align-items: center;\n gap: 8px;\n background: #eef2ff;\n padding: 4px 12px;\n border-radius: 20px;\n border: 1px solid #c7d2fe;\n }\n\n .selection-info.visible {\n display: flex;\n }\n\n .selection-label {\n font-size: 12px;\n font-weight: 600;\n color: #4f46e5;\n text-transform: uppercase;\n }\n\n .delete-btn {\n background: none;\n border: none;\n color: #ef4444;\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .delete-btn:hover {\n color: #dc2626;\n }\n\n .zoom-controls, .file-controls {\n display: flex;\n align-items: center;\n background: #f1f5f9;\n border-radius: 8px;\n padding: 4px;\n }\n\n .zoom-btn, .file-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #475569;\n transition: all 0.2s;\n }\n\n .zoom-btn:hover, .file-btn:hover {\n color: #4f46e5;\n }\n\n .zoom-text {\n padding: 4px 8px;\n font-size: 12px;\n font-weight: 500;\n color: #475569;\n background: transparent;\n border: none;\n cursor: pointer;\n min-width: 50px;\n text-align: center;\n }\n\n .zoom-text:hover {\n color: #4f46e5;\n }\n\n /* 画布容器 */\n .canvas-container {\n flex: 1;\n position: relative;\n background: #f1f5f9;\n overflow: hidden;\n }\n\n .main-canvas {\n position: absolute;\n inset: 0;\n display: block;\n cursor: crosshair;\n touch-action: none;\n }\n\n /* 小地图 */\n .minimap-wrapper {\n position: absolute;\n top: 16px;\n right: 16px;\n z-index: 30;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n padding: 2px;\n }\n\n .minimap-wrapper > * {\n background: #ffffff;\n }\n\n .minimap-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: linear-gradient(to right, #f8fafc, #f1f5f9);\n border-bottom: 1px solid #e2e8f0;\n font-size: 12px;\n font-weight: 600;\n color: #475569;\n border-radius: 10px 10px 0 0;\n }\n\n .minimap-icon {\n width: 12px;\n height: 12px;\n color: #4f46e5;\n }\n\n .minimap-canvas {\n cursor: pointer;\n background: #f8fafc;\n border-radius: 0 0 10px 10px;\n display: block;\n }\n\n .minimap-canvas:hover {\n filter: brightness(1.05);\n }\n\n /* 文本输入 */\n .text-input-container {\n position: absolute;\n z-index: 20;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n }\n\n .text-input-hint {\n background: rgba(0, 0, 0, 0.75);\n color: #fff;\n font-size: 12px;\n padding: 4px 8px;\n border-radius: 4px;\n margin-bottom: 4px;\n white-space: nowrap;\n }\n\n .text-input {\n padding: 8px 12px;\n border: 2px solid #4f46e5;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n outline: none;\n min-width: 200px;\n font-size: 16px;\n }\n\n /* 空画布提示 */\n .empty-hint {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n opacity: 0.4;\n }\n\n .empty-hint h3 {\n font-size: 24px;\n font-weight: 700;\n color: #94a3b8;\n margin-bottom: 8px;\n }\n\n .empty-hint p {\n color: #94a3b8;\n }\n `;\n }\n}\n\n// 注册 Web Component\nif (typeof window !== 'undefined' && !customElements.get('canvas-drawing-editor')) {\n customElements.define('canvas-drawing-editor', CanvasDrawingEditor);\n}\n"],"names":["defaultConfig","CanvasDrawingEditor","name","oldValue","newValue","initialData","data","obj","img","err","loadInitial","e","rect","clientX","clientY","x","y","r","c","t","width","p","minX","pt","maxX","minY","maxY","bounds","handleSize","handles","handle","previousState","o","selectedObj","newObj","mouseX","mouseY","delta","newScale","centerX","centerY","clampedScale","mouseXBeforeZoom","mouseYBeforeZoom","newPanOffsetX","newPanOffsetY","zoomText","tool","btn","screenX","screenY","text","value","_b","_a","existingObj","screenPos","clickedObject","id","dx","dy","newX","newY","newWidth","newHeight","radius","origT","scaleFactor","origP","scaleX","scaleY","textObj","ctx","isMinimap","i","imgObj","corner","minimap","mainCanvas","minimapScale","canvasW","canvasH","offsetX","offsetY","clickX","clickY","relX","relY","canvasX","canvasY","viewportCenterX","viewportCenterY","input","file","reader","event","dataUrl","maxSize","height","ratio","imageElement","rest","json","blob","url","a","tempCanvas","tempCtx","selectionInfo","typeLabel","deleteBtn","undoBtn","emptyHint","colorPicker","imageInput","zoomInBtn","zoomOutBtn","saveJsonBtn","loadJsonInput","exportPngBtn","iconClass","title","icons"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canvas-drawing-editor",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"description": "A powerful canvas-based drawing editor Web Component - Zero dependencies, works with Vue 2/3, React, Angular, and vanilla HTML",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/canvas-drawing-editor.umd.js",
|