@zzalai/leafer-point-annotation 1.1.1 → 1.1.2

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.
@@ -1,536 +1,268 @@
1
1
  # 点标注与笔刷涂抹工具 - 需求文档
2
2
 
3
+ > **版本**: v1.1.x | **用途**: 记录项目所有已实现功能与约束,作为验收与回归基准
4
+
5
+ ---
6
+
3
7
  ## 1. 项目概述
4
8
 
5
9
  ### 1.1 项目背景
6
10
 
7
- 本项目是一个基于 Vue3 和 LeaferJS 的图像标注工具,主要提供两大核心功能:
8
- - **点标注**:在图片上添加可编辑的标注点
9
- - **笔刷涂抹**:在图片上进行涂抹操作并输出二值图
11
+ 本项目是一个基于 Vue 3 和 LeaferJS 的图像标注工具,主要提供两大核心功能:
12
+ - **点标注**:在图片上添加可编辑的标注点(适用于 keypoint 数据集构建、关键点定位场景)
13
+ - **笔刷涂抹**:在图片上涂抹/擦除并输出二值图(适用于 segmentation、掩蔽区域、遮挡区域场景)
10
14
 
11
15
  ### 1.2 项目目标
12
16
 
13
- 为 AI 模型训练提供高质量的标注数据,支持点标注坐标导出和笔刷涂抹区域的二值图输出。
17
+ 1. 为 AI 模型训练提供高质量的标注数据:支持点标注像素/归一化坐标、笔刷涂抹区域的二值图输出
18
+ 2. 作为 Vue 3 组件发布到 npm,可被任意 Vue 3 项目作为依赖安装
19
+ 3. 提供 GitHub Pages 在线演示(`docs/` 目录)
14
20
 
15
21
  ### 1.3 技术栈
16
22
 
17
23
  | 技术 | 版本 | 说明 |
18
24
  |------|------|------|
19
- | Vue | 3.3+ | 前端框架 |
25
+ | Vue | 3.3+ | 前端框架(peer dependency) |
20
26
  | TypeScript | 6.0+ | 类型系统 |
27
+ | Vite | 8.0+ | 构建工具(含 vite-plugin-dts) |
21
28
  | LeaferUI | 2.0.8+ | Canvas 渲染引擎 |
22
- | LeaferEditor | 2.0.8+ | 编辑器插件 |
23
- | LeaferViewport | 2.0.8+ | 视口插件 |
24
- | LeaferTextEditor | 2.1.0+ | 文本编辑插件 |
25
- | LeaferResize | 2.0.8+ | 缩放插件 |
26
- | tinykeys | 3.0+ | 热键系统 |
27
- | @zzalai/leafer-undo-redo | 1.0.3+ | 撤销/重做系统 |
28
- | vue-pick-colors | 1.8+ | 颜色选择器 |
29
+ | @leafer-in/editor | 2.0.8+ | 编辑器插件(选择/框选) |
30
+ | @leafer-in/viewport | 2.0.8+ | 视口插件(缩放/平移) |
31
+ | @leafer-in/state | 2.1.0+ | hover/selected 状态 |
32
+ | @leafer-in/text-editor | 2.1.0+ | label 文本编辑 |
33
+ | @zzalai/leafer-undo-redo | 1.0.3 | 撤销/重做 CommandManager |
34
+ | tinykeys | 3.0+ | 键盘热键系统 |
35
+ | vue-pick-colors | 1.8+ | 颜色选择器组件 |
36
+
37
+ ### 1.4 发布信息
38
+
39
+ | 字段 | 值 |
40
+ |------|---|
41
+ | npm 包名 | `@zzalai/leafer-point-annotation` |
42
+ | 版本 | `1.1.x` |
43
+ | 入口 | ESM: `dist/leafer-point-annotation.es.js` UMD: `dist/leafer-point-annotation.umd.js` |
44
+ | 类型声明 | `dist/index.d.ts` |
45
+ | 样式文件 | `dist/leafer-point-annotation.css`(需手动 import) |
46
+ | 演示站点 | GitHub Pages (`docs/` 目录) |
47
+ | 作者 | `zzalai` |
48
+ | License | `MIT` |
29
49
 
30
50
  ---
31
51
 
