@zzalai/leafer-point-annotation 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,402 @@
1
+ # @zzalai/leafer-point-annotation - Project Context
2
+
3
+ > 新开 AI 会话时阅读此文件,可快速了解项目全貌与核心能力
4
+
5
+ ---
6
+
7
+ ## 1. 项目概览
8
+
9
+ - **项目名称**: `@zzalai/leafer-point-annotation`
10
+ - **类型**: Vue 3 + TypeScript 组件库(发布到 npm)
11
+ - **核心用途**: 图像点标注 + 多图层笔刷涂抹,专为 AI 模型训练数据集标注设计
12
+ - **底层渲染**: LeaferJS(Canvas 渲染引擎)
13
+ - **构建工具**: Vite + vite-plugin-dts(生成 .d.ts 类型声明)
14
+ - **样式导入**: 使用者必须手动 `import '@zzalai/leafer-point-annotation/dist/leafer-point-annotation.css'`
15
+ - **演示站点**: GitHub Pages,由 `docs/` 目录提供(通过 `vite.docs.config.ts` 构建)
16
+ - **版本管理**: npm(`package.json` 的 `version` 字段)
17
+
18
+ ---
19
+
20
+ ## 2. 核心功能(功能清单)
21
+
22
+ ### 2.1 点标注功能
23
+ - 画布上点击添加标注点(point 工具下)
24
+ - 点拖拽移动(select 工具下)
25
+ - 删除点:Delete 键或 `deleteSelected()` 方法
26
+ - **序号自动重排**:删除点后剩余点按顺序重新编号(1, 2, 3...)
27
+ - 点支持 hover / selected 两态(颜色与缩放变化)
28
+ - `fixedSizeOnZoom`:点不随画布缩放变大
29
+ - 点样式完全可配(circleFill、circleStroke、hover*、selected*、label 样式等)
30
+ - `maxPoints` 可配置最大点数限制
31
+
32
+ ### 2.2 多图层笔刷功能
33
+ - 每个图层一个 HTML canvas + 独立 CanvasBrush 实例
34
+ - 通过 `options.brushLayers` 配置图层数组(不传则默认单图层 `{label:'默认图层', value:'default'}`)
35
+ - 笔刷可调:颜色、透明度(Group.opacity)、大小、连续性阈值
36
+ - 橡皮擦工具:擦除当前图层内容
37
+ - `clearBrush()` / `clearAllBrushLayers()` 清空笔刷
38
+ - 图层切换:`setActiveLayer(value)` 或 `v-model:current-layer`
39
+ - **撤销/重做**:基于 ImageData 快照(BrushSnapshotCommand)
40
+
41
+ ### 2.3 `enableBrush` 功能开关(v1.1 新增)
42
+ - `options.enableBrush = false` 时完全禁用笔刷相关功能
43
+ - 影响范围:工具栏笔刷/橡皮按钮、笔刷配置面板、`brushTool()`、`eraserTool()`、`updateBrushStyle()`、`clearBrush()`、`clearAllBrushLayers()`、`createBrushFromPoints()`、`exportMaskImage*()`、`getMaskBlob()`、`getMaskFile()`、`getAllMaskBlobs()`、快捷键 `b` / `e`
44
+ - 启用/禁用切换时会重建或清理笔刷图层
45
+ - 删除确认文案根据是否启用笔刷动态变化
46
+
47
+ ### 2.4 点轨迹生成笔刷区域(v1.1 新增)
48
+ - `createBrushFromPoints()` 方法
49
+ - 按 `sequenceNumber` 顺序将点的像素坐标连成闭合多边形
50
+ - 使用当前笔刷的 color + opacity 填充
51
+ - 点数量 < 3 时不执行
52
+ - 支持撤销/重做
53
+
54
+ ### 2.5 导出功能
55
+ - **JSON**: `exportCanvasJSON()` / `importCanvasJSON()` - 全量导入导出
56
+ - **COCO**: `exportCOCO()` - 点作为 keypoints
57
+ - **YOLO**: `exportYOLO()` - YOLO 格式标注
58
+ - **Mask (dataURL)**: `exportMaskImage()` / `exportMaskImageByLayer()` / `exportAllMaskImages()`
59
+ - **Mask (Blob)**: `getMaskBlob()` - 直接用于后端上传 `FormData.append()`
60
+ - **Mask (File)**: `getMaskFile()` - 带 filename + mime
61
+ - **Mask (多图层 Blob)**: `getAllMaskBlobs()` - 返回 `Record<layerValue, Blob>`
62
+
63
+ ### 2.6 工具栏与快捷键
64
+ - 内置工具栏包含:select、point、brush、eraser、delete、clear、undo、redo
65
+ - `showToolbar: false` / `showZoomController: false` 隐藏内置 UI,父组件自定义
66
+ - 快捷键生效条件:画布 focus 或 hover 画布
67
+ - 快捷键列表:`v` (select)、`p` (point)、`b` (brush, 需 enableBrush)、`e` (eraser, 需 enableBrush)、`Ctrl+Z` (undo)、`Ctrl+Y` (redo)、`Delete` (删除选中)、`Ctrl++/-` (缩放)、`Ctrl+0` (重置)、`Alt` (快捷键提示)
68
+
69
+ ### 2.7 图片加载
70
+ - `props.imageSource.url` 加载远程图片
71
+ - 不传 imageSource 时显示大面积上传区域(点击选择 + 拖拽文件)
72
+ - 本地图片上传后若 props.url 变化,会重新加载远程
73
+ - 无图片时不渲染画布/工具栏/缩放控制器
74
+
75
+ ---
76
+
77
+ ## 3. Props 配置速查
78
+
79
+ ### 3.1 核心 Props
80
+
81
+ | Prop | 类型 | 说明 |
82
+ |------|------|------|
83
+ | `imageSource` | `{ url: string, id?: string }` | 图片来源;不传则显示本地上传区域 |
84
+ | `options` | `OptionsSource` | 所有可配置项(详见下方) |
85
+ | `currentLayer` | `string` | v-model 受控图层切换 |
86
+
87
+ ### 3.2 OptionsSource 关键字段
88
+
89
+ ```ts
90
+ interface OptionsSource {
91
+ // 功能开关
92
+ enableBrush?: boolean // ✨ 是否启用笔刷(默认 true),false 时所有笔刷功能失效
93
+
94
+ // UI 开关
95
+ showToolbar?: boolean // 是否显示内置工具栏(默认 true)
96
+ showZoomController?: boolean // 是否显示内置缩放控制器(默认 true)
97
+ canvasBackground?: string // 画布背景色(默认 '#f6f6f6')
98
+
99
+ // 缩放
100
+ zoomMin?: number // 最小缩放(默认 0.2)
101
+ zoomMax?: number // 最大缩放(默认 4)
102
+
103
+ // 点标注样式(覆盖默认)
104
+ pointStyle?: Partial<PointStyle> // circleFill, circleStroke, hover*, selected*, label*, fixedSizeOnZoom 等
105
+
106
+ // 笔刷样式(覆盖默认)
107
+ brushStyle?: Partial<BrushStyle> // color, opacity, size, minSize, maxSize, continuity
108
+
109
+ // 多图层配置(不传则单图层 'default')
110
+ brushLayers?: BrushLayerConfig[] // [{label, value, color?, opacity?, size?}]
111
+
112
+ // 限制
113
+ maxPoints?: number // 最大点数
114
+ maxUndoSteps?: number // 最大撤销步数(默认 100)
115
+
116
+ // Mask 导出默认
117
+ maskExportFormat?: 'png' | 'jpeg' | 'jpg'
118
+ maskExportForeground?: 'black' | 'white'
119
+ }
120
+ ```
121
+
122
+ ### 3.3 重要类型定义
123
+
124
+ ```ts
125
+ interface PointStyle {
126
+ circleRadius, circleFill, circleStroke, circleStrokeWidth
127
+ hoverCircleFill, hoverCircleStroke
128
+ selectedCircleFill, selectedCircleStroke, selectedCircleScale
129
+ circleTextFontSize, circleTextFontFamily, circleTextFill
130
+ labelBackgroundColor, labelTextColor, labelFontSize, labelPadding
131
+ fixedSizeOnZoom, fixedSizeScale
132
+ }
133
+
134
+ interface BrushStyle {
135
+ color, opacity, size, minSize, maxSize, continuity
136
+ }
137
+
138
+ interface BrushLayerConfig {
139
+ label, value, color?, opacity?, size?
140
+ }
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 4. Events 事件速查
146
+
147
+ | 事件名 | 参数 | 触发时机 |
148
+ |--------|------|---------|
149
+ | `point-change` | `(points: PointAnnotation[])` | 点新增/删除/修改/重排 |
150
+ | `load-start` | - | 开始加载图片 |
151
+ | `load-success` | `{ url, width, height }` | 图片加载成功 |
152
+ | `load-error` | `{ error }` | 图片加载失败 |
153
+ | `undo-state-change` | `{ canUndo }` | 撤销栈状态变化 |
154
+ | `redo-state-change` | `{ canRedo }` | 重做栈状态变化 |
155
+ | `update:currentLayer` | `layerValue` | 当前笔刷图层变化(配合 v-model) |
156
+ | `layer-change` | `layerValue` | 同 update:currentLayer |
157
+
158
+ ---
159
+
160
+ ## 5. Ref API 方法速查(父组件调用)
161
+
162
+ ### 5.1 点标注
163
+ - `getPointAnnotations()` - 获取所有点
164
+ - `createPointAnnotation(x, y, label?)` - 程序化加点
165
+ - `removePointAnnotation(id)` - 程序化删点
166
+ - `updatePointAnnotationLabel(id, label)` - 修改点的 label 文案
167
+
168
+ ### 5.2 图片 & 画布
169
+ - `getImageInfo()` - `{ url, width, height }`
170
+ - `loadImage(url)` - 动态加载新图片
171
+
172
+ ### 5.3 工具切换
173
+ - `getCurrentTool()` - `'select' | 'point' | 'brush' | 'eraser'`
174
+ - `setTool(tool)` - 切换工具(enableBrush=false 时 brush/eraser 被拦截)
175
+ - `selectTool()` / `pointTool()` / `brushTool(openPanel?)` / `eraserTool()`
176
+
177
+ ### 5.4 删除 & 清空
178
+ - `deleteSelected()` - 删除选中点(带确认)
179
+ - `clearAllAnnotationsAndBrush()` - 清空所有点+笔刷(带确认)
180
+ - `clearBrush()` - 清空当前图层笔刷
181
+ - `clearAllBrushLayers()` - 清空所有图层笔刷
182
+
183
+ ### 5.5 笔刷图层
184
+ - `getCurrentLayer()` - 当前激活图层 value
185
+ - `setActiveLayer(value)` - 切换到指定图层
186
+ - `getAllLayers()` - 返回 `BrushLayerConfig[]`
187
+
188
+ ### 5.6 笔刷样式
189
+ - `getBrushStyle()` - 返回当前样式拷贝
190
+ - `updateBrushStyle({ color?, opacity?, size?, continuity? })` - 动态修改
191
+
192
+ ### 5.7 点轨迹生成笔刷区域
193
+ - `createBrushFromPoints()` - 按点顺序连成多边形填充
194
+
195
+ ### 5.8 缩放
196
+ - `zoomIn()` / `zoomOut()` / `resetZoom()`
197
+
198
+ ### 5.9 撤销 / 重做
199
+ - `undo()` / `redo()`
200
+
201
+ ### 5.10 导入 / 导出
202
+ - `exportCanvasJSON()` / `importCanvasJSON(data)` - 全量
203
+ - `exportMaskImage(format?, fg?)` - 当前图层 mask (dataURL)
204
+ - `exportMaskImageByLayer(layerValue, format?, fg?)` - 指定图层
205
+ - `exportAllMaskImages(format?, fg?)` - 所有图层(Record)
206
+ - `getMaskBlob(layerValue?, format?, fg?)` - Blob(后端上传用)
207
+ - `getMaskFile(layerValue?, filename?, format?, fg?)` - File 对象
208
+ - `getAllMaskBlobs(format?, fg?)` - 所有图层 Blob 集合
209
+ - `exportCOCO()` - COCO JSON
210
+ - `exportYOLO()` - YOLO 格式
211
+
212
+ > 参数说明:`format` = `'png' | 'jpeg' | 'jpg'`,`fg` = `'black' | 'white'`
213
+ > 注意:Mask 相关方法需浏览器环境,`enableBrush=false` 时返回 null/{ }
214
+
215
+ ---
216
+
217
+ ## 6. 项目结构
218
+
219
+ ```
220
+ leafer-point-annotation/
221
+ ├── src/
222
+ │ ├── components/
223
+ │ │ ├── PointAnnotation.vue # ✨ 核心主组件(~1900 行,整合所有能力)
224
+ │ │ ├── BrushSizeSlider.vue # 笔刷大小浮动滑块
225
+ │ │ └── BrushStylePanel.vue # 笔刷样式配置面板
226
+ │ ├── elements/
227
+ │ │ └── PointAnnotationElement.ts # 自定义点元素(Group + Ellipse + Text),内置状态切换、自动重排
228
+ │ ├── utils/
229
+ │ │ ├── CanvasBrush.ts # ✨ 笔刷底层(canvas/ctx、绘制、fillPolygon、ImageData 快照)
230
+ │ │ ├── BrushCommands.ts # BrushSnapshotCommand(笔刷 undo/redo 命令)
231
+ │ │ ├── PointCommands.ts # AddPointCommand / RemovePointCommand(点 undo/redo 命令)
232
+ │ │ ├── BrushStroke.ts # 笔画数据结构
233
+ │ │ ├── COCOExporter.ts # COCO 格式导出
234
+ │ │ └── YOLOExporter.ts # YOLO 格式导出
235
+ │ ├── types/
236
+ │ │ └── index.ts # ✨ 所有对外类型 + DEFAULT 常量
237
+ │ ├── App.vue # 开发演示页面(含所有功能的测试按钮)
238
+ │ ├── index.ts # 对外导出入口
239
+ │ └── main.ts # dev 入口
240
+ ├── project-docs/
241
+ │ ├── ARCHITECTURE.md # 架构文档(模块划分、实现要点、文件索引)
242
+ │ ├── REQUIREMENTS.md # 需求文档(功能清单、非功能需求、约束边界)
243
+ │ ├── IMPLEMENTATION_PLAN.md # 实现方案记录
244
+ │ ├── TODO.md # 待办与已完成
245
+ │ └── leafer-development-guide/ # LeaferJS 开发指南
246
+ ├── skills/
247
+ │ └── project-context.md # ✨ 本文件(新开 AI 会话时快速阅读)
248
+ ├── docs/ # GitHub Pages 演示站点(构建产物)
249
+ │ ├── index.html
250
+ │ └── assets/
251
+ ├── dist/ # npm 发布产物
252
+ │ ├── leafer-point-annotation.es.js # ESM
253
+ │ ├── leafer-point-annotation.umd.js # UMD
254
+ │ ├── leafer-point-annotation.css # 样式(使用者必须手动 import)
255
+ │ └── index.d.ts # TypeScript 类型声明
256
+ ├── vite.config.ts # 库构建配置
257
+ ├── vite.docs.config.ts # 演示站点构建配置
258
+ ├── package.json # 包信息与脚本
259
+ ├── README.md # 中文文档
260
+ └── README_EN.md # 英文文档
261
+ ```
262
+
263
+ ---
264
+
265
+ ## 7. 构建与发布流程
266
+
267
+ ### 7.1 npm scripts
268
+
269
+ | 命令 | 作用 |
270
+ |------|------|
271
+ | `pnpm dev` | 本地开发(App.vue 为演示入口) |
272
+ | `pnpm build` | 构建库产物到 `dist/`(.es.js, .umd.js, .css, .d.ts) |
273
+ | `pnpm docs:build` | 构建演示站点到 `docs/` |
274
+ | `pnpm build:all` | ✨ 同时构建库 + 演示站点(发布前必执行) |
275
+ | `pnpm preview` | 预览构建产物 |
276
+ | `pnpm tsc --noEmit` | 类型检查 |
277
+
278
+ ### 7.2 发布前检查清单
279
+ - ✅ `pnpm build:all` 无错误
280
+ - ✅ `dist/` 含完整产物(.es.js, .umd.js, .css, .d.ts)
281
+ - ✅ `docs/` 含最新演示站点
282
+ - ✅ 类型检查通过
283
+ - ✅ README.md / README_EN.md 已同步更新
284
+ - ✅ `project-docs/` 中文档已同步
285
+
286
+ ### 7.3 发布命令
287
+ ```bash
288
+ # 1. 构建
289
+ pnpm install
290
+ pnpm run build:all
291
+
292
+ # 2. 更新 package.json 的 version 字段
293
+
294
+ # 3. 发布到 npm
295
+ npm publish
296
+
297
+ # 4. 推送到 GitHub(触发 Pages 自动刷新)
298
+ git add .
299
+ git commit -m "chore: release vX.Y.Z"
300
+ git push
301
+ ```
302
+
303
+ ---
304
+
305
+ ## 8. 关键实现要点(开发时需注意)
306
+
307
+ ### 8.1 点元素(PointAnnotationElement.ts)
308
+ - 继承 LeaferJS 的 `Group`,内部含 `Ellipse`(圆点)+ `Text`(序号)
309
+ - hover/selected 状态切换由 LeaferJS 原生事件驱动
310
+ - `sequenceNumber` 字段用于显示序号,删除点后由父组件调用 `renumberSequenceNumbers()` 重排
311
+ - label 文本:不允许为空字符串
312
+ - undo/redo 时 Text.text 变化需判断是否为"自动重排变值",避免误存
313
+
314
+ ### 8.2 笔刷(CanvasBrush.ts)
315
+ - 每个图层一个实例,含独立 HTML canvas
316
+ - 绘制方式:`stroke(x, y, color)` + `eraserStroke(x, y, size)` + `fillPolygon(points, color)`
317
+ - 撤销基于操作前后的 ImageData 快照
318
+ - 透明度通过 Group.opacity 控制,避免叠加问题
319
+
320
+ ### 8.3 `enableBrush` 的影响范围
321
+ - 入口:`effectiveEnableBrush = computed(() => props.options?.enableBrush !== false)`
322
+ - UI 层:笔刷按钮、橡皮按钮、BrushStylePanel 加 `v-if="effectiveEnableBrush"`
323
+ - 方法层:所有笔刷相关方法开头加守卫
324
+ - 数据层:`initBrushLayers` 跳过 canvas 创建
325
+ - 快捷键层:`b` / `e` 按键 handler 加守卫
326
+ - 导出层:Mask/Blob/File 相关方法返回 null/{ }
327
+ - 确认文案:deleteSelected 的提示根据是否启用笔刷动态变化
328
+
329
+ ### 8.4 Props 命名约定
330
+ - **组件对外 props 用 kebab-case**(Vue 模板中):`:image-source`、`:options`、`v-model:current-layer`
331
+ - **事件名也用 kebab-case**:`@point-change`、`@load-success`、`@layer-change`
332
+ - **内部 TypeScript 用 camelCase**:`imageSource`、`currentLayer`、`pointStyle`
333
+ - `PointStyle` / `BrushStyle` 的字段名是 camelCase(circleFill, circleStroke 等)
334
+
335
+ ---
336
+
337
+ ## 9. 常见开发问题速查
338
+
339
+ ### Q1. 本地图片上传后 props.imageSource.url 变更无效?
340
+ - 组件内部 `hasLocalImage` / `localImageUrl` 标记本地上传后优先本地;若 props.url 变更在上传之后,会监听并清空本地标记重新加载。
341
+
342
+ ### Q2. 使用者报告样式没生效?
343
+ - 检查是否手动导入 CSS:`import '@zzalai/leafer-point-annotation/dist/leafer-point-annotation.css'`(CSS 不会自动注入)。
344
+
345
+ ### Q3. 我只需要点标注功能,不需要笔刷?
346
+ - `options={{ enableBrush: false }}`,所有笔刷 UI 与方法都会被屏蔽。
347
+
348
+ ### Q4. 我要给后端上传 Mask,用 dataURL 还是 Blob?
349
+ - 后端推荐 Blob/File:`getMaskBlob()` 或 `getMaskFile()` 直接用于 `formData.append('file', blob)`。
350
+
351
+ ### Q5. 父组件要自定义工具栏?
352
+ - `options={{ showToolbar: false, showZoomController: false }}`,然后通过 `ref` 调用暴露的任意方法。
353
+
354
+ ### Q6. 多图层如何切换?
355
+ - 通过 `options.brushLayers` 配置多个图层;父组件可通过 `v-model:currentLayer="'foreground'"` 或 `setActiveLayer('foreground')` 控制。
356
+
357
+ ### Q7. 笔刷撤销的性能?
358
+ - 对超大图片,单张 ImageData 快照约 4×W×H 字节,`maxUndoSteps` 不宜过大(默认 100)。
359
+
360
+ ---
361
+
362
+ ## 10. 文档索引
363
+
364
+ | 文档 | 位置 | 用途 |
365
+ |------|------|------|
366
+ | 中文 README | `README.md` | 使用者中文文档(安装、快速开始、API、示例) |
367
+ | 英文 README | `README_EN.md` | 使用者英文文档 |
368
+ | 架构文档 | `project-docs/ARCHITECTURE.md` | 模块划分、实现要点、依赖清单、文件索引 |
369
+ | 需求文档 | `project-docs/REQUIREMENTS.md` | 功能需求清单、非功能需求、约束边界、版本与发布流程 |
370
+ | 实现方案 | `project-docs/IMPLEMENTATION_PLAN.md` | 开发方案记录 |
371
+ | 待办清单 | `project-docs/TODO.md` | 待完成与已完成任务 |
372
+ | LeaferJS 指南 | `project-docs/leafer-development-guide/` | LeaferJS 开发实践指南 |
373
+ | 项目上下文(本文件) | `skills/project-context.md` | 新开 AI 会话时快速阅读 |
374
+
375
+ ---
376
+
377
+ ## 11. 关键依赖
378
+
379
+ | 依赖 | 版本范围 | 用途 |
380
+ |------|----------|------|
381
+ | `vue` | ^3.3.0 (peerDependency) | 宿主框架,不打包进产物 |
382
+ | `leafer-ui` | ^2.0.8 | Canvas 渲染引擎(App/Image/Group/Ellipse/Text) |
383
+ | `@leafer-in/editor` | ^2.0.8 | 编辑器(选择/框选) |
384
+ | `@leafer-in/viewport` | ^2.0.8 | 视口(缩放/平移) |
385
+ | `@leafer-in/resize` | ^2.0.8 | 元素 resize 手柄 |
386
+ | `@leafer-in/state` | ^2.1.0 | hover/selected 状态 |
387
+ | `@leafer-in/text-editor` | ^2.1.0 | label 文本编辑 |
388
+ | `@leafer-in/view` | ^2.0.8 | view 盒子 |
389
+ | `@leafer-in/box` | ^2.1.6 | box 布局 |
390
+ | `@zzalai/leafer-undo-redo` | 1.0.3 | 撤销/重做 CommandManager |
391
+ | `tinykeys` | ^3.0.0 | 键盘热键系统 |
392
+ | `vue-pick-colors` | ^1.8.0 | 颜色选择器组件 |
393
+
394
+ ---
395
+
396
+ ## 12. 版本信息速查
397
+
398
+ - **当前版本**: 见 `package.json` 的 `version` 字段
399
+ - **npm 包名**: `@zzalai/leafer-point-annotation`
400
+ - **发布渠道**: npm public registry
401
+ - **作者**: zzalai
402
+ - **License**: MIT