@versa_ai/vmml-editor 1.0.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.
- package/.turbo/turbo-build.log +335 -0
- package/CHANGELOG.md +16 -0
- package/README.md +1 -0
- package/biome.json +7 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2675 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2673 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -0
- package/postcss.config.js +3 -0
- package/src/assets/css/closeLayer.scss +50 -0
- package/src/assets/css/colorSelector.scss +59 -0
- package/src/assets/css/editorTextMenu.less +130 -0
- package/src/assets/css/editorTextMenu.scss +149 -0
- package/src/assets/css/index.scss +252 -0
- package/src/assets/css/loading.scss +31 -0
- package/src/assets/css/maxTextLayer.scss +31 -0
- package/src/assets/img/icon_Brush.png +0 -0
- package/src/assets/img/icon_Change.png +0 -0
- package/src/assets/img/icon_Cut.png +0 -0
- package/src/assets/img/icon_Face.png +0 -0
- package/src/assets/img/icon_Graffiti.png +0 -0
- package/src/assets/img/icon_Mute.png +0 -0
- package/src/assets/img/icon_Refresh.png +0 -0
- package/src/assets/img/icon_Text1.png +0 -0
- package/src/assets/img/icon_Text2.png +0 -0
- package/src/assets/img/icon_Volume.png +0 -0
- package/src/assets/img/icon_Word.png +0 -0
- package/src/components/CloseLayer.tsx +25 -0
- package/src/components/ColorSelector.tsx +90 -0
- package/src/components/Controls.tsx +32 -0
- package/src/components/EditorCanvas.tsx +566 -0
- package/src/components/Loading.tsx +16 -0
- package/src/components/MaxTextLayer.tsx +27 -0
- package/src/components/SeekBar.tsx +126 -0
- package/src/components/TextMenu.tsx +332 -0
- package/src/components/VideoMenu.tsx +49 -0
- package/src/index.tsx +551 -0
- package/src/utils/HistoryClass.ts +131 -0
- package/src/utils/VmmlConverter.ts +339 -0
- package/src/utils/const.ts +10 -0
- package/src/utils/keyBoardUtils.ts +199 -0
- package/src/utils/usePeekControl.ts +242 -0
- package/tsconfig.json +5 -0
- package/tsup.config.ts +14 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { fabric } from "fabric";
|
|
2
|
+
function drawImg(
|
|
3
|
+
ctx: CanvasRenderingContext2D,
|
|
4
|
+
left: number,
|
|
5
|
+
top: number,
|
|
6
|
+
img: HTMLImageElement,
|
|
7
|
+
wSize: number,
|
|
8
|
+
hSize: number,
|
|
9
|
+
angle: number | undefined,
|
|
10
|
+
) {
|
|
11
|
+
if (angle === undefined) return;
|
|
12
|
+
ctx.save();
|
|
13
|
+
ctx.translate(left, top);
|
|
14
|
+
ctx.rotate(fabric.util.degreesToRadians(angle));
|
|
15
|
+
ctx.drawImage(img, -wSize / 2, -hSize / 2, wSize, hSize);
|
|
16
|
+
ctx.restore();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 顶点
|
|
20
|
+
function usePeekControl(canvas: fabric.Canvas) {
|
|
21
|
+
const iconUrls = {
|
|
22
|
+
delete: "https://mass.alipay.com/finmedia_versaassets/uri/file/as/0c7084a9-6067-4277-a5af-2932983cbeb7.png",
|
|
23
|
+
// zoom: "https://mass.alipay.com/finmedia_versaassets/uri/file/as/ff6aea73-4d12-4201-9404-3134d5f9525c.png",
|
|
24
|
+
zoom: "https://mass.alipay.com/finmedia_versaassets/uri/file/as/0909485d-46db-47b4-8bb1-5a686510ddb3.png",
|
|
25
|
+
mute: "https://mass.alipay.com/finmedia_versaassets/uri/file/as/3fef2c5d-7576-424d-813c-34508b051884.png",
|
|
26
|
+
volume: "https://mass.alipay.com/finmedia_versaassets/uri/file/as/1ab2e829-c5b1-4177-8554-ae2f74b02e1d.png",
|
|
27
|
+
edit: "https://mass.alipay.com/finmedia_versaassets/uri/file/as/9e9ed472-62cf-4b71-891f-bc08c8b08a5d.png"
|
|
28
|
+
};
|
|
29
|
+
const createIconElement = (url: string) => {
|
|
30
|
+
const img = document.createElement("img");
|
|
31
|
+
img.src = url;
|
|
32
|
+
return img;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// 删除选中元素
|
|
36
|
+
const deleteObject = (mouseEvent: MouseEvent, target: fabric.Transform) => {
|
|
37
|
+
if (target.action === "rotate") return true;
|
|
38
|
+
const activeObject = canvas.getActiveObjects();
|
|
39
|
+
if (activeObject) {
|
|
40
|
+
activeObject.map((item: any) => canvas.remove(item));
|
|
41
|
+
canvas.requestRenderAll();
|
|
42
|
+
canvas.discardActiveObject();
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
46
|
+
const delImg = createIconElement(iconUrls.delete);
|
|
47
|
+
const zoomImg = createIconElement(iconUrls.zoom);
|
|
48
|
+
const muteImg = createIconElement(iconUrls.mute);
|
|
49
|
+
const volumeImg = createIconElement(iconUrls.volume);
|
|
50
|
+
const editImg = createIconElement(iconUrls.edit);
|
|
51
|
+
//渲染删除图标
|
|
52
|
+
const renderDelIcon = (
|
|
53
|
+
ctx: CanvasRenderingContext2D,
|
|
54
|
+
left: number,
|
|
55
|
+
top: number,
|
|
56
|
+
styleOverride: any,
|
|
57
|
+
fabricObject: fabric.Object,
|
|
58
|
+
) => {
|
|
59
|
+
drawImg(ctx, left, top, delImg, 28, 28, fabricObject.angle);
|
|
60
|
+
};
|
|
61
|
+
//渲染旋转缩放图标
|
|
62
|
+
const renderIconRotate = (
|
|
63
|
+
ctx: CanvasRenderingContext2D,
|
|
64
|
+
left: number,
|
|
65
|
+
top: number,
|
|
66
|
+
styleOverride: any,
|
|
67
|
+
fabricObject: fabric.Object,
|
|
68
|
+
) => {
|
|
69
|
+
drawImg(ctx, left, top, zoomImg, 28, 28, fabricObject.angle);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const renderMultiIcon = (ctx: CanvasRenderingContext2D,
|
|
73
|
+
left: number,
|
|
74
|
+
top: number,
|
|
75
|
+
styleOverride: any,
|
|
76
|
+
fabricObject: fabric.Object | any,) => {
|
|
77
|
+
if (fabricObject.clipData.type === '文字') {
|
|
78
|
+
renderEditIcon(ctx,
|
|
79
|
+
left,
|
|
80
|
+
top,
|
|
81
|
+
styleOverride,
|
|
82
|
+
fabricObject)
|
|
83
|
+
} else {
|
|
84
|
+
renderMuteIcon(ctx,
|
|
85
|
+
left,
|
|
86
|
+
top,
|
|
87
|
+
styleOverride,
|
|
88
|
+
fabricObject
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//渲染静音图标
|
|
93
|
+
const renderMuteIcon = (
|
|
94
|
+
ctx: CanvasRenderingContext2D,
|
|
95
|
+
left: number,
|
|
96
|
+
top: number,
|
|
97
|
+
styleOverride: any,
|
|
98
|
+
fabricObject: fabric.Object | any,
|
|
99
|
+
) => {
|
|
100
|
+
if (!fabricObject.clipData) return false;
|
|
101
|
+
if (fabricObject.clipData.type === '表情包') {
|
|
102
|
+
if (fabricObject.clipData.isMute === null || fabricObject.clipData.isMute === undefined) return false;
|
|
103
|
+
if (fabricObject.clipData.isMute) {
|
|
104
|
+
drawImg(ctx, left, top, muteImg, 28, 28, fabricObject.angle);
|
|
105
|
+
} else {
|
|
106
|
+
drawImg(ctx, left, top, volumeImg, 28, 28, fabricObject.angle);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
//渲染编辑图标
|
|
111
|
+
const renderEditIcon = (
|
|
112
|
+
ctx: CanvasRenderingContext2D,
|
|
113
|
+
left: number,
|
|
114
|
+
top: number,
|
|
115
|
+
styleOverride: any,
|
|
116
|
+
fabricObject: fabric.Object | any,
|
|
117
|
+
) => {
|
|
118
|
+
if (!fabricObject.clipData) return false;
|
|
119
|
+
if (fabricObject.clipData.type === '文字') {
|
|
120
|
+
drawImg(ctx, left, top, editImg, 28, 28, fabricObject.angle);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const multiAction = (eventData: any, transform: any, x: any, y: any)=>{
|
|
124
|
+
if (transform.target.clipData.type === '文字') {
|
|
125
|
+
editAction(eventData, transform, x, y)
|
|
126
|
+
}else{
|
|
127
|
+
muteAction(eventData, transform, x, y)
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
//需要暴露方法
|
|
132
|
+
const muteAction = (eventData: any, transform: any, x: any, y: any) => {
|
|
133
|
+
if (transform.target.clipData.isMute === null) return false;
|
|
134
|
+
transform.target.clipData = JSON.parse(JSON.stringify(transform.target.clipData));
|
|
135
|
+
transform.target.clipData.isMute = !transform.target.clipData.isMute;
|
|
136
|
+
canvas.renderAll();
|
|
137
|
+
canvas.fire("object:muteChange", { target: transform.target });
|
|
138
|
+
return true;
|
|
139
|
+
};
|
|
140
|
+
//暴露文字编辑方法
|
|
141
|
+
const editAction = (eventData: any, transform: any, x: any, y: any) => {
|
|
142
|
+
if (transform.target.clipData.type === '文字') {
|
|
143
|
+
canvas.fire("object:textEdit", { target: transform.target });
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const actionHandler = (
|
|
149
|
+
eventData: any, // 在实际应用中,应该为 eventData 提供准确的类型定义
|
|
150
|
+
transform: any,
|
|
151
|
+
x: number,
|
|
152
|
+
y: number
|
|
153
|
+
): boolean => {
|
|
154
|
+
try {
|
|
155
|
+
const t = transform,
|
|
156
|
+
target = t.target,
|
|
157
|
+
pivotPoint = target.translateToOriginPoint(
|
|
158
|
+
target.getCenterPoint(),
|
|
159
|
+
t.originX,
|
|
160
|
+
t.originY
|
|
161
|
+
);
|
|
162
|
+
const lastAngle = Math.atan2(t.ey - pivotPoint.y, t.ex - pivotPoint.x),
|
|
163
|
+
curAngle = Math.atan2(y - pivotPoint.y, x - pivotPoint.x);
|
|
164
|
+
let angle2 = Math.floor((curAngle - lastAngle + t.theta) * (180 / Math.PI));
|
|
165
|
+
fabric.controlsUtils.scalingEqually(eventData, transform, x, y);
|
|
166
|
+
// 优化条件判断,使用策略模式或映射的方式进行旋转角度的决定
|
|
167
|
+
// 数组前两位是范围,最后一位是设置的角度
|
|
168
|
+
const rotateAngles = [
|
|
169
|
+
[85, 95, 90],
|
|
170
|
+
[175, 185, 180],
|
|
171
|
+
[265, 275, 270],
|
|
172
|
+
[355, 365, 360],
|
|
173
|
+
[440, 460, 90],
|
|
174
|
+
[-5, 5, 0],
|
|
175
|
+
[-95, -85, 270],
|
|
176
|
+
[-185, -175, 180]
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
for (let i = 0; i < rotateAngles.length; i++) {
|
|
180
|
+
if (rotateAngles[i][0] < angle2 && angle2 < rotateAngles[i][1]) {
|
|
181
|
+
target.rotate(rotateAngles[i][2]);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 如果没有符合的旋转范围,则使用默认的旋转逻辑
|
|
187
|
+
fabric.controlsUtils.rotationWithSnapping(eventData, transform, x, y);
|
|
188
|
+
return true;
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error("Action handler error:", error);
|
|
191
|
+
// 根据实际情况处理异常,例如返回false,或者重新抛出异常
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
//top left 删除
|
|
196
|
+
fabric.Object.prototype.controls.tl = new fabric.Control({
|
|
197
|
+
x: -0.5,
|
|
198
|
+
y: -0.5,
|
|
199
|
+
mouseUpHandler: deleteObject,
|
|
200
|
+
render: renderDelIcon,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
//bottom left 左下 -- 设置静音和音量
|
|
204
|
+
fabric.Object.prototype.controls.bl = new fabric.Control({
|
|
205
|
+
x: -0.5,
|
|
206
|
+
y: 0.5,
|
|
207
|
+
mouseUpHandler: muteAction,
|
|
208
|
+
render: renderMuteIcon,
|
|
209
|
+
});
|
|
210
|
+
//top right 右上 文字类型设置编辑
|
|
211
|
+
fabric.Object.prototype.controls.tr = new fabric.Control({
|
|
212
|
+
x: 0.5,
|
|
213
|
+
y: -0.5,
|
|
214
|
+
mouseUpHandler: editAction,
|
|
215
|
+
render: renderEditIcon
|
|
216
|
+
});
|
|
217
|
+
// 中间横杠
|
|
218
|
+
fabric.Object.prototype.controls.ml = new fabric.Control({ visible: false });
|
|
219
|
+
fabric.Object.prototype.controls.mr = new fabric.Control({ visible: false });
|
|
220
|
+
fabric.Object.prototype.controls.mb = new fabric.Control({ visible: false });
|
|
221
|
+
fabric.Object.prototype.controls.mt = new fabric.Control({ visible: false });
|
|
222
|
+
//旋转图标
|
|
223
|
+
fabric.Object.prototype.controls.mtr = new fabric.Control({ visible: false });
|
|
224
|
+
//bottom right 旋转+缩放
|
|
225
|
+
fabric.Object.prototype.controls.br = new fabric.Control({
|
|
226
|
+
x: 0.5,
|
|
227
|
+
y: 0.5,
|
|
228
|
+
actionName: "rotate",
|
|
229
|
+
actionHandler: actionHandler,
|
|
230
|
+
render: renderIconRotate,
|
|
231
|
+
});
|
|
232
|
+
// 选中边框样式
|
|
233
|
+
fabric.Object.prototype.set({
|
|
234
|
+
borderColor: "#d6d6d699",
|
|
235
|
+
borderScaleFactor: 1.5,
|
|
236
|
+
borderOpacityWhenMoving: 1,
|
|
237
|
+
padding: 5,
|
|
238
|
+
});
|
|
239
|
+
return canvas;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export { usePeekControl };
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { sassPlugin } from "esbuild-sass-plugin";
|
|
2
|
+
import { defineConfig } from "tsup";
|
|
3
|
+
export default defineConfig((options) => ({
|
|
4
|
+
entryPoints: ["./src/index.tsx"],
|
|
5
|
+
format: ["cjs", "esm"],
|
|
6
|
+
esbuildPlugins: [sassPlugin({ type: "style" })],
|
|
7
|
+
treeshake: true,
|
|
8
|
+
dts: true,
|
|
9
|
+
sourcemap: true,
|
|
10
|
+
splitting: true,
|
|
11
|
+
external: ["react"],
|
|
12
|
+
injectStyle: true,
|
|
13
|
+
...options,
|
|
14
|
+
}));
|