32
- ## 2. 功能需求
33
-
34
- ### 2.1 点标注功能
35
-
36
- #### 2.1.1 核心功能
37
-
38
- | 需求编号 | 功能描述 | 优先级 |
39
- |----------|----------|--------|
40
- | P1-001 | 在图片上点击添加标注点 | ⭐⭐⭐ |
41
- | P1-002 | 标注点由圆点(Ellipse)+ 可编辑标签(Text)组成 | ⭐⭐⭐ |
42
- | P1-003 | 标注点支持拖拽移动 | ⭐⭐⭐ |
43
- | P1-004 | 标注点支持删除 | ⭐⭐⭐ |
44
- | P1-005 | 标签默认值为 #1、#2、#3... 自动递增 | ⭐⭐⭐ |
45
- | P1-006 | 标签支持编辑,但不允许重复或为空 | ⭐⭐⭐ |
46
- | P1-007 | 选中状态可通过 app.editor.list 获取 | ⭐⭐⭐ |
47
- | P1-008 | 支持标注点固定大小(不随画布缩放) | ⭐⭐ |
48
-
49
- #### 2.1.2 视觉效果
50
-
51
- | 需求编号 | 功能描述 | 优先级 |
52
- |----------|----------|--------|
53
- | P1-101 | Hover 状态:圆点样式变化 | ⭐⭐⭐ |
54
- | P1-102 | Selected 状态:圆点放大并高亮 | ⭐⭐⭐ |
55
- | P1-103 | 标签带背景色(boxStyle) | ⭐⭐⭐ |
56
-
57
- #### 2.1.3 数据输出
58
-
59
- | 需求编号 | 功能描述 | 优先级 |
60
- |----------|----------|--------|
61
- | P1-201 | 输出归一化坐标(0-1) | ⭐⭐⭐ |
62
- | P1-202 | 输出像素坐标 | ⭐⭐⭐ |
63
- | P1-203 | 输出标签文本 | ⭐⭐⭐ |
64
-
65
- ### 2.2 笔刷涂抹功能
66
-
67
- #### 2.2.1 核心功能
68
-
69
- | 需求编号 | 功能描述 | 优先级 |
70
- |----------|----------|--------|
71
- | P2-001 | 支持笔刷涂抹绘制 | ⭐⭐⭐ |
72
- | P2-002 | 支持擦除功能 | ⭐⭐⭐ |
73
- | P2-003 | 实时预览涂抹效果 | ⭐⭐⭐ |
74
- | P2-004 | 一键清除所有涂抹 | ⭐⭐⭐ |
75
- | P2-005 | 笔刷大小可调节(通过浮动配置面板) | ⭐⭐⭐ |
76
- | P2-006 | 笔刷颜色可配置 | ⭐⭐⭐ |
77
- | P2-007 | 笔刷透明度可配置 | ⭐⭐⭐ |
78
- | P2-008 | 笔刷连续性阈值可配置 | ⭐⭐ |
79
- | P2-009 | 使用 Group 控制整体透明度,避免多次叠加 | ⭐⭐ |
80
- | P2-010 | 提供 hasContent() 方法检测是否有笔刷内容 | ⭐⭐ |
81
-
82
- #### 2.2.2 输出格式
83
-
84
- | 需求编号 | 功能描述 | 优先级 |
85
- |----------|----------|--------|
86
- | P2-101 | 输出与原图尺寸相同的二值图 | ⭐⭐⭐ |
87
- | P2-102 | 涂抹区域颜色可配置(默认黑色) | ⭐⭐⭐ |
88
- | P2-103 | 支持 PNG 和 JPG 格式输出 | ⭐⭐⭐ |
89
- | P2-104 | JPG 格式自动处理背景色 | ⭐⭐ |
90
-
91
- ### 2.3 工具切换
92
-
93
- #### 2.3.1 工具栏
94
-
95
- | 需求编号 | 功能描述 | 优先级 |
96
- |----------|----------|--------|
97
- | P3-001 | 选择工具(热键 V) | ⭐⭐⭐ |
98
- | P3-002 | 点标注工具(热键 P) | ⭐⭐⭐ |
99
- | P3-003 | 笔刷工具(热键 B) | ⭐⭐⭐ |
100
- | P3-004 | 擦除工具(热键 E) | ⭐⭐⭐ |
101
- | P3-005 | 显示当前激活工具状态 | ⭐⭐⭐ |
102
- | P3-006 | 点击笔刷工具显示/隐藏配置面板 | ⭐⭐⭐ |
103
- | P3-007 | 点击面板外其他地方关闭配置面板 | ⭐⭐⭐ |
104
-
105
- #### 2.3.2 Editor 动态控制
106
-
107
- | 需求编号 | 功能描述 | 优先级 |
108
- |----------|----------|--------|
109
- | P3-101 | 仅在 select point 模式下启用 editor | ⭐⭐⭐ |
110
- | P3-102 | 笔刷和擦除模式下禁用 editor(避免干扰) | ⭐⭐⭐ |
111
- | P3-103 | 标签编辑权限控制:仅 select point 工具可编辑 | ⭐⭐⭐ |
112
- | P3-104 | focusout 时自动取消编辑器选中状态 | ⭐⭐ |
113
-
114
- ### 2.4 撤销/重做
115
-
116
- | 需求编号 | 功能描述 | 优先级 |
117
- |----------|----------|--------|
118
- | P4-001 | 支持撤销操作(Ctrl+Z) | ⭐⭐⭐ |
119
- | P4-002 | 支持重做操作(Ctrl+Y) | ⭐⭐⭐ |
120
- | P4-003 | 点标注和笔刷操作统一管理 | ⭐⭐⭐ |
121
- | P4-004 | 清除和擦除操作也支持撤销 | ⭐⭐⭐ |
122
- | P4-005 | 可配置历史记录步数限制(默认100) | ⭐⭐ |
123
-
124
- ### 2.5 导出/导入
125
-
126
- #### 2.5.1 导出功能
127
-
128
- | 需求编号 | 功能描述 | 优先级 |
129
- |----------|----------|--------|
130
- | P5-001 | 导出完整画布 JSON 数据(包含笔刷 mask) | ⭐⭐⭐ |
131
- | P5-002 | 导出 COCO 格式数据 | ⭐⭐ |
132
- | P5-003 | 导出 YOLO 格式数据 | ⭐⭐ |
133
- | P5-004 | 导出二值图 Mask(PNG/JPG 可选) | ⭐⭐⭐ |
134
- | P5-005 | 二值图前景色可配置(黑/白) | ⭐⭐⭐ |
135
- | P5-006 | 导出格式可通过 options 配置 | ⭐⭐ |
136
-
137
- #### 2.5.2 导入功能
138
-
139
- | 需求编号 | 功能描述 | 优先级 |
140
- |----------|----------|--------|
141
- | P5-101 | 支持导入 JSON 数据 | ⭐⭐⭐ |
142
- | P5-102 | 支持重置缩放和自适应图片 | ⭐⭐⭐ |
143
- | P5-103 | 支持导入不同 URL 的图片 | ⭐⭐ |
144
-
145
- ### 2.6 画布控制
146
-
147
- | 需求编号 | 功能描述 | 优先级 |
148
- |----------|----------|--------|
149
- | P6-001 | 支持画布缩放(+/-) | ⭐⭐⭐ |
150
- | P6-002 | 支持重置缩放 | ⭐⭐⭐ |
151
- | P6-003 | 支持图片自适应画布 | ⭐⭐⭐ |
152
- | P6-004 | 显示当前缩放比例 | ⭐⭐ |
153
-
154
- ### 2.7 本地图片上传功能
155
-
156
- | 需求编号 | 功能描述 | 优先级 |
157
- |----------|----------|--------|
158
- | P7-001 | imageSource 未提供时,显示本地上传界面 | ⭐⭐⭐ |
159
- | P7-002 | 支持点击选择本地图片文件 | ⭐⭐⭐ |
160
- | P7-003 | 支持拖拽图片文件到画布区域上传 | ⭐⭐⭐ |
161
- | P7-004 | 本地图片上传后,自动加载到画布 | ⭐⭐⭐ |
162
- | P7-005 | 上传区域样式:占满整个画布,虚线边框,拖拽时有视觉反馈 | ⭐⭐⭐ |
163
-
164
- ### 2.8 清除所有功能
165
-
166
- | 需求编号 | 功能描述 | 优先级 |
167
- |----------|----------|--------|
168
- | P8-001 | select 模式下未选中任何标注点时,按 Delete 清除所有标注和笔刷 | ⭐⭐⭐ |
169
- | P8-002 | 清除前显示确认对话框 | ⭐⭐⭐ |
52
+ ## 2. 功能需求(按模块)
53
+
54
+ ### 2.1 图片加载与显示(P0-XXX)
55
+
56
+ | 编号 | 功能描述 | 优先级 | 状态 |
57
+ |------|----------|--------|------|
58
+ | P0-001 | 通过 `props.imageSource.url` 加载远程图片 | ⭐⭐⭐ | ✅ |
59
+ | P0-002 | 未传 imageSource 时显示大面积上传区域(点击 + 拖拽均可) | ⭐⭐⭐ | ✅ |
60
+ | P0-003 | 本地上传后若 props.imageSource.url 变化,应丢弃本地标记并重新加载远程 | ⭐⭐⭐ | ✅ |
61
+ | P0-004 | 无图片时不渲染画布、工具栏、缩放控制器,仅显示上传区域 | ⭐⭐⭐ | ✅ |
62
+ | P0-005 | 提供 `loadStart` / `loadSuccess` / `loadError` 事件 | ⭐⭐⭐ | ✅ |
63
+ | P0-006 | 图片加载成功后画布自动适应图片尺寸 | ⭐⭐⭐ | ✅ |
64
+ | P0-007 | 通过 `options.canvasBackground` 自定义画布背景色 | ⭐⭐ | ✅ |
65
+
66
+ ### 2.2 点标注功能(P1-XXX)
67
+
68
+ | 编号 | 功能描述 | 优先级 | 状态 |
69
+ |------|----------|--------|------|
70
+ | P1-001 | 在图片上点击添加标注点(需在 point 工具下) | ⭐⭐⭐ | ✅ |
71
+ | P1-002 | 标注点由 `Ellipse`(圆点) + `Text`(序号/label)构成,继承自 `Group` | ⭐⭐⭐ | ✅ |
72
+ | P1-003 | 标注点支持拖拽移动(在 select 工具下) | ⭐⭐⭐ | ✅ |
73
+ | P1-004 | 标注点支持删除:Delete / 工具栏按钮 / `removePointAnnotation(id)` | ⭐⭐⭐ | ✅ |
74
+ | P1-005 | 点序号:按 `sequenceNumber` 自动显示 1,2,3...;删除后剩余点自动重排 | ⭐⭐⭐ | ✅ |
75
+ | P1-006 | `order` 字段保留历史创建顺序(不随删除变化);`sequenceNumber` 是显示序号 | ⭐⭐⭐ | ✅ |
76
+ | P1-007 | label 编辑:通过 `updatePointAnnotationLabel(id, label)` 修改文案;label 不允许为空 | ⭐⭐⭐ | ✅ |
77
+ | P1-008 | undo/redo 时 label.text 的"自动重排变值"不应被误判为用户手动编辑 | ⭐⭐⭐ | ✅ |
78
+ | P1-009 | hover 状态:鼠标移上去圆点颜色变深(hoverCircleFill) | ⭐⭐⭐ | ✅ |
79
+ | P1-010 | press 状态:按下时圆点进一步变化 | ⭐⭐⭐ | ✅ |
80
+ | P1-011 | selected 状态:selectedCircleFill + selectedCircleScale 放大 | ⭐⭐⭐ | ✅ |
81
+ | P1-012 | 点标注数据:像素坐标 + 归一化坐标 + label + 时间戳 | ⭐⭐⭐ | ✅ |
82
+ | P1-013 | `pointChange` 事件:每次点新增/删除/修改/重排都 emit | ⭐⭐⭐ | ✅ |
83
+ | P1-014 | `maxPoints` 可配置最大点数限制 | ⭐⭐ | ✅ |
84
+ | P1-015 | `options.pointStyle` 可覆盖默认样式(circleRadius、circleFill、circleStroke、hover*、selected*、label 背景/文字/字号、fixedSizeOnZoom 等) | ⭐⭐⭐ | ✅ |
85
+ | P1-016 | `fixedSizeOnZoom` 让点不随画布缩放而变大 | ⭐⭐ | ✅ |
86
+
87
+ ### 2.3 笔刷涂抹功能(P2-XXX)
88
+
89
+ | 编号 | 功能描述 | 优先级 | 状态 |
90
+ |------|----------|--------|------|
91
+ | P2-001 | 笔刷工具:拖动鼠标/触摸涂抹 | ⭐⭐⭐ | ✅ |
92
+ | P2-002 | 橡皮擦工具:擦除已绘制内容 | ⭐⭐⭐ | ✅ |
93
+ | P2-003 | 笔刷大小可调:通过 `updateBrushStyle({size})` 或工具栏面板 | ⭐⭐⭐ | ✅ |
94
+ | P2-004 | 笔刷颜色可调 | ⭐⭐⭐ | ✅ |
95
+ | P2-005 | 笔刷透明度可调(通过 Group.opacity,避免叠加问题) | ⭐⭐⭐ | ✅ |
96
+ | P2-006 | 笔刷连续性阈值 `continuity`:超过则不连线 | ⭐⭐ | ✅ |
97
+ | P2-007 | 实时预览涂抹效果(canvas 即时渲染) | ⭐⭐⭐ | ✅ |
98
+ | P2-008 | 清空当前图层笔刷:`clearBrush()` | ⭐⭐⭐ | ✅ |
99
+ | P2-009 | 清空所有图层笔刷:`clearAllBrushLayers()` | ⭐⭐⭐ | |
100
+ | P2-010 | 多图层笔刷:通过 `options.brushLayers` 配置多个图层(`{label, value, color?, opacity?, size?}`) | ⭐⭐⭐ | ✅ |
101
+ | P2-011 | 默认单图层:不传 brushLayers 时自动创建 `{label:'默认图层', value:'default'}` | ⭐⭐⭐ | ✅ |
102
+ | P2-012 | 图层切换:`setActiveLayer(value)` 或通过 `v-model:currentLayer` 受控驱动 | ⭐⭐⭐ | ✅ |
103
+ | P2-013 | `effectiveCurrentLayer` / `activeCanvasBrush` 为运行时当前笔刷的入口 | ⭐⭐⭐ | ✅ |
104
+ | P2-014 | 笔刷撤销/重做:基于 ImageData 快照(BrushSnapshotCommand) | ⭐⭐⭐ | ✅ |
105
+
106
+ ### 2.4 enableBrush 功能开关(P3-XXX)
107
+
108
+ > v1.1 新增
109
+
110
+ | 编号 | 功能描述 | 优先级 | 状态 |
111
+ |------|----------|--------|------|
112
+ | P3-001 | `options.enableBrush = false` 时禁用所有笔刷功能 | ⭐⭐⭐ | ✅ |
113
+ | P3-002 | 禁用时工具栏笔刷/橡皮擦按钮不渲染 | ⭐⭐⭐ | ✅ |
114
+ | P3-003 | 禁用时 BrushStylePanel 不渲染 | ⭐⭐⭐ | ✅ |
115
+ | P3-004 | 禁用时 `brushTool()` / `eraserTool()` / `updateBrushStyle()` / `clearBrush()` / `clearAllBrushLayers()` / `createBrushFromPoints()` 直接 return | ⭐⭐⭐ | ✅ |
116
+ | P3-005 | 禁用时 `setTool('brush'|'eraser')` 被拦截 | ⭐⭐⭐ | ✅ |
117
+ | P3-006 | 禁用时 `initBrushLayers` 不创建 canvas 实例,仅清除旧的 | ⭐⭐⭐ | ✅ |
118
+ | P3-007 | 禁用时快捷键 `b` / `e` 不生效 | ⭐⭐⭐ | ✅ |
119
+ | P3-008 | 禁用时 `exportMaskImage*` / `getMaskBlob` / `getMaskFile` / `getAllMaskBlobs` 返回 null 或 {} | ⭐⭐⭐ | ✅ |
120
+ | P3-009 | 禁用时删除/清空确认文案不出现"笔刷"字样 | ⭐⭐ | |
121
+ | P3-010 | 运行时切换 enableBrush:开启时若有画布则重建笔刷层;关闭时自动把当前 tool 重置为 select | ⭐⭐ | ✅ |
122
+
123
+ ### 2.5 点轨迹生成笔刷区域(P4-XXX)
124
+
125
+ > v1.1 新增
126
+
127
+ | 编号 | 功能描述 | 优先级 | 状态 |
128
+ |------|----------|--------|------|
129
+ | P4-001 | `createBrushFromPoints()`:按所有点标注的 sequenceNumber 顺序将像素坐标连成多边形 | ⭐⭐⭐ | |
130
+ | P4-002 | 点数量 < 3 时不操作(无法形成多边形) | ⭐⭐⭐ | ✅ |
131
+ | P4-003 | 自动闭合多边形起点终点 | ⭐⭐⭐ | |
132
+ | P4-004 | 使用当前笔刷的 color + opacity 填充 | ⭐⭐⭐ | ✅ |
133
+ | P4-005 | 支持撤销/重做(通过 BrushSnapshotCommand 保存操作前后快照) | ⭐⭐⭐ | ✅ |
134
+
135
+ ### 2.6 导出功能(P5-XXX)
136
+
137
+ | 编号 | 功能描述 | 优先级 | 状态 |
138
+ |------|----------|--------|------|
139
+ | P5-001 | `getPointAnnotations()`:获取当前所有点标注(含像素/归一化坐标、label) | ⭐⭐⭐ | ✅ |
140
+ | P5-002 | `exportMaskImage()`:当前图层导出为 mask(dataURL);前景色可配置黑/白 | ⭐⭐⭐ | ✅ |
141
+ | P5-003 | `exportMaskImageByLayer(layerValue)`:指定图层 mask | ⭐⭐⭐ | ✅ |
142
+ | P5-004 | `exportAllMaskImages()`:所有图层 mask → Record<layerValue, dataURL> | ⭐⭐⭐ | ✅ |
143
+ | P5-005 | `getMaskBlob(layerValue?, format?, fg?)`:当前/指定图层 mask 导出为 Blob(后端上传用) | ⭐⭐⭐ | ✅ |
144
+ | P5-006 | `getMaskFile(layerValue?, filename?, format?, fg?)`:当前/指定图层 mask 导出为 File | ⭐⭐⭐ | ✅ |
145
+ | P5-007 | `getAllMaskBlobs()`:所有图层 Blob 集合 | ⭐⭐⭐ | ✅ |
146
+ | P5-008 | `exportCOCO()`:导出 COCO JSON(keypoints) | ⭐⭐ | ✅ |
147
+ | P5-009 | `exportYOLO()`:导出 YOLO 标注文件 | ⭐⭐ | ✅ |
148
+ | P5-010 | `exportCanvasJSON()`:全量导出(点 + 笔刷 + 图片信息) | ⭐⭐⭐ | ✅ |
149
+ | P5-011 | `importCanvasJSON(data)`:从导出的 JSON 恢复画布 | ⭐⭐ | ✅ |
150
+ | P5-012 | 导出格式支持 png / jpeg,通过 options 或调用参数指定 | ⭐⭐ | ✅ |
151
+
152
+ ### 2.7 工具栏与快捷键(P6-XXX)
153
+
154
+ | 编号 | 功能描述 | 优先级 | 状态 |
155
+ |------|----------|--------|------|
156
+ | P6-001 | 默认工具栏含:select、point、brush、eraser、delete、clear、undo、redo 按钮 | ⭐⭐⭐ | ✅ |
157
+ | P6-002 | `options.showToolbar = false` 可隐藏默认工具栏 | ⭐⭐⭐ | ✅ |
158
+ | P6-003 | `options.showZoomController = false` 可隐藏缩放控制器 | ⭐⭐⭐ | ✅ |
159
+ | P6-004 | 父组件可通过 `ref` 调用暴露的方法自定义工具栏 | ⭐⭐⭐ | ✅ |
160
+ | P6-005 | 快捷键 `v` 选择工具 / `p` 点工具 / `b` 笔刷 / `e` 橡皮 / `Ctrl+Z` 撤销 / `Ctrl+Y` 重做 / `Delete` 删除选中 / `Ctrl++` 放大 / `Ctrl+-` 缩小 / `Ctrl+0` 重置 | ⭐⭐⭐ | ✅ |
161
+ | P6-006 | 快捷键生效条件:画布 focus 或鼠标 hover 画布 | ⭐⭐⭐ | ✅ |
162
+ | P6-007 | `Alt` 键显示/隐藏快捷键提示浮层 | ⭐⭐ | ✅ |
163
+ | P6-008 | `b` / `e` 快捷键受 `enableBrush` 限制 | ⭐⭐⭐ | ✅ |
164
+
165
+ ### 2.8 撤销与重做(P7-XXX)
166
+
167
+ | 编号 | 功能描述 | 优先级 | 状态 |
168
+ |------|----------|--------|------|
169
+ | P7-001 | 点新增:通过 AddPointCommand 进栈 | ⭐⭐⭐ | ✅ |
170
+ | P7-002 | 点删除:通过 RemovePointCommand 进栈 | ⭐⭐⭐ | ✅ |
171
+ | P7-003 | 笔刷绘制:通过 BrushSnapshotCommand 进栈(基于 ImageData 快照) | ⭐⭐⭐ | ✅ |
172
+ | P7-004 | 笔刷擦除:同上 | ⭐⭐⭐ | ✅ |
173
+ | P7-005 | 点轨迹生成笔刷:同上 | ⭐⭐⭐ | ✅ |
174
+ | P7-006 | 最大 undo 步数:可配置(options.maxUndoSteps),默认 100 | ⭐⭐ | ✅ |
175
+ | P7-007 | `undoStateChange` / `redoStateChange` 事件供父组件监听 undo/redo 是否可继续 | ⭐⭐ | ✅ |
176
+
177
+ ### 2.9 缩放与视口(P8-XXX)
178
+
179
+ | 编号 | 功能描述 | 优先级 | 状态 |
180
+ |------|----------|--------|------|
181
+ | P8-001 | `zoomIn()` / `zoomOut()` / `resetZoom()` 可通过 ref 调用 | ⭐⭐⭐ | ✅ |
182
+ | P8-002 | 顶部缩放控制器显示当前百分比并可调 | ⭐⭐⭐ | ✅ |
183
+ | P8-003 | `options.zoomMin` / `options.zoomMax` 限制缩放范围 | ⭐⭐ | ✅ |
184
+ | P8-004 | 画布平移/缩放通过 @leafer-in/viewport 驱动 | ⭐⭐⭐ | ✅ |
185
+
186
+ ### 2.10 父组件 API 暴露(P9-XXX)
187
+
188
+ > 完整方法列表详见 ARCHITECTURE.md 3.6
189
+
190
+ | 分类 | 已暴露方法 |
191
+ |------|-----------|
192
+ | 点标注 | `getPointAnnotations`, `createPointAnnotation`, `removePointAnnotation`, `updatePointAnnotationLabel` |
193
+ | 图片 & 画布 | `getImageInfo`, `loadImage` |
194
+ | 工具切换 | `getCurrentTool`, `setTool`, `selectTool`, `pointTool`, `brushTool`, `eraserTool` |
195
+ | 删除 / 清空 | `deleteSelected`, `clearAllAnnotationsAndBrush`, `clearBrush`, `clearAllBrushLayers` |
196
+ | 笔刷图层 | `getCurrentLayer`, `setActiveLayer`, `getAllLayers` |
197
+ | 笔刷样式 | `getBrushStyle`, `updateBrushStyle` |
198
+ | 点轨迹 | `createBrushFromPoints` |
199
+ | 缩放 | `zoomIn`, `zoomOut`, `resetZoom` |
200
+ | 撤销 / 重做 | `undo`, `redo` |
201
+ | 导入导出 | `exportCanvasJSON`, `importCanvasJSON`, `exportMaskImage`, `exportMaskImageByLayer`, `exportAllMaskImages`, `getMaskBlob`, `getMaskFile`, `getAllMaskBlobs`, `exportCOCO`, `exportYOLO` |
170
202
 
