@machete-jhun/canvas-studio 0.0.1 → 0.0.6
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/README.md +236 -75
- package/dist/constants-CHIlz4uF.js +1 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +25 -25
- package/dist/utils/index.js +1 -0
- package/dist/utils/shapeTools.js +1 -0
- package/dist/utils-DrbTlu3h.js +1 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -1,140 +1,201 @@
|
|
|
1
1
|
# CanvasStudio 🎨
|
|
2
2
|
|
|
3
|
-
基于 React + Konva
|
|
3
|
+
基于 React + Konva 的专业级 Canvas 画布编辑器组件库,支持丰富的图形编辑能力。
|
|
4
4
|
|
|
5
5
|
## 特性
|
|
6
6
|
|
|
7
|
-
- 🎨
|
|
8
|
-
- 🖱️
|
|
9
|
-
- ⌨️
|
|
10
|
-
- 📐
|
|
11
|
-
- 🔲
|
|
12
|
-
- 📏
|
|
13
|
-
- 💪
|
|
14
|
-
-
|
|
7
|
+
- 🎨 **丰富的图形支持**:矩形、圆形、文本、图片、SVG、二维码、条形码、视频、Rive 动画、屏幕背景
|
|
8
|
+
- 🖱️ **完整的编辑能力**:拖拽、缩放、旋转、多选、组选等
|
|
9
|
+
- ⌨️ **键盘快捷键**:支持常用快捷键(复制、粘贴、删除、撤销等)
|
|
10
|
+
- 📐 **智能对齐**:辅助线与吸附功能,支持网格对齐
|
|
11
|
+
- 🔲 **灵活的背景**:棋盘格、点阵、纯色背景可选
|
|
12
|
+
- 📏 **标尺与刻度**:像素/毫米刻度可配置
|
|
13
|
+
- 💪 **完整的 TypeScript 支持**:完善的类型定义
|
|
14
|
+
- 📦 **轻量级**:仅 ~53KB(gzip ~17KB)
|
|
15
15
|
|
|
16
16
|
## 安装
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
npm install canvas-studio
|
|
20
|
-
# 或
|
|
21
|
-
pnpm add canvas-studio
|
|
22
|
-
```
|
|
18
|
+
### 使用 npm/pnpm/yarn
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
```bash
|
|
21
|
+
# 使用 pnpm(推荐)
|
|
22
|
+
pnpm add @machete-jhun/canvas-studio
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
# 或使用 npm
|
|
25
|
+
npm install @machete-jhun/canvas-studio
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
# 或使用 yarn
|
|
28
|
+
yarn add @machete-jhun/canvas-studio
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
|
|
32
|
+
## 快速开始
|
|
33
33
|
|
|
34
34
|
```tsx
|
|
35
|
-
import { CanvasStudio, type CanvasStudioRef } from 'canvas-studio'
|
|
35
|
+
import { CanvasStudio, type CanvasStudioRef } from '@machete-jhun/canvas-studio'
|
|
36
36
|
import { useRef } from 'react'
|
|
37
37
|
|
|
38
|
-
function App() {
|
|
38
|
+
export function App() {
|
|
39
39
|
const canvasRef = useRef<CanvasStudioRef>(null)
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<CanvasStudio
|
|
43
43
|
ref={canvasRef}
|
|
44
|
-
width={
|
|
45
|
-
height={
|
|
44
|
+
width={1200}
|
|
45
|
+
height={800}
|
|
46
46
|
backgroundType="grid"
|
|
47
47
|
/>
|
|
48
48
|
)
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
## Props
|
|
52
|
+
## 组件 Props
|
|
53
53
|
|
|
54
54
|
| 属性 | 类型 | 默认值 | 说明 |
|
|
55
55
|
|------|------|--------|------|
|
|
56
|
-
| `width` | `number` | `800` |
|
|
57
|
-
| `height` | `number` | `600` |
|
|
58
|
-
| `shapeList` | `MyShapeConfig[]` | `[]` |
|
|
59
|
-
| `bgShapeList` | `MyShapeConfig[]` | `[]` |
|
|
56
|
+
| `width` | `number` | `800` | 画布宽度(像素) |
|
|
57
|
+
| `height` | `number` | `600` | 画布高度(像素) |
|
|
58
|
+
| `shapeList` | `MyShapeConfig[]` | `[]` | 编辑图形列表 |
|
|
59
|
+
| `bgShapeList` | `MyShapeConfig[]` | `[]` | 背景图形列表(锁定,不可编辑) |
|
|
60
60
|
| `backgroundType` | `'grid' \| 'point' \| 'none'` | `'grid'` | 背景类型 |
|
|
61
61
|
| `hideRuler` | `boolean` | `false` | 是否隐藏标尺 |
|
|
62
|
-
| `ppmm` | `number` | `1` |
|
|
63
|
-
| `className` | `string` | `''` |
|
|
64
|
-
| `onShapeListChange` | `(shapes: MyShapeConfig[]) => void` | - |
|
|
65
|
-
| `onSelectedShapeIdsChange` | `(ids: string[]) => void` | - |
|
|
62
|
+
| `ppmm` | `number` | `1` | 每毫米像素数(用于精确尺寸设置) |
|
|
63
|
+
| `className` | `string` | `''` | 自定义容器类名 |
|
|
64
|
+
| `onShapeListChange` | `(shapes: MyShapeConfig[]) => void` | - | 图形列表变化回调 |
|
|
65
|
+
| `onSelectedShapeIdsChange` | `(ids: string[]) => void` | - | 选中图形变化回调 |
|
|
66
|
+
| `onBgShapeListChange` | `(shapes: MyShapeConfig[]) => void` | - | 背景图形变化回调 |
|
|
67
|
+
|
|
68
|
+
## Ref API
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
通过 `useRef` 访问画布的命令式 API:
|
|
68
71
|
|
|
69
72
|
```tsx
|
|
70
73
|
const canvasRef = useRef<CanvasStudioRef>(null)
|
|
71
74
|
|
|
72
|
-
//
|
|
73
|
-
canvasRef.current?.getStage()
|
|
75
|
+
// 获取原生 Konva Stage 实例
|
|
76
|
+
const stage = canvasRef.current?.getStage()
|
|
74
77
|
|
|
75
|
-
//
|
|
76
|
-
canvasRef.current?.getSelectedShapeIds()
|
|
77
|
-
canvasRef.current?.setSelectedShapeIds(['id1'
|
|
78
|
+
// 管理选中的图形
|
|
79
|
+
canvasRef.current?.getSelectedShapeIds() // 获取选中图形 ID
|
|
80
|
+
canvasRef.current?.setSelectedShapeIds(['id1']) // 设置选中图形
|
|
78
81
|
|
|
79
|
-
//
|
|
80
|
-
canvasRef.current?.addShapes([
|
|
81
|
-
canvasRef.current?.removeShapes(['id1'])
|
|
82
|
-
canvasRef.current?.updateShape(shape)
|
|
83
|
-
canvasRef.current?.getShapeList()
|
|
82
|
+
// 管理图形
|
|
83
|
+
canvasRef.current?.addShapes([shape1, shape2]) // 添加图形
|
|
84
|
+
canvasRef.current?.removeShapes(['id1', 'id2']) // 删除图形
|
|
85
|
+
canvasRef.current?.updateShape(shape) // 更新单个图形
|
|
86
|
+
canvasRef.current?.getShapeList() // 获取所有图形列表
|
|
84
87
|
|
|
85
|
-
//
|
|
86
|
-
canvasRef.current?.fitToContent()
|
|
88
|
+
// 视图操作
|
|
89
|
+
canvasRef.current?.fitToContent() // 自适应缩放显示所有内容
|
|
87
90
|
```
|
|
88
91
|
|
|
89
|
-
##
|
|
92
|
+
## 创建和操作图形
|
|
93
|
+
|
|
94
|
+
导入工具函数快速创建各类图形:
|
|
90
95
|
|
|
91
96
|
```tsx
|
|
92
97
|
import {
|
|
93
98
|
createRect,
|
|
94
|
-
createEllipse,
|
|
95
99
|
createText,
|
|
96
100
|
createImage,
|
|
97
101
|
createSvg,
|
|
98
102
|
createQrCode,
|
|
99
103
|
createBarCode,
|
|
104
|
+
createRive,
|
|
105
|
+
createVideo,
|
|
106
|
+
createBackScreen,
|
|
100
107
|
copyShape,
|
|
101
|
-
|
|
108
|
+
type MyShapeConfig,
|
|
109
|
+
} from '@machete-jhun/canvas-studio/utils'
|
|
102
110
|
|
|
103
111
|
// 创建矩形
|
|
104
|
-
const rect = createRect(100, 100, 200, 150, '#
|
|
112
|
+
const rect = createRect(100, 100, 200, 150, '#3498db')
|
|
105
113
|
|
|
106
114
|
// 创建文本
|
|
107
|
-
const text = createText(
|
|
115
|
+
const text = createText(
|
|
116
|
+
'Hello Canvas', // 文本内容
|
|
117
|
+
24, // 字体大小
|
|
118
|
+
'Arial', // 字体族
|
|
119
|
+
'font-id', // 字体 ID
|
|
120
|
+
'https://...', // 字体 URL
|
|
121
|
+
50, // x 坐标
|
|
122
|
+
50 // y 坐标
|
|
123
|
+
)
|
|
108
124
|
|
|
109
125
|
// 创建图片
|
|
110
|
-
const image = createImage(
|
|
126
|
+
const image = createImage(
|
|
127
|
+
'https://example.com/image.png', // 图片 URL
|
|
128
|
+
'image.png', // 图片名
|
|
129
|
+
'图片描述', // 显示名
|
|
130
|
+
'', // ID(可选)
|
|
131
|
+
100, // x 坐标
|
|
132
|
+
100, // y 坐标
|
|
133
|
+
200, // 宽度
|
|
134
|
+
200 // 高度
|
|
135
|
+
)
|
|
111
136
|
|
|
112
137
|
// 创建二维码
|
|
113
|
-
const qrcode = createQrCode(
|
|
138
|
+
const qrcode = createQrCode(
|
|
139
|
+
'https://example.com', // 二维码内容
|
|
140
|
+
100, // x 坐标
|
|
141
|
+
100, // y 坐标
|
|
142
|
+
150 // 边长
|
|
143
|
+
)
|
|
114
144
|
|
|
115
145
|
// 创建条形码
|
|
116
|
-
const barcode = createBarCode(
|
|
117
|
-
|
|
118
|
-
//
|
|
119
|
-
|
|
146
|
+
const barcode = createBarCode(
|
|
147
|
+
'1234567890', // 条形码内容
|
|
148
|
+
100, // x 坐标
|
|
149
|
+
100, // y 坐标
|
|
150
|
+
200, // 宽度
|
|
151
|
+
100 // 高度
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
// 创建 Rive 动画
|
|
155
|
+
const rive = createRive(
|
|
156
|
+
'https://example.com/animation.riv', // Rive 文件 URL
|
|
157
|
+
'animation', // 名称
|
|
158
|
+
'动画描述', // 显示名
|
|
159
|
+
100, // x 坐标
|
|
160
|
+
100, // y 坐标
|
|
161
|
+
400, // 宽度
|
|
162
|
+
300, // 高度
|
|
163
|
+
'rive-id', // ID
|
|
164
|
+
[] // 状态机输入配置
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
// 创建视频
|
|
168
|
+
const video = createVideo(
|
|
169
|
+
'https://example.com/video.mp4', // 视频 URL
|
|
170
|
+
'我的视频', // 显示名
|
|
171
|
+
100, // x 坐标
|
|
172
|
+
100, // y 坐标
|
|
173
|
+
1920, // 宽度
|
|
174
|
+
1080, // 高度
|
|
175
|
+
1.0, // 播放速度
|
|
176
|
+
true // 是否循环
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
// 创建屏幕背景(展示用)
|
|
180
|
+
const screen = createBackScreen(
|
|
181
|
+
'SCREEN_001', // 屏幕 ID
|
|
182
|
+
'#ffffff', // 背景色
|
|
183
|
+
100, // x 坐标
|
|
184
|
+
100, // y 坐标
|
|
185
|
+
400, // 宽度
|
|
186
|
+
600 // 高度
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
// 复制图形(会生成新 ID)
|
|
190
|
+
const copiedRect = copyShape(rect)
|
|
120
191
|
|
|
121
192
|
// 添加到画布
|
|
122
|
-
canvasRef.current?.addShapes([rect, text, image])
|
|
193
|
+
canvasRef.current?.addShapes([rect, text, image, qrcode])
|
|
123
194
|
```
|
|
124
195
|
|
|
125
|
-
##
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
import {
|
|
129
|
-
STAGE_CONTAINER_ID,
|
|
130
|
-
CUSTOMER_SHAPE_NAME,
|
|
131
|
-
BACKGROUND_SHAPE_NAME,
|
|
132
|
-
SHAPE_LAYER_NAME,
|
|
133
|
-
// ...
|
|
134
|
-
} from 'canvas-studio'
|
|
135
|
-
```
|
|
196
|
+
## 工具函数
|
|
136
197
|
|
|
137
|
-
|
|
198
|
+
导入并使用各种工具函数:
|
|
138
199
|
|
|
139
200
|
```tsx
|
|
140
201
|
import {
|
|
@@ -143,15 +204,115 @@ import {
|
|
|
143
204
|
getPointerPosition,
|
|
144
205
|
getStageCenter,
|
|
145
206
|
getStageViewPortCenter,
|
|
146
|
-
|
|
207
|
+
assertNever,
|
|
208
|
+
} from '@machete-jhun/canvas-studio/utils'
|
|
209
|
+
|
|
210
|
+
// 检查是否 macOS
|
|
211
|
+
if (isMac()) {
|
|
212
|
+
// 使用 Cmd 键
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 获取互补色
|
|
216
|
+
const complementColor = complementaryHexColor('#ff0000') // '#00ffff'
|
|
217
|
+
|
|
218
|
+
// 获取鼠标在 Stage 中的位置
|
|
219
|
+
const position = getPointerPosition(konvaEvent)
|
|
220
|
+
|
|
221
|
+
// 获取 Stage 中心坐标
|
|
222
|
+
const center = getStageCenter(stage)
|
|
223
|
+
|
|
224
|
+
// 获取视口中心坐标
|
|
225
|
+
const vpCenter = getStageViewPortCenter({ stage, offsetWidth: 0 })
|
|
147
226
|
```
|
|
148
227
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
228
|
+
## 导出的常量
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { SHAPE_TYPES, THEME_COLOR } from '@machete-jhun/canvas-studio'
|
|
232
|
+
|
|
233
|
+
// 图形类型
|
|
234
|
+
console.log(SHAPE_TYPES.TEXT) // 'text'
|
|
235
|
+
console.log(SHAPE_TYPES.RECT) // 'rect'
|
|
236
|
+
console.log(SHAPE_TYPES.IMAGE) // 'image'
|
|
237
|
+
console.log(SHAPE_TYPES.SVG) // 'svg'
|
|
238
|
+
console.log(SHAPE_TYPES.QR_CODE) // 'qr_code'
|
|
239
|
+
console.log(SHAPE_TYPES.BAR_CODE) // 'bar_code'
|
|
240
|
+
console.log(SHAPE_TYPES.RIVE) // 'rive'
|
|
241
|
+
console.log(SHAPE_TYPES.VIDEO) // 'video'
|
|
242
|
+
console.log(SHAPE_TYPES.BACK_SCREEN) // 'back_screen'
|
|
243
|
+
|
|
244
|
+
// 主题色
|
|
245
|
+
console.log(THEME_COLOR.active) // '#1677ff'
|
|
246
|
+
console.log(THEME_COLOR.disable) // '#666'
|
|
247
|
+
console.log(THEME_COLOR.default) // '#ccc'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## 导出路径
|
|
251
|
+
|
|
252
|
+
本库支持两种导出方式:
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
// 方式 1:导入主组件和类型
|
|
256
|
+
import {
|
|
257
|
+
CanvasStudio,
|
|
258
|
+
type CanvasStudioProps,
|
|
259
|
+
type CanvasStudioRef,
|
|
260
|
+
SHAPE_TYPES,
|
|
261
|
+
} from '@machete-jhun/canvas-studio'
|
|
262
|
+
|
|
263
|
+
// 方式 2:导入工具函数(推荐单独导入)
|
|
264
|
+
import {
|
|
265
|
+
createRect,
|
|
266
|
+
createText,
|
|
267
|
+
createImage,
|
|
268
|
+
// ...其他工具
|
|
269
|
+
} from '@machete-jhun/canvas-studio/utils'
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## 高级用法
|
|
273
|
+
|
|
274
|
+
### 自定义背景图层
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
<CanvasStudio
|
|
278
|
+
shapeList={editableShapes}
|
|
279
|
+
bgShapeList={backgroundShapes}
|
|
280
|
+
onShapeListChange={handleEditableChange}
|
|
281
|
+
onBgShapeListChange={handleBgChange}
|
|
282
|
+
/>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 精确尺寸控制
|
|
286
|
+
|
|
287
|
+
使用 `ppmm`(每毫米像素数)实现精确的物理尺寸:
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
<CanvasStudio
|
|
291
|
+
width={210} // 210mm 宽(A4 纸)
|
|
292
|
+
height={297} // 297mm 高(A4 纸)
|
|
293
|
+
ppmm={3.78} // 1mm = 3.78px (96DPI)
|
|
294
|
+
/>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## 常见问题
|
|
298
|
+
|
|
299
|
+
### 如何处理视频透明背景?
|
|
300
|
+
|
|
301
|
+
可以使用 FFmpeg 将视频转换为支持透明通道的格式:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# 扣绿幕效果
|
|
305
|
+
ffmpeg -i input.mp4 -vf "chromakey=0x00FF00:0.15:0.1" \
|
|
306
|
+
-c:v libvpx-vp9 -pix_fmt yuva420p -b:v 5M output.webm
|
|
307
|
+
|
|
308
|
+
# MOV 转 WebM(支持透明)
|
|
309
|
+
ffmpeg -i input.mov -c:v libvpx-vp9 -pix_fmt yuva420p output.webm
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## 许可证
|
|
154
313
|
|
|
155
314
|
MIT
|
|
156
315
|
|
|
316
|
+
## 贡献
|
|
317
|
+
|
|
157
318
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const c="c0",a="c1",s="c2",e="c3",E="c4",t="c5",d="c6",r="c7",_="c8",i="c9",o="ca",C="cb",R="cc",b="cd",f="ce",v={IMAGE:"image",RECT:"rect",TEXT:"text",SVG:"svg",QR_CODE:"qr_code",BAR_CODE:"bar_code",RIVE:"rive",VIDEO:"video",BACK_SCREEN:"back_screen"},g={active:"#1677ff",disable:"#666",default:"#ccc"};export{g as _,s as a,E as c,b as d,d as f,c as g,R as h,r as i,C as l,v as m,o as n,a as o,t as p,i as r,_ as s,e as t,f as u};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,3 @@ export { CanvasStudio, type CanvasStudioProps, type CanvasStudioRef } from './Ca
|
|
|
2
2
|
export { default } from './CanvasStudio';
|
|
3
3
|
export * from './types';
|
|
4
4
|
export * from './constants';
|
|
5
|
-
export * from './utils/shapeTools';
|
|
6
|
-
export { isMac, complementaryHexColor, getPointerPosition, getStageCenter, getStageViewPortCenter, } from './utils';
|