@zzalai/leafer-point-annotation 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/README_EN.md +308 -0
- package/docs/assets/index-DGiYiG5f.css +1 -0
- package/docs/assets/index-L8gL3x2V.js +1 -0
- package/docs/index.html +14 -0
- package/index.html +13 -0
- package/package.json +64 -0
- package/project-docs/ARCHITECTURE.md +401 -0
- package/project-docs/IMPLEMENTATION_PLAN.md +196 -0
- package/project-docs/REQUIREMENTS.md +517 -0
- package/project-docs/TODO.md +167 -0
- package/project-docs/leafer-development-guide/LEAFER_DEVELOPMENT_GUIDE.md +835 -0
- package/project-docs/leafer-development-guide/LEAFER_UNDO_REDO_GUIDE.md +329 -0
- package/project-docs/leafer-development-guide/TINYKEYS_GUIDE.md +407 -0
- package/src/App.vue +464 -0
- package/src/components/BrushSizeSlider.vue +190 -0
- package/src/components/BrushStylePanel.vue +295 -0
- package/src/components/PointAnnotation.vue +1663 -0
- package/src/elements/PointAnnotationElement.ts +155 -0
- package/src/index.ts +4 -0
- package/src/main.ts +4 -0
- package/src/types/index.ts +122 -0
- package/src/utils/BrushCommands.ts +47 -0
- package/src/utils/BrushStroke.ts +96 -0
- package/src/utils/COCOExporter.ts +90 -0
- package/src/utils/CanvasBrush.ts +179 -0
- package/src/utils/PointCommands.ts +74 -0
- package/src/utils/YOLOExporter.ts +39 -0
- package/src/vite-env.d.ts +7 -0
- package/tsconfig.json +24 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +42 -0
- package/vite.docs.config.ts +28 -0
package/docs/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Leafer Point Annotation</title>
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-L8gL3x2V.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DGiYiG5f.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Leafer Point Annotation</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zzalai/leafer-point-annotation",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Vue3 component for point annotation and brush painting on images using LeaferJS, supporting COCO/YOLO/JSON export, designed for AI model training dataset annotation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/leafer-point-annotation.umd.js",
|
|
10
|
+
"module": "./dist/leafer-point-annotation.es.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/leafer-point-annotation.es.js",
|
|
15
|
+
"require": "./dist/leafer-point-annotation.umd.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "vite build",
|
|
22
|
+
"preview": "vite preview",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"docs:build": "vite build --config vite.docs.config.ts"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"vue3",
|
|
28
|
+
"leaferjs",
|
|
29
|
+
"point",
|
|
30
|
+
"annotation",
|
|
31
|
+
"image-processing"
|
|
32
|
+
],
|
|
33
|
+
"author": "zzalai",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/otaku1951/leafer-point-annotation.git"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://otaku1951.github.io/leafer-point-annotation/",
|
|
40
|
+
"packageManager": "pnpm@10.33.0",
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"vue": "^3.3.0"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@leafer-in/editor": "^2.0.8",
|
|
46
|
+
"@leafer-in/resize": "^2.0.8",
|
|
47
|
+
"@leafer-in/state": "^2.1.0",
|
|
48
|
+
"@leafer-in/text-editor": "^2.1.0",
|
|
49
|
+
"@leafer-in/view": "^2.0.8",
|
|
50
|
+
"@leafer-in/viewport": "^2.0.8",
|
|
51
|
+
"@zzalai/leafer-undo-redo": "1.0.3",
|
|
52
|
+
"leafer-ui": "^2.0.8",
|
|
53
|
+
"tinykeys": "^3.0.0",
|
|
54
|
+
"vue-pick-colors": "^1.8.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^25.6.0",
|
|
58
|
+
"@vitejs/plugin-vue": "^6.0.6",
|
|
59
|
+
"terser": "^5.46.1",
|
|
60
|
+
"typescript": "^6.0.2",
|
|
61
|
+
"vite": "^8.0.8",
|
|
62
|
+
"vite-plugin-dts": "^3.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# 点标注与笔刷涂抹工具 - 功能架构文档
|
|
2
|
+
|
|
3
|
+
## 1. 架构概述
|
|
4
|
+
|
|
5
|
+
### 1.1 架构设计原则
|
|
6
|
+
|
|
7
|
+
| 原则 | 说明 |
|
|
8
|
+
|------|------|
|
|
9
|
+
| **单一职责** | 每个组件/模块只负责一个功能 |
|
|
10
|
+
| **分层架构** | 清晰的层次划分:UI层、业务层、数据层 |
|
|
11
|
+
| **可扩展性** | 预留扩展接口,支持后续功能迭代 |
|
|
12
|
+
| **响应式设计** | 使用 Vue3 Composition API |
|
|
13
|
+
| **性能优先** | 优化 Canvas 渲染和事件处理 |
|
|
14
|
+
|
|
15
|
+
### 1.2 整体架构图
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
19
|
+
│ UI 层 │
|
|
20
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
21
|
+
│ │ BrushSize │ │BrushStyle │ │ PointAnnotation │ │
|
|
22
|
+
│ │ Slider.vue │ │ Panel.vue │ │ (主画布组件) │ │
|
|
23
|
+
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
|
|
24
|
+
│ │ │ │ │
|
|
25
|
+
└─────────┼─────────────────┼────────────────────┼──────────┘
|
|
26
|
+
│ │ │
|
|
27
|
+
▼ ▼ ▼
|
|
28
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
29
|
+
│ 业务逻辑层 │
|
|
30
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
31
|
+
│ │ PointAnnotation.vue (主组件) │ │
|
|
32
|
+
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │ │
|
|
33
|
+
│ │ │ 工具切换逻辑 │ │ 事件处理逻辑 │ │ 命令管理 │ │ │
|
|
34
|
+
│ │ └──────────────┘ └──────────────┘ └───────────┘ │ │
|
|
35
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
36
|
+
└───────────────────────────┬────────────────────────────────┘
|
|
37
|
+
│
|
|
38
|
+
┌─────────────────┼─────────────────┐
|
|
39
|
+
▼ ▼ ▼
|
|
40
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
41
|
+
│ 元素封装层 │ │ 工具类层 │ │ 类型定义层 │
|
|
42
|
+
│ PointAnnotation │ │CanvasBrush.ts │ │ types/index │
|
|
43
|
+
│ Element.ts │ │PointCommands.ts │ │ .ts │
|
|
44
|
+
│ │ │BrushCommands.ts │ │ │
|
|
45
|
+
│ │ │COCOExporter.ts │ │ │
|
|
46
|
+
│ │ │YOLOExporter.ts │ │ │
|
|
47
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. 模块划分
|
|
53
|
+
|
|
54
|
+
### 2.1 UI 层
|
|
55
|
+
|
|
56
|
+
| 模块 | 文件路径 | 职责 |
|
|
57
|
+
|------|----------|------|
|
|
58
|
+
| BrushSizeSlider | src/components/BrushSizeSlider.vue | 笔刷大小调节浮动组件 |
|
|
59
|
+
| BrushStylePanel | src/components/BrushStylePanel.vue | 笔刷样式配置面板(颜色、透明度、大小等) |
|
|
60
|
+
| Canvas | 集成在 PointAnnotation.vue 中 | LeaferJS 画布渲染、事件监听 |
|
|
61
|
+
| App.vue (测试入口) | src/App.vue | 提供完整的测试界面和示例 |
|
|
62
|
+
|
|
63
|
+
### 2.2 业务逻辑层
|
|
64
|
+
|
|
65
|
+
| 模块 | 文件路径 | 职责 |
|
|
66
|
+
|------|----------|------|
|
|
67
|
+
| 工具切换 | PointAnnotation.vue | 管理 currentTool 状态、Editor 动态配置 |
|
|
68
|
+
| 事件处理 | PointAnnotation.vue | 处理鼠标/键盘事件、触发相应操作 |
|
|
69
|
+
| 命令管理 | PointAnnotation.vue | 集成 CommandManager、管理撤销/重做 |
|
|
70
|
+
| 数据管理 | PointAnnotation.vue | 管理点标注和笔刷数据的增删改查 |
|
|
71
|
+
| 导出导入 | PointAnnotation.vue | 处理数据导出和导入逻辑 |
|
|
72
|
+
| 标签编辑 | PointAnnotation.vue | 管理标签编辑状态,根据工具控制可编辑性 |
|
|
73
|
+
|
|
74
|
+
### 2.3 元素封装层
|
|
75
|
+
|
|
76
|
+
| 模块 | 文件路径 | 职责 |
|
|
77
|
+
|------|----------|------|
|
|
78
|
+
| PointAnnotationElement | src/elements/PointAnnotationElement.ts | 封装点标注元素(Group + Ellipse + Text),支持 hover/selected 状态 |
|
|
79
|
+
|
|
80
|
+
### 2.4 工具类层
|
|
81
|
+
|
|
82
|
+
| 模块 | 文件路径 | 职责 |
|
|
83
|
+
|------|----------|------|
|
|
84
|
+
| CanvasBrush | src/utils/CanvasBrush.ts | 使用 LeaferJS Canvas 实现笔刷绘制,支持 draw/erase/clear/hasContent |
|
|
85
|
+
| PointCommands | src/utils/PointCommands.ts | 点标注的 Add/Remove 命令实现 |
|
|
86
|
+
| BrushCommands | src/utils/BrushCommands.ts | 笔刷的快照命令实现(BrushSnapshotCommand) |
|
|
87
|
+
| COCOExporter | src/utils/COCOExporter.ts | COCO 格式数据导出 |
|
|
88
|
+
| YOLOExporter | src/utils/YOLOExporter.ts | YOLO 格式数据导出 |
|
|
89
|
+
|
|
90
|
+
### 2.5 类型定义层
|
|
91
|
+
|
|
92
|
+
| 模块 | 文件路径 | 职责 |
|
|
93
|
+
|------|----------|------|
|
|
94
|
+
| Types | src/types/index.ts | 定义所有 TypeScript 类型接口(PointAnnotation、BrushStyle、ExportOptions 等) |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 3. 核心组件设计
|
|
99
|
+
|
|
100
|
+
### 3.1 PointAnnotation.vue(主组件)
|
|
101
|
+
|
|
102
|
+
**核心职责**:
|
|
103
|
+
- 初始化 LeaferJS 应用和图片加载
|
|
104
|
+
- 管理工具状态和切换逻辑
|
|
105
|
+
- 处理用户交互事件
|
|
106
|
+
- 管理撤销/重做队列
|
|
107
|
+
- 提供对外 API
|
|
108
|
+
- 处理数据导出/导入
|
|
109
|
+
|
|
110
|
+
**状态管理**:
|
|
111
|
+
```typescript
|
|
112
|
+
// 工具状态
|
|
113
|
+
const currentTool = ref<ToolType>('select');
|
|
114
|
+
|
|
115
|
+
// 数据状态
|
|
116
|
+
const pointAnnotations = ref<PointAnnotation[]>([]);
|
|
117
|
+
const pointCounter = ref(1);
|
|
118
|
+
|
|
119
|
+
// 画布状态
|
|
120
|
+
const loadStatus = ref('idle');
|
|
121
|
+
const imageWidth = ref<number | null>(null);
|
|
122
|
+
const imageHeight = ref<number | null>(null);
|
|
123
|
+
|
|
124
|
+
// UI 状态
|
|
125
|
+
const showBrushPanel = ref(false);
|
|
126
|
+
const brushButtonRect = ref({ x: 0, y: 0, width: 0, height: 0 });
|
|
127
|
+
|
|
128
|
+
// 笔刷样式
|
|
129
|
+
const localBrushStyle = ref<BrushStyle>({ ...DEFAULT_BRUSH_STYLE });
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**事件处理**:
|
|
133
|
+
| 事件 | 处理方法 | 触发操作 |
|
|
134
|
+
|------|----------|----------|
|
|
135
|
+
| pointerdown | handlePointerDown | 创建点标注/开始笔刷绘制 |
|
|
136
|
+
| pointermove | handlePointerMove | 笔刷绘制/鼠标追踪 |
|
|
137
|
+
| pointerup | handlePointerUp | 完成笔刷绘制 |
|
|
138
|
+
| keydown | handleKeyDown | 热键处理(tinykeys) |
|
|
139
|
+
|
|
140
|
+
**对外 API**:
|
|
141
|
+
```typescript
|
|
142
|
+
defineExpose({
|
|
143
|
+
getPointAnnotations,
|
|
144
|
+
getImageInfo,
|
|
145
|
+
exportCanvasJSON,
|
|
146
|
+
exportMaskImage,
|
|
147
|
+
exportCOCO,
|
|
148
|
+
exportYOLO,
|
|
149
|
+
importCanvasJSON,
|
|
150
|
+
loadImage,
|
|
151
|
+
clearBrush,
|
|
152
|
+
zoomIn,
|
|
153
|
+
zoomOut,
|
|
154
|
+
resetZoom,
|
|
155
|
+
undo,
|
|
156
|
+
redo,
|
|
157
|
+
getCurrentTool,
|
|
158
|
+
setTool,
|
|
159
|
+
createPointAnnotation,
|
|
160
|
+
removePointAnnotation,
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 3.2 PointAnnotationElement(点标注元素)
|
|
165
|
+
|
|
166
|
+
**结构设计**:
|
|
167
|
+
```
|
|
168
|
+
Group (容器) - id: 点数据的 id, _element_tag: 'point-annotation'
|
|
169
|
+
├── Ellipse (圆点) - 负责视觉样式、hover/selected 效果
|
|
170
|
+
└── Text (标签) - 负责显示标签文本、支持编辑,带 boxStyle 背景
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**核心方法**:
|
|
174
|
+
| 方法 | 功能 |
|
|
175
|
+
|------|------|
|
|
176
|
+
| constructor | 初始化元素、绑定事件、配置 hoverStyle |
|
|
177
|
+
| handlePointAnnotationSelected | 更新选中状态样式(fill/stroke/scale) |
|
|
178
|
+
| handleLabelChange | 处理标签编辑变更(非空校验) |
|
|
179
|
+
| updatePosition | 更新位置坐标 |
|
|
180
|
+
| updateLabel | 更新标签文本 |
|
|
181
|
+
| getLabel / getLastValidLabel | 获取标签值 |
|
|
182
|
+
|
|
183
|
+
### 3.3 CanvasBrush(笔刷绘制)
|
|
184
|
+
|
|
185
|
+
**核心职责**:
|
|
186
|
+
- 使用 LeaferJS Canvas 实现笔刷绘制
|
|
187
|
+
- 外层 Group 控制整体透明度(避免多次叠加)
|
|
188
|
+
- 支持绘制和擦除模式
|
|
189
|
+
- 连续性阈值处理(两个点之间距离过远时自动连线)
|
|
190
|
+
- 图片数据导出/恢复
|
|
191
|
+
- hasContent() 检测是否有内容
|
|
192
|
+
|
|
193
|
+
**核心方法**:
|
|
194
|
+
| 方法 | 功能 |
|
|
195
|
+
|------|------|
|
|
196
|
+
| constructor | 初始化 Canvas 和外层 Group |
|
|
197
|
+
| draw | 绘制笔刷(使用多个圆填充路径) |
|
|
198
|
+
| erase | 擦除操作(destination-out) |
|
|
199
|
+
| clear | 清除所有内容 |
|
|
200
|
+
| getImageData | 导出为 PNG dataURL |
|
|
201
|
+
| restoreImageData | 从 dataURL 恢复画布 |
|
|
202
|
+
| hasContent | 检测是否有非透明像素 |
|
|
203
|
+
| setPointerEvents | 控制 Canvas 是否拦截事件 |
|
|
204
|
+
|
|
205
|
+
### 3.4 笔刷命令设计
|
|
206
|
+
|
|
207
|
+
**BrushSnapshotCommand**:
|
|
208
|
+
- 笔刷操作使用快照方式实现撤销/重做
|
|
209
|
+
- 保存操作前后的完整图片状态
|
|
210
|
+
- undo/redo 时恢复对应状态
|
|
211
|
+
|
|
212
|
+
### 3.5 导出导入实现
|
|
213
|
+
|
|
214
|
+
**导出格式**:
|
|
215
|
+
1. **JSON Full**:完整数据(点标注 + 笔刷 mask)
|
|
216
|
+
2. **JSON Points**:仅点标注数据
|
|
217
|
+
3. **COCO**:COCO 数据集格式
|
|
218
|
+
4. **YOLO**:YOLO 数据集格式
|
|
219
|
+
5. **Mask**:二值图(PNG/JPEG 可选)
|
|
220
|
+
|
|
221
|
+
**二值图特性**:
|
|
222
|
+
- 前景色可配置(黑/白)
|
|
223
|
+
- PNG 支持透明背景
|
|
224
|
+
- JPG 自动处理背景色(前景黑则背景白,反之亦然)
|
|
225
|
+
- 使用 getImageData() 扫描像素,重新绘制为纯二值图
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 4. 数据流转
|
|
230
|
+
|
|
231
|
+
### 4.1 点标注数据流转
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
用户点击 → handlePointerDown → createPointAnnotation →
|
|
235
|
+
PointAnnotationElement → AddPointCommand → CommandManager →
|
|
236
|
+
pointAnnotations (响应式数组) → 导出数据
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 4.2 笔刷数据流转
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
用户绘制 → handlePointerMove → CanvasBrush.draw →
|
|
243
|
+
(操作结束) → BrushSnapshotCommand → CommandManager →
|
|
244
|
+
(内部保存 mask 图片)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 4.3 导出/导入数据流转
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
导出:用户触发 → exportData(format) → Exporter 格式化 → 返回数据字符串/Blob
|
|
251
|
+
导入:用户触发 → importCanvasJSON(json) → 解析数据 → 重建元素 → fitImageToCanvas
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 5. 命令模式实现
|
|
257
|
+
|
|
258
|
+
### 5.1 命令类型
|
|
259
|
+
|
|
260
|
+
| 命令类 | 功能 | 撤销逻辑 |
|
|
261
|
+
|--------|------|----------|
|
|
262
|
+
| AddPointCommand | 添加点标注 | 移除点标注、从数组删除 |
|
|
263
|
+
| RemovePointCommand | 删除点标注 | 重新添加点标注、插入原位置 |
|
|
264
|
+
| BrushSnapshotCommand | 笔刷快照 | 恢复操作前的图片状态 |
|
|
265
|
+
|
|
266
|
+
### 5.2 命令管理器集成
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
// 初始化命令管理器(历史限制 100)
|
|
270
|
+
const commandManager = new CommandManager(100);
|
|
271
|
+
|
|
272
|
+
// 执行命令
|
|
273
|
+
commandManager.executeCommand(new AddPointCommand(pointLayer, element, pointAnnotations.value, data));
|
|
274
|
+
|
|
275
|
+
// 撤销/重做
|
|
276
|
+
commandManager.undo();
|
|
277
|
+
commandManager.redo();
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 6. Editor 动态控制
|
|
283
|
+
|
|
284
|
+
### 6.1 控制策略
|
|
285
|
+
|
|
286
|
+
| 工具 | Editor 状态 | 说明 |
|
|
287
|
+
|------|-------------|------|
|
|
288
|
+
| select | 启用 | 允许选择和拖拽 |
|
|
289
|
+
| point | 启用 | 允许选择和拖拽点标注 |
|
|
290
|
+
| brush | 禁用 | 避免干扰笔刷绘制 |
|
|
291
|
+
| eraser | 禁用 | 避免干扰擦除操作 |
|
|
292
|
+
|
|
293
|
+
### 6.2 标签编辑控制
|
|
294
|
+
|
|
295
|
+
| 工具 | 标签可编辑 | 说明 |
|
|
296
|
+
|------|-----------|------|
|
|
297
|
+
| select | true | 可以编辑标签 |
|
|
298
|
+
| point | true | 可以编辑标签 |
|
|
299
|
+
| brush | false | 禁用编辑,避免冲突 |
|
|
300
|
+
| eraser | false | 禁用编辑,避免冲突 |
|
|
301
|
+
|
|
302
|
+
### 6.3 实现逻辑
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const switchToSelect = () => {
|
|
306
|
+
currentTool.value = 'select';
|
|
307
|
+
showBrushPanel.value = false;
|
|
308
|
+
if (!app) return;
|
|
309
|
+
app.editor.config.moveable = false;
|
|
310
|
+
app.editor.config.resizeable = false;
|
|
311
|
+
app.editor.config.multipleSelect = true;
|
|
312
|
+
canvasBrush?.setPointerEvents(false);
|
|
313
|
+
updateLabelEditable(true);
|
|
314
|
+
};
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 7. 对外 API 设计
|
|
320
|
+
|
|
321
|
+
### 7.1 方法列表
|
|
322
|
+
|
|
323
|
+
| 方法名 | 参数 | 返回值 | 功能描述 |
|
|
324
|
+
|--------|------|--------|----------|
|
|
325
|
+
| getPointAnnotations | 无 | PointAnnotation[] | 获取所有点标注数据 |
|
|
326
|
+
| createPointAnnotation | (x, y) | string \| null | 创建标注点,返回 id |
|
|
327
|
+
| removePointAnnotation | (id) | boolean | 删除指定 id 的点标注 |
|
|
328
|
+
| clearBrush | 无 | void | 清除所有笔刷内容 |
|
|
329
|
+
| exportCanvasJSON | 无 | string | 导出完整 JSON 数据 |
|
|
330
|
+
| exportMaskImage | (format, fgColor) | string \| null | 导出二值图 dataURL |
|
|
331
|
+
| exportCOCO | 无 | COCOExport | 导出 COCO 格式数据 |
|
|
332
|
+
| exportYOLO | 无 | YOLOExport | 导出 YOLO 格式数据 |
|
|
333
|
+
| importCanvasJSON | (json) | void | 导入 JSON 数据 |
|
|
334
|
+
| loadImage | (url) | Promise | 加载图片 |
|
|
335
|
+
| undo | 无 | void | 撤销操作 |
|
|
336
|
+
| redo | 无 | void | 重做操作 |
|
|
337
|
+
| setTool | (tool) | void | 设置当前工具 |
|
|
338
|
+
| getCurrentTool | 无 | ToolType | 获取当前工具 |
|
|
339
|
+
| zoomIn | 无 | void | 放大画布 |
|
|
340
|
+
| zoomOut | 无 | void | 缩小画布 |
|
|
341
|
+
| resetZoom | 无 | void | 重置缩放 |
|
|
342
|
+
|
|
343
|
+
### 7.2 Props 配置
|
|
344
|
+
|
|
345
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
346
|
+
|--------|------|--------|------|
|
|
347
|
+
| imageSource | { id, url } | - | 图片源配置 |
|
|
348
|
+
| pointStyle | Partial\<PointStyle\> | DEFAULT_POINT_STYLE | 点标注样式 |
|
|
349
|
+
| brushStyle | Partial\<BrushStyle\> | DEFAULT_BRUSH_STYLE | 笔刷样式 |
|
|
350
|
+
| options | { maskExportFormat, maskExportForeground } | - | 导出配置 |
|
|
351
|
+
|
|
352
|
+
### 7.3 Events
|
|
353
|
+
|
|
354
|
+
| 事件名 | 参数 | 说明 |
|
|
355
|
+
|--------|------|------|
|
|
356
|
+
| pointChange | PointAnnotation[] | 点标注数据变化 |
|
|
357
|
+
| loadStart | - | 图片开始加载 |
|
|
358
|
+
| loadSuccess | - | 图片加载成功 |
|
|
359
|
+
| loadError | - | 图片加载失败 |
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## 8. 性能优化策略
|
|
364
|
+
|
|
365
|
+
### 8.1 Canvas 渲染优化
|
|
366
|
+
|
|
367
|
+
| 策略 | 说明 |
|
|
368
|
+
|------|------|
|
|
369
|
+
| **Group 透明度控制** | 透明度设置在 Group 上,避免 Canvas 上多次叠加 |
|
|
370
|
+
| **批量更新** | 使用 `set()` 批量更新属性,减少重绘次数 |
|
|
371
|
+
| **图层分离** | 图片层、点标注层、笔刷层分离,独立控制 |
|
|
372
|
+
| **路径填充** | 笔刷使用多个圆填充,避免复杂路径计算 |
|
|
373
|
+
|
|
374
|
+
### 8.2 事件处理优化
|
|
375
|
+
|
|
376
|
+
| 策略 | 说明 |
|
|
377
|
+
|------|------|
|
|
378
|
+
| **事件委托** | LeaferJS 自动处理事件冒泡 |
|
|
379
|
+
| **条件监听** | 笔刷 Canvas 只在需要时拦截事件(pointerEvents) |
|
|
380
|
+
| **updateLabelEditable** | 只在工具切换时更新标签可编辑性 |
|
|
381
|
+
|
|
382
|
+
### 8.3 内存管理
|
|
383
|
+
|
|
384
|
+
| 策略 | 说明 |
|
|
385
|
+
|------|------|
|
|
386
|
+
| **及时清理** | 移除元素时调用 destroy() |
|
|
387
|
+
| **历史限制** | 设置合理的撤销历史记录限制(默认 100) |
|
|
388
|
+
| **Canvas 重用** | CanvasBrush 复用同一 Canvas 对象 |
|
|
389
|
+
|
|
390
|
+
### 8.4 导出优化
|
|
391
|
+
|
|
392
|
+
| 策略 | 说明 |
|
|
393
|
+
|------|------|
|
|
394
|
+
| **异步处理** | 图片导出使用 async/await,避免阻塞 |
|
|
395
|
+
| **DataURL 缓存** | 笔刷快照保存 dataURL,避免重复序列化 |
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
**文档版本**:2.0
|
|
400
|
+
**创建日期**:2026-04-28
|
|
401
|
+
**最后更新**:2026-05-02
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# 点标注与笔刷涂抹工具 - 任务拆解实现先后文档
|
|
2
|
+
|
|
3
|
+
## 1. 任务拆解原则
|
|
4
|
+
|
|
5
|
+
### 1.1 优先级排序
|
|
6
|
+
|
|
7
|
+
| 优先级 | 说明 | 示例 |
|
|
8
|
+
|------|------|------|
|
|
9
|
+
| P0 | 阻塞性任务,必须先完成 | 类型定义、核心工具类 |
|
|
10
|
+
| P1 | 核心功能,影响后续开发 | 点标注创建、笔刷绘制 |
|
|
11
|
+
| P2 | 重要功能,增强用户体验 | 撤销/重做、导出导入 |
|
|
12
|
+
| P3 | 优化和完善 | 性能优化、边界处理 |
|
|
13
|
+
|
|
14
|
+
### 1.2 依赖关系
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
类型定义 → 工具类 → 组件 → 主逻辑 → 导出导入 → 优化
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. 任务拆解与实现顺序
|
|
23
|
+
|
|
24
|
+
### 阶段一:基础准备(P0)✅ 已完成
|
|
25
|
+
|
|
26
|
+
| 序号 | 任务 | 描述 | 依赖 | 状态 |
|
|
27
|
+
|------|------|------|------|------|
|
|
28
|
+
| 1 | 创建类型定义文件 | 定义所有 TypeScript 接口和类型 | 无 | ✅ |
|
|
29
|
+
| 2 | 创建 PointAnnotationElement 类 | 封装点标注元素结构 | 类型定义 | ✅ |
|
|
30
|
+
| 3 | 创建 CanvasBrush 工具类 | 使用 LeaferJS Canvas 实现笔刷绘制 | 类型定义 | ✅ |
|
|
31
|
+
| 4 | 创建 BrushSizeSlider 组件 | 笔刷大小调节浮动组件 | 类型定义 | ✅ |
|
|
32
|
+
| 5 | 创建 BrushStylePanel 组件 | 笔刷样式配置面板(颜色、透明度、大小、连续性) | 类型定义 | ✅ |
|
|
33
|
+
|
|
34
|
+
### 阶段二:核心功能实现(P1)✅ 已完成
|
|
35
|
+
|
|
36
|
+
| 序号 | 任务 | 描述 | 依赖 | 状态 |
|
|
37
|
+
|------|------|------|------|------|
|
|
38
|
+
| 6 | 初始化 LeaferJS 应用 | 在主组件中配置 App 实例、图片加载 | 无 | ✅ |
|
|
39
|
+
| 7 | 实现工具切换逻辑 | 管理 currentTool 状态、动态配置 Editor | 阶段一 | ✅ |
|
|
40
|
+
| 8 | 实现点标注创建功能 | 点击画布创建点标注 | PointAnnotationElement | ✅ |
|
|
41
|
+
| 9 | 实现点标注移动功能 | 拖拽移动点标注 | PointAnnotationElement | ✅ |
|
|
42
|
+
| 10 | 实现点标注删除功能 | 删除选中的点标注 | CommandManager | ✅ |
|
|
43
|
+
| 11 | 实现标签编辑功能 | 双击编辑标签、非空校验 | PointAnnotationElement | ✅ |
|
|
44
|
+
| 12 | 实现笔刷绘制功能 | 实时预览笔刷涂抹 | CanvasBrush | ✅ |
|
|
45
|
+
| 13 | 实现擦除功能 | 切换擦除模式、使用 destination-out | CanvasBrush | ✅ |
|
|
46
|
+
| 14 | 实现清除所有功能 | Select 模式未选中时按 Delete 清除所有(带确认对话框) | 所有功能 | ✅ |
|
|
47
|
+
|
|
48
|
+
### 阶段三:撤销/重做系统(P1)✅ 已完成
|
|
49
|
+
|
|
50
|
+
| 序号 | 任务 | 描述 | 依赖 | 状态 |
|
|
51
|
+
|------|------|------|------|------|
|
|
52
|
+
| 15 | 集成 CommandManager | 初始化命令管理器(历史限制 100) | @zzalai/leafer-undo-redo | ✅ |
|
|
53
|
+
| 16 | 创建 AddPointCommand | 添加点标注命令 | PointAnnotationElement | ✅ |
|
|
54
|
+
| 17 | 创建 RemovePointCommand | 删除点标注命令 | PointAnnotationElement | ✅ |
|
|
55
|
+
| 18 | 创建 BrushSnapshotCommand | 笔刷快照命令(保存操作前后状态) | CanvasBrush | ✅ |
|
|
56
|
+
| 19 | 实现撤销/重做热键 | Ctrl+Z / Ctrl+Y | 所有命令 | ✅ |
|
|
57
|
+
|
|
58
|
+
### 阶段四:工具栏与交互(P2)✅ 已完成
|
|
59
|
+
|
|
60
|
+
| 序号 | 任务 | 描述 | 依赖 | 状态 |
|
|
61
|
+
|------|------|------|------|------|
|
|
62
|
+
| 20 | 实现工具栏 UI | 添加工具按钮、图标、热键提示、激活状态 | 工具切换逻辑 | ✅ |
|
|
63
|
+
| 21 | 实现笔刷大小调节 | 点击笔刷按钮后显示面板、滑块实时更新 | BrushSizeSlider | ✅ |
|
|
64
|
+
| 22 | 实现笔刷样式配置 | 颜色选择器(vue-pick-colors)、透明度滑块、大小滑块、连续性滑块 | BrushStylePanel | ✅ |
|
|
65
|
+
| 23 | 实现画布缩放功能 | 放大/缩小/重置、图片自适应 | LeaferJS viewport | ✅ |
|
|
66
|
+
|
|
67
|
+
### 阶段五:导出/导入功能(P2)✅ 已完成
|
|
68
|
+
|
|
69
|
+
| 序号 | 任务 | 描述 | 依赖 | 状态 |
|
|
70
|
+
|------|------|------|------|------|
|
|
71
|
+
| 24 | 实现 JSON Full 导出 | 导出完整画布数据(点标注 + 笔刷 mask) | 点标注数据、笔刷数据 | ✅ |
|
|
72
|
+
| 25 | 实现二值图导出 | 导出 PNG/JPEG 格式二值图、支持前景色配置 | 笔刷数据 | ✅ |
|
|
73
|
+
| 26 | 实现 COCO 格式导出 | 导出 COCO 标注格式 | 点标注数据 | ✅ |
|
|
74
|
+
| 27 | 实现 YOLO 格式导出 | 导出 YOLO 标注格式 | 点标注数据 | ✅ |
|
|
75
|
+
| 28 | 实现 JSON 导入 | 导入之前导出的数据、自动重置缩放和适应图片 | 所有数据结构 | ✅ |
|
|
76
|
+
|
|
77
|
+
### 阶段六:样式与视觉效果(P2)✅ 已完成
|
|
78
|
+
|
|
79
|
+
| 序号 | 任务 | 描述 | 依赖 | 状态 |
|
|
80
|
+
|------|------|------|------|------|
|
|
81
|
+
| 29 | 实现点标注 Hover 效果 | 鼠标悬停时样式变化(hoverStyle 配置) | PointAnnotationElement | ✅ |
|
|
82
|
+
| 30 | 实现点标注 Selected 效果 | 选中时放大高亮(scale + 颜色变化) | PointAnnotationElement | ✅ |
|
|
83
|
+
| 31 | 实现标签样式 | 带背景色的可编辑文本(boxStyle + shadow) | PointAnnotationElement | ✅ |
|
|
84
|
+
| 32 | 实现工具栏样式 | 美观的按钮样式、激活状态、过渡动画 | 工具栏 UI | ✅ |
|
|
85
|
+
| 33 | 实现画布样式 | 边框、背景、加载/错误状态 UI | LeaferJS 应用 | ✅ |
|
|
86
|
+
|
|
87
|
+
### 阶段七:对外 API(P2)✅ 已完成
|
|
88
|
+
|
|
89
|
+
| 序号 | 任务 | 描述 | 依赖 | 状态 |
|
|
90
|
+
|------|------|------|------|------|
|
|
91
|
+
| 34 | 暴露点标注操作方法 | createPointAnnotation / removePointAnnotation / getPointAnnotations | 点标注逻辑 | ✅ |
|
|
92
|
+
| 35 | 暴露笔刷操作方法 | clearBrush / exportMaskImage | 笔刷逻辑 | ✅ |
|
|
93
|
+
| 36 | 暴露撤销/重做方法 | undo / redo | CommandManager | ✅ |
|
|
94
|
+
| 37 | 暴露工具控制方法 | setTool / getCurrentTool | 工具切换逻辑 | ✅ |
|
|
95
|
+
| 38 | 暴露画布控制方法 | zoomIn / zoomOut / resetZoom | 画布缩放 | ✅ |
|
|
96
|
+
| 39 | 暴露导出导入方法 | exportCanvasJSON / importCanvasJSON / exportCOCO / exportYOLO | 导出导入逻辑 | ✅ |
|
|
97
|
+
|
|
98
|
+
### 阶段八:测试与优化(P3)⏳ 待完成
|
|
99
|
+
|
|
100
|
+
| 序号 | 任务 | 描述 | 依赖 | 优先级 |
|
|
101
|
+
|------|------|------|------|------|
|
|
102
|
+
| 40 | 坐标转换测试 | 像素坐标 ↔ 归一化坐标正确性校验 | 点标注数据 | P1 |
|
|
103
|
+
| 41 | 标签编辑测试 | 空值校验、编辑状态正确性 | 标签编辑逻辑 | P1 |
|
|
104
|
+
| 42 | 撤销/重做完整性测试 | 确保所有操作可正确撤销/重做 | 所有命令 | P1 |
|
|
105
|
+
| 43 | 性能优化 | Canvas 渲染优化、高频事件防抖 | 所有功能 | P2 |
|
|
106
|
+
| 44 | 边界处理 | 极端坐标、空数据、异常输入的容错 | 所有功能 | P2 |
|
|
107
|
+
| 45 | 跨浏览器测试 | 确保在各浏览器正常工作 | 所有功能 | P3 |
|
|
108
|
+
| 46 | 文档完善 | 更新 README、API 文档、添加示例 | 所有功能 | P2 |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 3. 任务依赖关系图
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
阶段一:基础准备
|
|
116
|
+
├── 1. 类型定义
|
|
117
|
+
├── 2. PointAnnotationElement ─────────┐
|
|
118
|
+
├── 3. CanvasBrush ────────────────────┤
|
|
119
|
+
├── 4. BrushSizeSlider ────────────────┤
|
|
120
|
+
└── 5. BrushStylePanel ────────────────┘
|
|
121
|
+
↓
|
|
122
|
+
阶段二:核心功能
|
|
123
|
+
├── 6. LeaferJS 初始化
|
|
124
|
+
├── 7. 工具切换逻辑
|
|
125
|
+
├── 8. 点标注创建 ────────────────────┐
|
|
126
|
+
├── 9. 点标注移动 ────────────────────┤
|
|
127
|
+
├── 10. 标签编辑 ─────────────────────┤
|
|
128
|
+
├── 11. 点标注删除 ───────────────────┤
|
|
129
|
+
├── 12. 笔刷绘制 ─────────────────────┤
|
|
130
|
+
├── 13. 擦除功能 ─────────────────────┤
|
|
131
|
+
└── 14. 清除所有(Select 模式)────────┘
|
|
132
|
+
↓
|
|
133
|
+
阶段三:撤销/重做
|
|
134
|
+
├── 15. CommandManager 集成
|
|
135
|
+
├── 16. AddPointCommand
|
|
136
|
+
├── 17. RemovePointCommand
|
|
137
|
+
├── 18. BrushSnapshotCommand
|
|
138
|
+
└── 19. 撤销/重做热键
|
|
139
|
+
↓
|
|
140
|
+
阶段四:工具栏与交互
|
|
141
|
+
├── 20. 工具栏 UI
|
|
142
|
+
├── 21. 笔刷大小调节
|
|
143
|
+
├── 22. 笔刷样式配置
|
|
144
|
+
└── 23. 画布缩放
|
|
145
|
+
↓
|
|
146
|
+
阶段五:导出/导入
|
|
147
|
+
├── 24. JSON Full 导出
|
|
148
|
+
├── 25. 二值图导出
|
|
149
|
+
├── 26. COCO 格式导出
|
|
150
|
+
├── 27. YOLO 格式导出
|
|
151
|
+
└── 28. JSON 导入
|
|
152
|
+
↓
|
|
153
|
+
阶段六:样式与视觉
|
|
154
|
+
├── 29. Hover 效果
|
|
155
|
+
├── 30. Selected 效果
|
|
156
|
+
├── 31. 标签样式
|
|
157
|
+
├── 32. 工具栏样式
|
|
158
|
+
└── 33. 画布样式
|
|
159
|
+
↓
|
|
160
|
+
阶段七:对外 API
|
|
161
|
+
├── 34. 点标注操作方法
|
|
162
|
+
├── 35. 笔刷操作方法
|
|
163
|
+
├── 36. 撤销/重做方法
|
|
164
|
+
├── 37. 工具控制方法
|
|
165
|
+
├── 38. 画布控制方法
|
|
166
|
+
└── 39. 导出导入方法
|
|
167
|
+
↓
|
|
168
|
+
阶段八:测试与优化
|
|
169
|
+
├── 40. 坐标转换测试
|
|
170
|
+
├── 41. 标签编辑测试
|
|
171
|
+
├── 42. 撤销/重做完整性测试
|
|
172
|
+
├── 43. 性能优化
|
|
173
|
+
├── 44. 边界处理
|
|
174
|
+
├── 45. 跨浏览器测试
|
|
175
|
+
└── 46. 文档完善
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 4. 里程碑规划
|
|
181
|
+
|
|
182
|
+
| 里程碑 | 阶段 | 完成标准 | 状态 |
|
|
183
|
+
|--------|------|------|------|
|
|
184
|
+
| M1 | 基础准备 | 类型定义、核心工具类完成 | ✅ 完成 |
|
|
185
|
+
| M2 | 核心功能 | 点标注和笔刷涂抹基本功能可用 | ✅ 完成 |
|
|
186
|
+
| M3 | 撤销/重做 | 所有操作支持撤销/重做 | ✅ 完成 |
|
|
187
|
+
| M4 | 交互完善 | 工具栏、画布控制完成 | ✅ 完成 |
|
|
188
|
+
| M5 | 数据导出 | 所有导出格式支持完成 | ✅ 完成 |
|
|
189
|
+
| M6 | API 暴露 | 所有对外 API 完成 | ✅ 完成 |
|
|
190
|
+
| M7 | 测试优化 | 测试通过、性能优化完成 | ⏳ 进行中 |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
**文档版本**:2.0
|
|
195
|
+
**创建日期**:2026-04-28
|
|
196
|
+
**最后更新**:2026-05-02
|