171
203
  ---
172
204
 
173
- ## 3. 数据结构
174
-
175
- ### 3.1 点标注数据
176
-
177
- ```typescript
178
- interface PointAnnotation {
179
- id: string; // "point_" + UUID
180
- pixel: {
181
- x: number; // 像素坐标 X
182
- y: number; // 像素坐标 Y
183
- };
184
- normalized: {
185
- x: number; // 归一化坐标 X (0-1)
186
- y: number; // 归一化坐标 Y (0-1)
187
- };
188
- label: string; // 标签文本
189
- createdAt: number; // 创建时间戳
190
- updatedAt: number; // 更新时间戳
191
- }
192
- ```
193
-
194
- ### 3.2 笔刷数据(内部快照)
195
-
196
- ```typescript
197
- interface BrushSnapshot {
198
- id: string;
199
- imageData: string; // Base64 编码的图片数据
200
- timestamp: number;
201
- }
202
- ```
203
-
204
- ### 3.3 导出数据结构
205
-
206
- ```typescript
207
- interface ExportData {
208
- version: string; // 数据版本
209
- imageUrl: string; // 图片地址
210
- imageWidth: number; // 图片宽度
211
- imageHeight: number; // 图片高度
212
- pointAnnotations: PointAnnotation[];
213
- brushMask: string | null; // Base64 编码的笔刷 mask
214
- exportTime: number; // 导出时间
215
- }
216
- ```
217
-
218
- ### 3.4 COCO 格式数据
219
-
220
- ```typescript
221
- interface COCOAnnotation {
222
- id: number;
223
- image_id: number;
224
- category_id: number;
225
- keypoints: number[]; // [x, y, visibility] 格式
226
- num_keypoints: number;
227
- bbox: [number, number, number, number];
228
- area: number;
229
- iscrowd: number;
230
- }
231
-
232
- interface COCOImage {
233
- id: number;
234
- file_name: string;
235
- width: number;
236
- height: number;
237
- }
238
-
239
- interface COCOCategory {
240
- id: number;
241
- name: string;
242
- keypoints: string[];
243
- skeleton: number[][];
244
- }
245
-
246
- interface COCOExport {
247
- info: {
248
- description: string;
249
- version: string;
250
- year: number;
251
- date_created: string;
252
- };
253
- licenses: any[];
254
- images: COCOImage[];
255
- annotations: COCOAnnotation[];
256
- categories: COCOCategory[];
257
- }
258
- ```
259
-
260
- ### 3.5 YOLO 格式数据
261
-
262
- ```typescript
263
- interface YOLOExport {
264
- annotations: string; // YOLO 格式的标注字符串
265
- classNames: string; // 类名列表(每行一个)
266
- }
267
- ```
268
-
269
- ---
270
-
271
- ## 4. Props 配置
272
-
273
- ### 4.1 图片源配置
274
-
275
- | 属性名 | 类型 | 默认值 | 说明 |
276
- |--------|------|--------|------|
277
- | imageSource | { id?: string, url: string } \| null | null | 图片源配置(可选,未提供时显示本地上传界面) |
278
-
279
- ### 4.2 点标注样式配置
280
-
281
- | 属性名 | 类型 | 默认值 | 说明 |
282
- |--------|------|--------|------|
283
- | options.pointStyle.circleRadius | number | 12 | 圆点半径 |
284
- | options.pointStyle.circleFill | string | '#ff4d4f' | 圆点填充色 |
285
- | options.pointStyle.circleStroke | string | '#ffffff' | 圆点边框色 |
286
- | options.pointStyle.circleStrokeWidth | number | 2 | 圆点边框宽度 |
287
- | options.pointStyle.hoverCircleFill | string | '#ff7875' | Hover 填充色 |
288
- | options.pointStyle.hoverCircleStroke | string | '#ffffff' | Hover 边框色 |
289
- | options.pointStyle.selectedCircleFill | string | '#1890ff' | Selected 填充色 |
290
- | options.pointStyle.selectedCircleStroke | string | '#ffffff' | Selected 边框色 |
291
- | options.pointStyle.selectedCircleScale | number | 1.2 | Selected 放大比例 |
292
- | options.pointStyle.labelBackgroundColor | string | '#ffffff' | 标签背景色 |
293
- | options.pointStyle.labelTextColor | string | '#333333' | 标签文字色 |
294
- | options.pointStyle.labelFontSize | number | 12 | 标签字体大小 |
295
- | options.pointStyle.labelPadding | number[] | [2, 4] | 标签内边距 |
296
- | options.pointStyle.fixedSizeOnZoom | boolean | false | 是否开启标注点固定大小(不随画布缩放) |
297
- | options.pointStyle.fixedSizeScale | number | 1 | 固定大小的缩放系数 |
298
-
299
- ### 4.3 笔刷样式配置
300
-
301
- | 属性名 | 类型 | 默认值 | 说明 |
302
- |--------|------|--------|------|
303
- | options.brushStyle.color | string | 'rgba(255,0,0,1)' | 笔刷颜色 |
304
- | options.brushStyle.opacity | number | 0.55 | 透明度 |
305
- | options.brushStyle.size | number | 100 | 默认笔刷大小 |
306
- | options.brushStyle.minSize | number | 50 | 最小笔刷大小 |
307
- | options.brushStyle.maxSize | number | 150 | 最大笔刷大小 |
308
- | options.brushStyle.continuity | number | 28 | 连续性阈值(像素) |
309
-
310
- ### 4.4 导出配置
311
-
312
- | 属性名 | 类型 | 默认值 | 说明 |
313
- |--------|------|--------|------|
314
- | options.maskExportFormat | 'png' \| 'jpg' \| 'jpeg' | 'png' | Mask 导出格式 |
315
- | options.maskExportForeground | 'black' \| 'white' | 'black' | Mask 前景色 |
316
-
317
- ### 4.5 其他配置
318
-
319
- | 属性名 | 类型 | 默认值 | 说明 |
320
- |--------|------|--------|------|
321
- | options.maxUndoSteps | number | 100 | 最大撤销/重做步数 |
322
-
323
- ---
324
-
325
- ## 5. 交互设计
326
-
327
- ### 5.1 热键映射
328
-
329
- | 热键 | 功能 | 适用工具 |
330
- |------|------|----------|
331
- | V | 选择工具 | 所有 |
332
- | P | 点标注工具 | 所有 |
333
- | B | 笔刷工具 | 所有 |
334
- | E | 擦除工具 | 所有 |
335
- | Ctrl+Z | 撤销 | 所有 |
336
- | Ctrl+Y | 重做 | 所有 |
337
- | Delete | 删除选中/清除所有 | 所有 |
338
- | Ctrl++ | 放大 | 所有 |
339
- | Ctrl+- | 缩小 | 所有 |
340
- | Ctrl+0 | 重置缩放 | 所有 |
341
- | Alt | 显示/隐藏热键提示 | 所有 |
342
-
343
- ### 5.2 工具栏交互
344
-
345
- 1. **工具切换**:点击工具栏按钮切换当前工具,激活状态高亮显示
346
- 2. **笔刷配置面板**:点击笔刷工具按钮后,在按钮上方浮动显示配置面板
347
- 3. **浮动面板关闭**:点击空白处或按 ESC 键关闭浮动配置面板
348
- 4. **热键提示**:按住 Alt 键显示热键提示
349
-
350
- ### 5.3 点标注交互
351
-
352
- 1. **创建**:点击画布空白处创建新标注点
353
- 2. **移动**:拖拽标注点到目标位置
354
- 3. **编辑标签**:双击标签进入编辑模式,失焦后校验唯一性
355
- 4. **选择**:点击标注点选中,支持多选(Shift+点击)
356
- 5. **删除**:选中后按 Delete 键或点击删除按钮
357
-
358
- ### 5.4 笔刷交互
359
-
360
- 1. **绘制**:按住鼠标左键拖动进行涂抹
361
- 2. **擦除**:切换到擦除工具后,按住鼠标左键拖动擦除
362
- 3. **清除**:点击删除按钮或按 Delete 键清除所有涂抹
363
- 4. **配置**:点击笔刷工具按钮打开配置面板调整样式
364
-
365
- ### 5.5 本地图片上传交互
366
-
367
- 1. **无图片状态**:imageSource 未提供时,整个画布区域显示上传界面
368
- 2. **点击上传**:点击"选择图片"按钮打开文件选择器
369
- 3. **拖拽上传**:拖拽图片文件到画布区域,显示高亮边框反馈
370
- 4. **文件选择**:选择本地图片后,自动加载到画布
371
- 5. **图片切换**:通过 props.imageSource.url 变化时,自动切换图片并清空画布
372
- 6. **图片清空**:props.imageSource.url 为空时,清空画布回到上传状态
373
-
374
- ### 5.6 清除所有交互
375
-
376
- 1. **条件**:select 模式下未选中任何标注点
377
- 2. **触发**:点击删除按钮或按 Delete 键
378
- 3. **确认**:显示确认对话框
379
- 4. **执行**:确认后清除所有标注点和笔刷绘制
205
+ ## 3. 非功能需求(NFR)
206
+
207
+ | 编号 | 描述 | 优先级 | 状态 |
208
+ |------|------|--------|------|
209
+ | NFR-001 | 组件必须以 TypeScript 开发并对外暴露完整类型 | ⭐⭐⭐ | ✅ |
210
+ | NFR-002 | 使用 Composition API + `<script setup>` | ⭐⭐⭐ | ✅ |
211
+ | NFR-003 | 构建产物必须同时提供 ESM 和 UMD 两种格式 | ⭐⭐⭐ | ✅ |
212
+ | NFR-004 | 构建产物必须包含独立的 CSS 文件(不随 JS 自动注入) | ⭐⭐⭐ | ✅ |
213
+ | NFR-005 | 构建产物必须包含 .d.ts 类型声明文件 | ⭐⭐⭐ | ✅ |
214
+ | NFR-006 | Vue 3 作为 peerDependency(不打包进产物) | ⭐⭐⭐ | ✅ |
215
+ | NFR-007 | 样式文件必须手动 import:`import '@zzalai/leafer-point-annotation/dist/leafer-point-annotation.css'` | ⭐⭐⭐ | ✅ |
216
+ | NFR-008 | 使用 pnpm 作为包管理器 | ⭐⭐ | ✅ |
217
+ | NFR-009 | GitHub Pages 演示站由 `docs/` 目录提供(通过 `vite.docs.config.ts` 构建) | ⭐⭐⭐ | ✅ |
218
+ | NFR-010 | `dist/` 必须干净:仅含 `.es.js` / `.umd.js` / `.css` / `.d.ts` | ⭐⭐⭐ | ✅ |
219
+ | NFR-011 | `build:all` 一步构建(dist + docs 同时生成) | ⭐⭐ | ✅ |
220
+ | NFR-012 | 中文 README + 英文 README 同步维护 | ⭐⭐ | ✅ |
380
221
 
