xiaodao-painter 0.1.8 → 0.1.10

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.
Files changed (3) hide show
  1. package/README.ZH.md +361 -0
  2. package/README.md +258 -85
  3. package/package.json +17 -3
package/README.ZH.md ADDED
@@ -0,0 +1,361 @@
1
+ # 小刀画板工具 (Xiaodao Painter)
2
+
3
+ **中文** | [English](./README.md) | [演示](https://painter.xdz.me)
4
+
5
+ [![Downloads](https://img.shields.io/npm/d18m/xiaodao-painter)](https://www.npmjs.com/package/xiaodao-painter)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![Vue 3](https://img.shields.io/badge/Vue-3.5+-42b883.svg)](https://vuejs.org/)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-6.0+-3178C6.svg)](https://www.typescriptlang.org/)
9
+ [![Vite](https://img.shields.io/badge/Vite-8.0+-646CFF.svg)](https://vitejs.dev/)
10
+
11
+ 一个自包含、可嵌入的 Vue 3 SVG 画板 / 绘图组件:把功能完整的画布(自由铅笔、基础图形、文本、选择与变换、平移缩放、撤销重做、内部剪贴板、颜色系统、明/暗主题、内置中英文国际化)打包进单个组件,并通过一个 `v-model` 驱动。
12
+
13
+ ![预览](./img/preview.png)
14
+
15
+ ---
16
+
17
+ ## 功能特性
18
+
19
+ ### 绘制与编辑
20
+
21
+ - **9 种工具**:选择、平移、铅笔、直线、矩形、圆、三角形、五角星、文本(另含独立的缩放工具)。
22
+ - **选择与变换**:单击选择;空白拖动框选;拖动已选图形移动;8 个手柄缩放(Shift = 对称 / 中心扩展);图层顺序(上移 / 下移)。
23
+ - **平移与缩放**:拖动平移画布;离散缩放档位(10%–1000%)以光标为锚点;触屏支持双指捏合缩放。
24
+ - **撤销 / 重做**:完整的笔画级撤销/重做栈。
25
+ - **内部剪贴板**:剪切、复制、粘贴(粘贴时偏移 +20px)。
26
+ - **颜色系统**:描边(前景)、填充(背景)、文本颜色三个色槽;可对选中图形逐个应用颜色,画布背景支持纯色或透明网格。
27
+ - **文本工具**:单击放置可编辑文本框;双击已有文本进行编辑。
28
+
29
+ ### 交互
30
+
31
+ - **指针与触屏**:统一的 `useDrawing` 交互层同时处理鼠标与触摸路径,包括双指捏合缩放、拖动绘制/选择。
32
+ - **缩放手柄**:图形 8 个、直线 2 个端点,拖动时实时预览。
33
+ - **修饰键**:`Ctrl` = 锁定 1:1 比例(闭合图形);`Shift` = 中心扩展 / 直线 45° 吸附。
34
+ - **快捷键**:Delete、Esc、Ctrl/⌘+Z / Ctrl/⌘+Y / Ctrl/⌘+X / Ctrl/⌘+C / Ctrl/� (V。
35
+ - **SVG 渲染**:每一条笔画都是真实的 SVG 元素(`<path>`、`<line>`、`<ellipse>`、`<rect>`、`<polygon>`、`foreignObject`),输出与分辨率无关、易于导出。
36
+
37
+ ### 视觉与主题
38
+
39
+ - **明 / 暗主题**:由 CSS 变量(`--wb-*`)驱动,切换主题只是换一个 class。
40
+ - **国际化**:内置中文(`zh-CN`)与英文(`en-US`),工具栏文案与缩放指示均走 `t()` 字典。
41
+ - **双向绑定**:通过 `v-model` 读写整份绘图数据。
42
+
43
+ ---
44
+
45
+ ## 安装
46
+
47
+ ```bash
48
+ # npm
49
+ npm install xiaodao-painter
50
+ # pnpm
51
+ pnpm add xiaodao-painter
52
+ # yarn
53
+ yarn add xiaodao-painter
54
+ ```
55
+
56
+ ### 对等依赖
57
+
58
+ - `vue` `^3.0.0`
59
+
60
+ 需要引入一次样式文件:
61
+
62
+ ```ts
63
+ import 'xiaodao-painter/style.css'
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 快速开始
69
+
70
+ ```bash
71
+ # 克隆仓库
72
+ git clone https://github.com/xiaodaozhi/xiaodao-painter.git
73
+ cd xiaodao-painter
74
+
75
+ # 安装依赖
76
+ npm install
77
+
78
+ # 启动开发服务器(演示页)
79
+ npm run dev
80
+ ```
81
+
82
+ 在浏览器打开 `http://localhost:5173` 查看演示应用。
83
+
84
+ 要构建独立演示页,运行 `npm run build:demo` 并打开 `dist-demo/index.html`。
85
+
86
+ ---
87
+
88
+ ## 基础用法
89
+
90
+ ```vue
91
+ <template>
92
+ <Painter
93
+ v-model="data"
94
+ theme="light"
95
+ locale="zh-CN"
96
+ />
97
+ </template>
98
+
99
+ <script setup lang="ts">
100
+ import { ref, watch } from 'vue'
101
+ import { Painter } from 'xiaodao-painter'
102
+ import type { PainterData } from 'xiaodao-painter'
103
+ import 'xiaodao-painter/style.css'
104
+
105
+ const data = ref<PainterData>({
106
+ strokes: [],
107
+ canvasWidth: 800,
108
+ canvasHeight: 600,
109
+ })
110
+
111
+ watch(data, (val) => {
112
+ console.log('绘图数据变化:', val)
113
+ }, { deep: true })
114
+ </script>
115
+ ```
116
+
117
+ ### 指定尺寸与暗色主题
118
+
119
+ ```html
120
+ <Painter
121
+ v-model="data"
122
+ theme="dark"
123
+ locale="en-US"
124
+ :width="1200"
125
+ :height="800"
126
+ />
127
+ ```
128
+
129
+ ### 自定义画布背景
130
+
131
+ ```html
132
+ <Painter
133
+ v-model="data"
134
+ :modelValue="{
135
+ strokes: [],
136
+ canvasWidth: 800,
137
+ canvasHeight: 600,
138
+ canvasBackgroundColor: '#f5f5f5',
139
+ }"
140
+ />
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Props(属性)
146
+
147
+ | 属性 | 类型 | 默认值 | 说明 |
148
+ |------|------|---------|------|
149
+ | `modelValue` (v-model) | `PainterData` | `{ strokes: [], canvasWidth: 800, canvasHeight: 600 }` | 双向绑定的绘图数据,支持 `canvasBackgroundColor`。 |
150
+ | `theme` | `'light' \| 'dark'` | `'light'` | 界面主题。 |
151
+ | `locale` | `'zh-CN' \| 'en-US'` | `'zh-CN'` | 界面语言,传入其他值回退到 `zh-CN`。 |
152
+ | `width` | `string \| number` | `'100%'` | 组件宽度(CSS 值或 px 数值)。 |
153
+ | `height` | `string \| number` | `'100%'` | 组件高度(CSS 值或 px 数值)。 |
154
+
155
+ ---
156
+
157
+ ## 数据模型
158
+
159
+ 整份绘图由一个可序列化的 `PainterData` 对象描述,便于持久化、撤销重做与协作。
160
+
161
+ ```ts
162
+ interface PainterData {
163
+ strokes: Stroke[]
164
+ canvasWidth: number
165
+ canvasHeight: number
166
+ canvasBackgroundColor?: string // 任意 CSS 颜色或 'transparent'
167
+ }
168
+
169
+ type ToolType =
170
+ | 'select' | 'pan' | 'zoom'
171
+ | 'pencil' | 'line' | 'circle' | 'rect' | 'triangle' | 'star'
172
+ | 'text'
173
+
174
+ interface Point { x: number; y: number }
175
+
176
+ interface Stroke {
177
+ id: string // crypto.randomUUID()
178
+ type: ToolType
179
+ x: number // 包围盒左上角(提交时归一化)
180
+ y: number
181
+ width: number // 包围盒宽(绘制中可能为负)
182
+ height: number
183
+ points: Point[] // 铅笔路径点;非铅笔图形为空
184
+ strokeColor: string
185
+ fillColor: string // 铅笔与直线为 'transparent' / 'none'
186
+ strokeWidth: number
187
+ // 仅文本图形
188
+ text?: string
189
+ fontSize?: number
190
+ textAlign?: 'left' | 'center' | 'right'
191
+ textColor?: string
192
+ textAutoWidth?: boolean
193
+ }
194
+ ```
195
+
196
+ 坐标统一保存在**画布(SVG 用户)坐标系**中,与缩放和平移无关。组件在提交时会把负宽高的图形归一化,保证存储的包围盒始终为正。
197
+
198
+ ### 类型导出
199
+
200
+ ```ts
201
+ import type {
202
+ PainterData, // 主数据接口
203
+ Stroke, // 单条笔画 / 图形 / 文本
204
+ Point, // { x: number; y: number }
205
+ ToolType, // 所有工具标识的联合类型
206
+ CanvasConfig, // 画布尺寸 / 背景配置
207
+ } from 'xiaodao-painter'
208
+
209
+ import {
210
+ DEFAULT_CANVAS_WIDTH,
211
+ DEFAULT_CANVAS_HEIGHT,
212
+ STROKE_WIDTH,
213
+ DEFAULT_FONT_SIZE,
214
+ HIT_THRESHOLD,
215
+ } from 'xiaodao-painter'
216
+ ```
217
+
218
+ ---
219
+
220
+ ## 架构
221
+
222
+ ```src/
223
+ ├── index.ts # 公开入口:导出 Painter 与类型/常量
224
+ ├── App.vue # 演示根组件(设置页面标题、挂载组件)
225
+ ├── style.css # 全局主题 CSS 变量(明/暗)
226
+ └── components/painter/
227
+ ├── Painter.vue # 公开组件:props、v-model 同步、主题/语言注入
228
+ ├── Whiteboard.vue # SVG 画布、指针/触摸处理、缩放 UI、文本编辑
229
+ ├── Toolbar.vue # 工具按钮、颜色指示、画布尺寸弹窗
230
+ ├── ColorPalette.vue # 前景/填充/文本色槽的颜色选择器
231
+ ├── SelectionOverlay.vue # 包围框 + 8 个缩放手柄(直线仅 2 个)
232
+ ├── StrokeRenderer.vue # 把单个 Stroke 渲染为 SVG 元素
233
+ ├── composables/
234
+ │ ├── useDrawing.ts # 交互引擎(绘制/选择/移动/缩放/平移/文本)
235
+ │ └── useI18n.ts # locale、theme、根元素的 provide/inject
236
+ ├── stores/
237
+ │ ├── canvas.ts # 中心仓库:笔画、颜色、平移缩放、撤销、剪贴板
238
+ │ └── tools.ts # 当前工具状态
239
+ ├── utils/
240
+ │ ├── geometry.ts # 命中测试、星/三角形顶点、包围盒
241
+ │ ├── svg.ts # Stroke → SVG 元素转换
242
+ │ └── i18n.ts # zh-CN / en-US 字典 + t()
243
+ └── types/index.ts # TypeScript 类型与默认常量
244
+ ```
245
+
246
+ ### 设计原则
247
+
248
+ - **自包含**:单个 Vue 3 组件,除 Vue 外零运行时依赖,无需全局注册,无需 CSS 框架。
249
+ - **状态可序列化**:整份绘图是单个普通对象(`PainterData`),便于保存、加载与同步。
250
+ - **基于 SVG 渲染**:每条笔画都是真实 SVG 节点(`<path>`、`<line>`、`<ellipse>`、`<rect>`、`<polygon>`、`foreignObject`),输出与分辨率无关、易主题化、易导出。
251
+ - **可预测的双向绑定**:父组件拥有数据,组件回显并在变更时通过 `v-model` 上报;store 内的 `syncing` 标志与组件内的 `suppressing` 标志共同避免回环。
252
+ - **轻量交互**:手动处理指针与触摸事件并显式计算坐标变换(不使用 `getScreenCTM`),规避 CSS transform 下 iOS Safari 的不一致行为。
253
+ - **纯函数工具**:命中测试、SVG 转换、i18n 都放在 `utils/` 中作为无框架依赖的纯函数,易于复用与单测。
254
+ - **组合式 API**:业务逻辑抽离到 `composables/`(`useDrawing`、`useI18n`)与轻量 store 层(`canvas.ts`、`tools.ts`),保持单一职责。
255
+
256
+ > **单实例说明**:`canvas.ts` 与 `tools.ts` 是模块级响应式单例,同一页面多个 `<Painter>` 会共享状态。若要支持多实例,应将 store 改为基于 `provide`/`inject` 的组件级 store。
257
+
258
+ ---
259
+
260
+ ## 快捷键
261
+
262
+ | 按键 | 动作 |
263
+ |------|------|
264
+ | `Delete` / `Backspace` | 删除选中图形 |
265
+ | `Escape` | 取消选择 / 取消文本编辑 |
266
+ | `Ctrl/⌘ + Z` | 撤销 |
267
+ | `Ctrl/⌘ + Y` 或 `Ctrl/⌘ + Shift + Z` | 重做 |
268
+ | `Ctrl/⌘ + X` | 剪切 |
269
+ | `Ctrl/⌘ + C` | 复制 |
270
+ | `Ctrl/⌘ + V` | 粘贴(偏移 +20px) |
271
+
272
+ 绘制或缩放过程中,`Ctrl` 锁定 1:1,`Shift` 对闭合图形为中心扩展、对直线为 45° 吸附。
273
+
274
+ ---
275
+
276
+ ## 主题与国际化
277
+
278
+ ### 内置主题
279
+
280
+ 通过 `theme` 属性提供两套主题,颜色由 `style.css` 中的 CSS 变量(`--wb-*`)驱动,自动适配明/暗。切换主题时若当前前景色为默认值,会自动在 `#000000` 与 `#ffffff` 之间翻转。
281
+
282
+ 可在任意外层元素上覆盖 CSS 变量实现深度定制:
283
+
284
+ ```html
285
+ <div style="--wb-bg: #1a1a2e; --wb-text: #e0e0e0;">
286
+ <Painter v-model="data" theme="dark" />
287
+ </div>
288
+ ```
289
+
290
+ ### 国际化
291
+
292
+ 内置 `zh-CN` 与 `en-US` 文案。通过 `locale` 切换,未知取值回退到 `zh-CN`。字典位于 `src/components/painter/utils/i18n.ts`,可方便扩展更多语言。
293
+
294
+ ---
295
+
296
+ ## 开发
297
+
298
+ ```bash
299
+ npm run dev # 启动 Vite 开发服务器(演示页)
300
+ npm run build # 类型检查(vue-tsc)并构建库产物
301
+ npm run build:demo # 构建独立演示页到 ./dist-demo
302
+ npm run preview # 预览库的生产构建
303
+ npm run lint:check # 运行 ESLint
304
+ npm run typecheck # 仅类型检查
305
+ ```
306
+
307
+ ## 构建
308
+
309
+ 库构建通过 `vite.config.ts`(lib 模式)。`npm run build` 产出:
310
+
311
+ | 文件 | 说明 |
312
+ |------|------|
313
+ | `dist/xiaodao-painter.es.js` | ES 模块(供打包工具使用) |
314
+ | `dist/xiaodao-painter.umd.js` | UMD 包(供直接 `<script>` 引入) |
315
+ | `dist/xiaodao-painter.css` | 提取的样式表 |
316
+ | `dist/types/` | TypeScript 类型声明 |
317
+
318
+ `npm run build:demo` 通过 `vite.demo.config.ts` 构建独立演示站点到 `dist-demo/`(按 `dist` 同等规则 git 忽略)。
319
+
320
+ ---
321
+
322
+ ## 路线图
323
+
324
+ ### 近期
325
+
326
+ - [x] 铅笔、图形、文本绘制与选择变换
327
+ - [x] 平移缩放、撤销重做、内部剪贴板
328
+ - [x] 颜色系统、明/暗主题、国际化
329
+ - [ ] 导出 PNG / SVG / JSON
330
+ - [ ] 图形级对齐吸附与参考线
331
+
332
+ ### 中期
333
+
334
+ - [ ] 多页面 / 多图层支持
335
+ - [ ] 图片嵌入
336
+ - [ ] 云端保存 / 加载集成
337
+
338
+ ### 长期
339
+
340
+ - [ ] 协同编辑
341
+ - [ ] 自定义图形渲染插件系统
342
+
343
+ ---
344
+
345
+ ## 技术栈
346
+
347
+ | 层 | 技术 | 版本 |
348
+ |----|------|------|
349
+ | 框架 | Vue 3(Composition API + `<script setup>`) | ^3.5 |
350
+ | 构建 | Vite | ^8.0 |
351
+ | 语言 | TypeScript(strict) | ~6.0 |
352
+ | 渲染 | SVG 2D(DOM) | - |
353
+ | 类型检查 | vue-tsc | ^3.3 |
354
+ | 包管理器 | npm / pnpm | - |
355
+ | CSS | Scoped CSS + CSS 自定义属性 | - |
356
+
357
+ ---
358
+
359
+ ## 许可证
360
+
361
+ 本项目基于 MIT 许可证发布,详见 [LICENSE](LICENSE) 文件。
package/README.md CHANGED
@@ -1,49 +1,106 @@
1
- # xiaodao-painter
1
+ # Xiaodao Painter
2
2
 
3
- A self-contained, embeddable SVG whiteboard component for Vue 3. Built with TypeScript and Vite. Zero extra dependencies beyond Vue 3.
3
+ [中文](./README.ZH.md) | **English** | [Demo](https://painter.xdz.me)
4
4
 
5
- Supports drawing tools, selection, resize, pan/zoom, undo/redo, clipboard, layer ordering, color palettes, i18n, and light/dark themes — all with two-way data binding via `v-model`.
5
+ [![Downloads](https://img.shields.io/npm/d18m/xiaodao-painter)](https://www.npmjs.com/package/xiaodao-painter)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![Vue 3](https://img.shields.io/badge/Vue-3.5+-42b883.svg)](https://vuejs.org/)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-6.0+-3178C6.svg)](https://www.typescriptlang.org/)
9
+ [![Vite](https://img.shields.io/badge/Vite-8.0+-646CFF.svg)](https://vitejs.dev/)
10
+
11
+ A self-contained, embeddable **SVG drawing component** for Vue 3: a full-featured drawing surface (freehand pencil, shape primitives, text, selection & transform, pan & zoom, undo/redo, an internal clipboard, a color system, light/dark themes, and built-in i18n) packed into a single component, driven by one `v-model`.
6
12
 
7
13
  ![Preview](./img/preview.png)
8
14
 
15
+ ---
16
+
9
17
  ## Features
10
18
 
11
- - **9 tools** — Select, Pan, Zoom, Pencil, Line, Circle, Rectangle, Triangle, Star
12
- - **Selection & manipulation** — click to select, box-select, drag-move, 8-handle resize (Shift = symmetric), layer reorder (bring to front / send to back)
13
- - **Pan & zoom** — grab-drag panning, discrete zoom steps (10% – 1000%) anchored at cursor position
14
- - **Undo / redo** — full stroke-level undo/redo stacks
15
- - **Internal clipboard** — cut, copy, paste (within the whiteboard, offset +20 px)
16
- - **Color system** — stroke (foreground) and fill (background) color slots, per-stroke color application to selections, canvas background color with transparent grid support
17
- - **Themes** — light and dark, with auto-switching foreground color on theme change
18
- - **Internationalization** — Chinese (zh-CN) and English (en-US) built in
19
- - **Keyboard shortcuts** — Delete, Escape, Ctrl+Z / Ctrl+Y / Ctrl+X / Ctrl+C / Ctrl+V
20
- - **Modifier keys** — Ctrl = constrain 1:1 ratio, Shift = center-out expansion / 45° line snap
21
- - **Two-way binding** — parent reads/writes full drawing data via `v-model`
19
+ ### Drawing & Editing
22
20
 
23
- ## Installation
21
+ - **9 tools**: Select, Pan, Pencil, Line, Rectangle, Circle, Triangle, Star, Text (plus a dedicated Zoom tool).
22
+ - **Selection & transform**: Click to select, box-select on empty space, drag to move, 8-handle resize (Shift = center-out / symmetric), layer reorder (bring to front / send to back).
23
+ - **Pan & zoom**: Grab-drag panning, discrete zoom steps (10%–1000%) anchored at the cursor; pinch zoom on touch devices.
24
+ - **Undo / redo**: Full stroke-level undo/redo stacks.
25
+ - **Internal clipboard**: Cut, copy, paste within the canvas (paste offset +20 px).
26
+ - **Color system**: Stroke (foreground), fill (background), and text-color slots; per-selection color application and canvas background (solid or transparent with grid).
27
+ - **Text tool**: Click to place an editable text box; double-click existing text to edit.
28
+
29
+ ### Interaction
30
+
31
+ - **Pointer & touch**: A single interaction layer (`useDrawing`) unifies mouse and touch paths, including pinch-to-zoom and drag-to-draw/select.
32
+ - **Resize handles**: 8 handles for shapes, 2 endpoints for lines; live preview while dragging.
33
+ - **Modifier keys**: `Ctrl` = constrain 1:1 ratio (shapes), `Shift` = center-out expansion / 45° line snap.
34
+ - **Keyboard shortcuts**: Delete, Escape, Ctrl/⌘+Z / Ctrl/⌘+Y / Ctrl/⌘+X / Ctrl/⌘+C / Ctrl/⌘+V.
35
+ - **SVG rendering**: Every stroke is a real SVG element (`<path>`, `<line>`, `<ellipse>`, `<rect>`, `<polygon>`, `foreignObject`), so the output is resolution-independent and easy to export.
36
+
37
+ ### Visual & Theming
24
38
 
25
- Install the package from npm:
39
+ - **Light / Dark themes**: Full color palette driven by CSS custom properties (`--wb-*`), switching swaps a single class.
40
+ - **Internationalization**: Chinese (`zh-CN`) and English (`en-US`) built in; toolbar labels and zoom indicator route through an `t()` dictionary.
41
+ - **Two-way binding**: Read and write the entire drawing via `v-model`.
42
+
43
+ ---
44
+
45
+ ## Installation
26
46
 
27
47
  ```bash
48
+ # npm
28
49
  npm install xiaodao-painter
50
+ # pnpm
51
+ pnpm add xiaodao-painter
52
+ # yarn
53
+ yarn add xiaodao-painter
29
54
  ```
30
55
 
31
- Or with pnpm / yarn:
56
+ ### Peer Dependencies
57
+
58
+ - `vue` `^3.0.0`
59
+
60
+ Import the stylesheet once:
61
+
62
+ ```ts
63
+ import 'xiaodao-painter/style.css'
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Quick Start
32
69
 
33
70
  ```bash
34
- pnpm add xiaodao-painter
35
- yarn add xiaodao-painter
71
+ # Clone the repository
72
+ git clone https://github.com/xiaodaozhi/xiaodao-painter.git
73
+ cd xiaodao-painter
74
+
75
+ # Install dependencies
76
+ npm install
77
+
78
+ # Start the dev server (demo page)
79
+ npm run dev
36
80
  ```
37
81
 
38
- ## Usage
82
+ Navigate to `http://localhost:5173` to see the demo application.
83
+
84
+ To build a standalone demo page, run `npm run build:demo` and open `dist-demo/index.html`.
85
+
86
+ ---
39
87
 
40
- Just import and use the `Painter` component. No additional plugins or global setup required — only Vue 3 is needed:
88
+ ## Basic Usage
41
89
 
42
90
  ```vue
91
+ <template>
92
+ <Painter
93
+ v-model="data"
94
+ theme="light"
95
+ locale="zh-CN"
96
+ />
97
+ </template>
98
+
43
99
  <script setup lang="ts">
44
100
  import { ref, watch } from 'vue'
45
101
  import { Painter } from 'xiaodao-painter'
46
102
  import type { PainterData } from 'xiaodao-painter'
103
+ import 'xiaodao-painter/style.css'
47
104
 
48
105
  const data = ref<PainterData>({
49
106
  strokes: [],
@@ -52,32 +109,24 @@ const data = ref<PainterData>({
52
109
  })
53
110
 
54
111
  watch(data, (val) => {
55
- console.log('Drawing data changed:', val)
112
+ console.log('Drawing changed:', val)
56
113
  }, { deep: true })
57
114
  </script>
58
-
59
- <template>
60
- <Painter
61
- v-model="data"
62
- theme="light"
63
- locale="zh-CN"
64
- />
65
- </template>
66
115
  ```
67
116
 
68
- With explicit dimensions and dark theme:
117
+ ### With explicit dimensions and dark theme
69
118
 
70
119
  ```html
71
120
  <Painter
72
121
  v-model="data"
73
122
  theme="dark"
74
123
  locale="en-US"
75
- width="1200"
76
- height="800"
124
+ :width="1200"
125
+ :height="800"
77
126
  />
78
127
  ```
79
128
 
80
- With a custom canvas background color:
129
+ ### With a custom canvas background
81
130
 
82
131
  ```html
83
132
  <Painter
@@ -91,44 +140,51 @@ With a custom canvas background color:
91
140
  />
92
141
  ```
93
142
 
143
+ ---
144
+
94
145
  ## Props
95
146
 
96
147
  | Prop | Type | Default | Description |
97
- | --- | --- | --- | --- |
98
- | `modelValue` | `PainterData` | `{ strokes: [], canvasWidth: 800, canvasHeight: 600 }` | Two-way bound drawing data (supports `canvasBackgroundColor`) |
99
- | `theme` | `'light' \| 'dark'` | `'light'` | UI theme |
100
- | `locale` | `string` | `'zh-CN'` | UI language (`'zh-CN'` or `'en-US'`) |
101
- | `width` | `string \| number` | `'100%'` | Component width (CSS value or px number) |
102
- | `height` | `string \| number` | `'100%'` | Component height (CSS value or px number) |
148
+ |------|------|---------|-------------|
149
+ | `modelValue` (v-model) | `PainterData` | `{ strokes: [], canvasWidth: 800, canvasHeight: 600 }` | Two-way bound drawing data. Supports `canvasBackgroundColor`. |
150
+ | `theme` | `'light' \| 'dark'` | `'light'` | UI theme. |
151
+ | `locale` | `'zh-CN' \| 'en-US'` | `'zh-CN'` | UI language. Any other value falls back to `zh-CN`. |
152
+ | `width` | `string \| number` | `'100%'` | Component width (CSS value or px number). |
153
+ | `height` | `string \| number` | `'100%'` | Component height (CSS value or px number). |
154
+
155
+ ---
103
156
 
104
157
  ## Data Model
105
158
 
159
+ The whole drawing is described by a single serializable `PainterData` object, which makes persistence, undo/redo, and collaboration straightforward.
160
+
106
161
  ```ts
107
162
  interface PainterData {
108
163
  strokes: Stroke[]
109
164
  canvasWidth: number
110
165
  canvasHeight: number
111
- canvasBackgroundColor?: string
166
+ canvasBackgroundColor?: string // any CSS color or 'transparent'
112
167
  }
113
168
 
114
- type ToolType = 'select' | 'pencil' | 'line' | 'circle' | 'rect' | 'triangle' | 'star' | 'text' | 'pan' | 'zoom'
169
+ type ToolType =
170
+ | 'select' | 'pan' | 'zoom'
171
+ | 'pencil' | 'line' | 'circle' | 'rect' | 'triangle' | 'star'
172
+ | 'text'
115
173
 
116
- interface Point {
117
- x: number
118
- y: number
119
- }
174
+ interface Point { x: number; y: number }
120
175
 
121
176
  interface Stroke {
122
- id: string
177
+ id: string // crypto.randomUUID()
123
178
  type: ToolType
124
- x: number
179
+ x: number // bounding-box top-left (normalized on commit)
125
180
  y: number
126
- width: number
181
+ width: number // bounding-box width (may be negative while drawing)
127
182
  height: number
128
- points: Point[]
183
+ points: Point[] // pencil path points; empty for non-pencil
129
184
  strokeColor: string
130
- fillColor: string
185
+ fillColor: string // 'transparent' / 'none' for pencil & line
131
186
  strokeWidth: number
187
+ // text-only fields
132
188
  text?: string
133
189
  fontSize?: number
134
190
  textAlign?: 'left' | 'center' | 'right'
@@ -137,52 +193,169 @@ interface Stroke {
137
193
  }
138
194
  ```
139
195
 
196
+ Coordinates are stored in **canvas (SVG user) coordinate space**, independent of zoom and pan. The component normalizes negative-width/height shapes on commit so the stored bounding box is always positive.
197
+
198
+ ### Type Exports
199
+
200
+ ```ts
201
+ import type {
202
+ PainterData, // main data interface
203
+ Stroke, // a single shape / text / pencil entry
204
+ Point, // { x: number; y: number }
205
+ ToolType, // union of all tool identifiers
206
+ CanvasConfig, // canvas size / background config
207
+ } from 'xiaodao-painter'
208
+
209
+ import {
210
+ DEFAULT_CANVAS_WIDTH,
211
+ DEFAULT_CANVAS_HEIGHT,
212
+ STROKE_WIDTH,
213
+ DEFAULT_FONT_SIZE,
214
+ HIT_THRESHOLD,
215
+ } from 'xiaodao-painter'
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Architecture
221
+
222
+ ```src/
223
+ ├── index.ts # Public entry: exports Painter + types/constants
224
+ ├── App.vue # Demo root (sets page title, mounts the component)
225
+ ├── style.css # Global theme CSS variables (light/dark)
226
+ └── components/painter/
227
+ ├── Painter.vue # Public component: props, v-model sync, theme/locale provide
228
+ ├── Whiteboard.vue # SVG canvas, pointer/touch handling, zoom UI, text editing
229
+ ├── Toolbar.vue # Tool buttons, color indicators, canvas-size popover
230
+ ├── ColorPalette.vue # Color picker for foreground/fill/text slots
231
+ ├── SelectionOverlay.vue # Bounding box + 8 resize handles (line gets 2 handles)
232
+ ├── StrokeRenderer.vue # Renders one Stroke as an SVG element
233
+ ├── composables/
234
+ │ ├── useDrawing.ts # Interaction engine (draw/select/move/resize/pan/zoom/text)
235
+ │ └── useI18n.ts # provide/inject for locale, theme, root element
236
+ ├── stores/
237
+ │ ├── canvas.ts # Central store: strokes, colors, pan/zoom, undo, clipboard
238
+ │ └── tools.ts # Active tool state
239
+ ├── utils/
240
+ │ ├── geometry.ts # Hit testing, star/triangle vertices, bounding box
241
+ │ ├── svg.ts # Stroke → SVG element conversion
242
+ │ └── i18n.ts # zh-CN / en-US dictionaries + t()
243
+ └── types/index.ts # TypeScript types and default constants
244
+ ```
245
+
246
+ ### Design Principles
247
+
248
+ - **Self-contained**: A single Vue 3 component with zero runtime dependencies beyond Vue; no global registration, no CSS framework.
249
+ - **Serializable state**: The whole drawing is one plain `PainterData` object, so it can be saved, loaded, and synced trivially.
250
+ - **SVG-based rendering**: Every stroke is a real SVG node (`<path>`, `<line>`, `<ellipse>`, `<rect>`, `<polygon>`, `foreignObject`). Output is resolution-independent, easily themeable, and export-friendly.
251
+ - **Predictable two-way binding**: The parent owns the data; the component reflects it and emits changes back through `v-model`. A `syncing` flag in the store plus a `suppressing` flag in the component prevents feedback loops.
252
+ - **Framework-light interaction**: Pointer + touch are handled manually with an explicit coordinate transform (not `getScreenCTM`), avoiding unreliable behavior under CSS transforms on iOS Safari.
253
+ - **Pure helpers**: Hit-testing, SVG conversion, and i18n live in `utils/` as framework-free functions, easy to reuse or unit-test.
254
+ - **Composition API**: Business logic is extracted into `composables/` (`useDrawing`, `useI18n`) and a small store layer (`canvas.ts`, `tools.ts`), keeping single-responsibility modules.
255
+
256
+ > **Single-instance note:** `canvas.ts` and `tools.ts` are module-level reactive singletons. Multiple `<Painter>` components on one page currently share state. To support multiple instances, promote the stores to a `provide`/`inject` scoped store per component.
257
+
258
+ ---
259
+
140
260
  ## Keyboard Shortcuts
141
261
 
142
262
  | Key | Action |
143
- | --- | --- |
263
+ |-----|--------|
144
264
  | `Delete` / `Backspace` | Delete selected strokes |
145
- | `Escape` | Clear selection |
146
- | `Ctrl + Z` | Undo |
147
- | `Ctrl + Y` / `Ctrl + Shift + Z` | Redo |
148
- | `Ctrl + X` | Cut |
149
- | `Ctrl + C` | Copy |
150
- | `Ctrl + V` | Paste (offset +20 px) |
265
+ | `Escape` | Clear selection / cancel text edit |
266
+ | `Ctrl/⌘ + Z` | Undo |
267
+ | `Ctrl/⌘ + Y` or `Ctrl/⌘ + Shift + Z` | Redo |
268
+ | `Ctrl/⌘ + X` | Cut |
269
+ | `Ctrl/⌘ + C` | Copy |
270
+ | `Ctrl/⌘ + V` | Paste (offset +20 px) |
151
271
 
152
- ## Project Structure
272
+ While drawing or resizing, `Ctrl` constrains to 1:1, `Shift` gives center-out expansion (for closed shapes) or 45° line snapping (for lines).
153
273
 
274
+ ---
275
+
276
+ ## Theming & Internationalization
277
+
278
+ ### Built-in Themes
279
+
280
+ The component ships two themes via the `theme` prop. Colors are driven by CSS custom properties defined in `style.css` (`--wb-*`), so the component adapts to light/dark automatically. Switching theme also flips the default foreground color (`#000000` ⇄ `#ffffff`) when the current color is the theme default.
281
+
282
+ Override CSS variables on any wrapper element for full customization:
283
+
284
+ ```html
285
+ <div style="--wb-bg: #1a1a2e; --wb-text: #e0e0e0;">
286
+ <Painter v-model="data" theme="dark" />
287
+ </div>
154
288
  ```
155
- src/
156
- components/painter/
157
- Painter.vue # Public entry (props, v-model, theme sync)
158
- Whiteboard.vue # SVG canvas, keyboard shortcuts, pan/zoom centering
159
- Toolbar.vue # Left toolbar (tools, canvas settings, color indicators)
160
- ColorPalette.vue # Color picker popover
161
- SelectionOverlay.vue # Selection bounding box + resize handles
162
- StrokeRenderer.vue # Renders a single stroke as SVG
163
- composables/
164
- useDrawing.ts # Mouse interaction engine (draw, select, move, resize)
165
- useI18n.ts # Locale and theme provide/inject
166
- stores/
167
- canvas.ts # Main store (strokes, colors, pan/zoom, undo, clipboard)
168
- tools.ts # Active tool state
169
- utils/
170
- geometry.ts # Hit testing, star/triangle vertices, bounding box
171
- svg.ts # Stroke-to-SVG element conversion
172
- i18n.ts # zh-CN / en-US dictionaries
173
- types/
174
- index.ts # TypeScript types and default constants
175
- style.css # CSS custom properties (themes)
176
- ```
289
+
290
+ ### Internationalization
291
+
292
+ UI text is provided for `zh-CN` and `en-US`. Pass `locale` to switch; unknown values fall back to `zh-CN`. The dictionary lives in `src/components/painter/utils/i18n.ts` and can be extended with additional locales.
293
+
294
+ ---
177
295
 
178
296
  ## Development
179
297
 
180
298
  ```bash
181
- npm run dev # Start dev server
182
- npm run build # Type-check and build for production
183
- npm run preview # Preview production build
299
+ npm run dev # Start the Vite dev server (demo page)
300
+ npm run build # Type-check (vue-tsc) and build the library bundle
301
+ npm run build:demo # Build the standalone demo page into ./dist-demo
302
+ npm run preview # Preview the library production build
303
+ npm run lint:check # Run ESLint
304
+ npm run typecheck # Type-check only
184
305
  ```
185
306
 
307
+ ## Building
308
+
309
+ The library build uses `vite.config.ts` (lib mode). `npm run build` produces:
310
+
311
+ | File | Description |
312
+ |------|-------------|
313
+ | `dist/xiaodao-painter.es.js` | ES module (for bundlers) |
314
+ | `dist/xiaodao-painter.umd.js` | UMD bundle (for direct `<script>` usage) |
315
+ | `dist/xiaodao-painter.css` | Extracted stylesheet |
316
+ | `dist/types/` | TypeScript declaration files |
317
+
318
+ `npm run build:demo` builds a standalone demo site via `vite.demo.config.ts` into `dist-demo/` (git-ignored, same as `dist`).
319
+
320
+ ---
321
+
322
+ ## Roadmap
323
+
324
+ ### Near-term
325
+
326
+ - [x] Pencil, shape, text drawing with selection & transform
327
+ - [x] Pan & zoom, undo/redo, internal clipboard
328
+ - [x] Color system, light/dark themes, i18n
329
+ - [ ] Export to PNG / SVG / JSON
330
+ - [ ] Shape-level snapping & alignment guides
331
+
332
+ ### Mid-term
333
+
334
+ - [ ] Multi-page / multi-layer support
335
+ - [ ] Image embedding
336
+ - [ ] Cloud save / load integration
337
+
338
+ ### Long-term
339
+
340
+ - [ ] Collaborative editing
341
+ - [ ] Plugin system for custom shape renderers
342
+
343
+ ---
344
+
345
+ ## Tech Stack
346
+
347
+ | Layer | Technology | Version |
348
+ |-------|-----------|---------|
349
+ | Framework | Vue 3 (Composition API + `<script setup>`) | ^3.5 |
350
+ | Build | Vite | ^8.0 |
351
+ | Language | TypeScript (strict) | ~6.0 |
352
+ | Rendering | SVG 2D (DOM) | - |
353
+ | Type Checker | vue-tsc | ^3.3 |
354
+ | Package Manager | npm / pnpm | - |
355
+ | CSS | Scoped CSS + CSS Custom Properties | - |
356
+
357
+ ---
358
+
186
359
  ## License
187
360
 
188
- MIT
361
+ This project is licensed under the MIT License: see the [LICENSE](LICENSE) file for details.
package/package.json CHANGED
@@ -1,15 +1,29 @@
1
1
  {
2
2
  "name": "xiaodao-painter",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A Vue 3 component for drawing on the canvas",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "homepage": "https://xiaodaozhi.com/xiaodao-painter.html",
8
8
  "keywords": [
9
9
  "vue",
10
+ "vue3",
11
+ "vue-component",
12
+ "whiteboard",
10
13
  "canvas",
11
- "drawing",
12
- "painter"
14
+ "draw",
15
+ "drawing-board",
16
+ "paint",
17
+ "sketch",
18
+ "annotation",
19
+ "手写板",
20
+ "白板",
21
+ "画布",
22
+ "画板",
23
+ "绘图",
24
+ "手绘",
25
+ "批注",
26
+ "电子白板"
13
27
  ],
14
28
  "files": [
15
29
  "dist"