381
222
  ---
382
223
 
383
- ## 6. 导出格式说明
384
-
385
- ### 6.1 JSON Full 格式
386
-
387
- 包含完整的标注数据和笔刷数据,用于数据备份和恢复。
388
-
389
- ```json
390
- {
391
- "version": "1.0",
392
- "imageUrl": "https://example.com/image.jpg",
393
- "imageWidth": 1280,
394
- "imageHeight": 720,
395
- "pointAnnotations": [
396
- {
397
- "id": "point_xxx",
398
- "pixel": { "x": 100, "y": 200 },
399
- "normalized": { "x": 0.078, "y": 0.278 },
400
- "label": "#1",
401
- "createdAt": 1716960000000,
402
- "updatedAt": 1716960000000
403
- }
404
- ],
405
- "brushMask": "data:image/png;base64,...",
406
- "exportTime": 1716960000000
407
- }
408
- ```
409
-
410
- ### 6.2 COCO 格式
411
-
412
- 符合 COCO 数据集标注格式,用于关键点检测模型训练。
413
-
414
- ```json
415
- {
416
- "info": {
417
- "description": "Point Annotation Export",
418
- "version": "1.0",
419
- "year": 2024,
420
- "date_created": "2024-05-01"
421
- },
422
- "licenses": [],
423
- "images": [
424
- {
425
- "id": 1,
426
- "file_name": "image.jpg",
427
- "width": 1280,
428
- "height": 720
429
- }
430
- ],
431
- "annotations": [
432
- {
433
- "id": 1,
434
- "image_id": 1,
435
- "category_id": 1,
436
- "keypoints": [100, 200, 2],
437
- "num_keypoints": 1,
438
- "bbox": [92, 192, 24, 24],
439
- "area": 452.16,
440
- "iscrowd": 0
441
- }
442
- ],
443
- "categories": [
444
- {
445
- "id": 1,
446
- "name": "point",
447
- "keypoints": ["point"],
448
- "skeleton": []
449
- }
450
- ]
451
- }
452
- ```
224
+ ## 4. 约束与边界
453
225
 
454
- ### 6.3 YOLO 格式
455
-
456
- 符合 YOLO 数据集标注格式,包含归一化坐标。
457
-
458
- ```txt
459
- # 类名文件(classNames)
460
- point
461
-
462
- # 标注文件(annotations)
463
- 0 0.078 0.278 0.019 0.033
464
- ```
465
-
466
- ### 6.4 二值图 Mask 格式
467
-
468
- PNG 或 JPG 格式图片,涂抹区域为前景色,其余为透明(PNG)或背景色(JPG)。
469
-
470
- - PNG:涂抹区域为前景色,其余透明
471
- - JPG:涂抹区域为前景色,其余为背景色(前景为黑则背景为白,反之亦然)
226
+ | 编号 | 约束/边界 | 说明 |
227
+ |------|----------|------|
228
+ | C-001 | 仅支持 Vue 3.3+ | 不支持 Vue 2 |
229
+ | C-002 | 仅支持图片(jpg/png/webp 等浏览器可加载格式) | 不支持视频/多帧图像 |
230
+ | C-003 | 点标注顺序由 `sequenceNumber` 决定,`order` 只表示历史创建顺序 | 历史兼容用 |
231
+ | C-004 | 笔刷图层不支持运行时动态增删(只能通过 options.brushLayers 配置,变了会重建) | 避免运行时图层表与已绘制数据不一致 |
232
+ | C-005 | enableBrush=false 时,即使 options.brushLayers 有值也不会创建笔刷 canvas | enableBrush 优先级更高 |
233
+ | C-006 | 笔刷撤销基于 ImageData 快照;对超大图片,单张快照 ~4*W*H 字节,maxUndoSteps 不宜过大 | 性能注意 |
234
+ | C-007 | Mask 导出格式 png/jpeg,jpeg 会丢失透明度(建议 png 用于二值图) | 导出注意 |
235
+ | C-008 | getMaskBlob/getMaskFile/getAllMaskBlobs 基于 HTMLCanvasElement.toBlob,必须在浏览器环境(不支持 SSR) | 服务端渲染不适用 |
472
236
 
473
237
  ---
474
238
 
475
- ## 7. 对外 API
476
-
477
- ### 7.1 暴露的方法
478
-
479
- | 方法名 | 参数 | 返回值 | 功能描述 |
480
- |--------|------|--------|----------|
481
- | getPointAnnotations | 无 | PointAnnotation[] | 获取所有点标注数据 |
482
- | getImageInfo | 无 | { id?: string, url: string, width: number, height: number } | 获取图片信息 |
483
- | exportCanvasJSON | 无 | string | 导出完整 JSON 数据 |
484
- | exportMaskImage | format?: string, fgColor?: string | Promise&lt;string \| null&gt; | 导出二值图 dataURL |
485
- | exportCOCO | 无 | string | 导出 COCO 格式 JSON 字符串 |
486
- | exportYOLO | 无 | { annotations: string, classNames: string } | 导出 YOLO 格式数据 |
487
- | importCanvasJSON | jsonString: string, options?: { resetZoom?: boolean } | Promise&lt;boolean&gt; | 导入 JSON 数据 |
488
- | loadImage | url?: string | Promise&lt;void&gt; | 手动加载图片 |
489
- | clearBrush | 无 | void | 清除所有笔刷内容 |
490
- | zoomIn | 无 | void | 放大画布 |
491
- | zoomOut | 无 | void | 缩小画布 |
492
- | resetZoom | 无 | void | 重置缩放 |
493
- | undo | 无 | void | 撤销操作 |
494
- | redo | 无 | void | 重做操作 |
495
- | getCurrentTool | 无 | 'select' \| 'point' \| 'brush' \| 'eraser' | 获取当前工具 |
496
- | setTool | tool: string | void | 设置当前工具 |
497
- | createPointAnnotation | x: number, y: number | string \| null | 创建标注点,返回 id |
498
- | removePointAnnotation | id: string | boolean | 删除指定 id 的点标注 |
499
-
500
- ### 7.2 事件
501
-
502
- | 事件名 | 参数 | 说明 |
503
- |--------|------|------|
504
- | pointChange | PointAnnotation[] | 点标注数据变化 |
505
- | loadStart | - | 图片开始加载 |
506
- | loadSuccess | - | 图片加载成功 |
507
- | loadError | error: any | 图片加载失败 |
508
- | undoStateChange | - | 撤销状态变化 |
509
- | redoStateChange | - | 重做状态变化 |
239
+ ## 5. 版本与发布流程
510
240
 
511
- ---
241
+ ### 5.1 构建验证清单(发布前必须全部 ✅)
512
242
 
513
- ## 8. 性能要求
243
+ - [ ] `pnpm run build:all` 执行无错误
244
+ - [ ] `dist/` 目录包含:`.es.js`、`.umd.js`、`.css`、`index.d.ts`
245
+ - [ ] `docs/` 目录包含最新演示站点(可本地预览验证)
246
+ - [ ] `tsc --noEmit` 类型检查通过
247
+ - [ ] README.md / README_EN.md 已同步更新
248
+ - [ ] `project-docs/` 中文档已同步
249
+ - [ ] skills/project-context 文件已更新
514
250
 
515
- ### 8.1 响应时间
251
+ ### 5.2 发布命令
516
252
 
517
- | 操作 | 响应时间要求 |
518
- |------|--------------|
519
- | 图片加载 | < 3s |
520
- | 点标注创建 | < 50ms |
521
- | 笔刷绘制 | < 16ms(60fps) |
522
- | 缩放操作 | < 100ms |
523
- | 撤销/重做 | < 100ms |
253
+ ```bash
254
+ # 1. 安装依赖 & 构建
255
+ pnpm install
256
+ pnpm run build:all
524
257
 
525
- ### 8.2 浏览器兼容性
258
+ # 2. 升级版本(package.json)
259
+ # 例如:手动把 version 从 1.1.2 改为 1.1.3
526
260
 
527
- - Chrome 60+
528
- - Firefox 55+
529
- - Safari 12+
530
- - Edge 79+
261
+ # 3. 发布
262
+ npm publish # 需已登录 npm 账号,且有 @zzalai 命名空间权限
531
263
 
532
- ---
533
-
534
- **文档版本**:2.1
535
- **创建日期**:2024-04-28
536
- **最后更新**:2026-05-08
264
+ # 4. 推送到 GitHub(让 GitHub Pages 自动刷新)
265
+ git add .
266
+ git commit -m "chore: release v1.1.3"
267
+ git push
268
+ ```