@versa_ai/vmml-editor 1.0.14 → 1.0.16
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 +9 -9
- package/dist/index.js +146 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +146 -49
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/assets/css/index.scss +1 -1
- package/src/components/EditorCanvas.tsx +176 -99
- package/src/index.tsx +74 -46
- package/src/utils/VmmlConverter.ts +0 -1
package/package.json
CHANGED
|
@@ -8,9 +8,11 @@ import { usePeekControl } from "../utils/usePeekControl";
|
|
|
8
8
|
import { toSvg } from 'dom-to-image'
|
|
9
9
|
|
|
10
10
|
const EditorCanvas = forwardRef(
|
|
11
|
-
({ previewState, showCanvas, canvasSize, enterPreview, intoTextEdit, frame, vmml, dragState, initFcObjs, onVideoChange, isBatchModify, hideConfig }: any, ref: any) => {
|
|
11
|
+
({ previewState, showCanvas, canvasSize, enterPreview, intoTextEdit, frame, vmml, dragState, initFcObjs, editClips = [], onVideoChange, isBatchModify, hideConfig }: any, ref: any) => {
|
|
12
12
|
const [fc, setFc] = useState<any>(null);
|
|
13
13
|
const [history, setHistory] = useState<any>(null);
|
|
14
|
+
const [canvasReady, setCanvasReady] = useState(false);
|
|
15
|
+
const editRenderTime = useRef(0);
|
|
14
16
|
const waitFcTasks = useRef<any>([]);
|
|
15
17
|
const vmmlConverterRef = useRef<any>(null);
|
|
16
18
|
const heightScaleRef = useRef<number>(1);
|
|
@@ -31,7 +33,8 @@ const EditorCanvas = forwardRef(
|
|
|
31
33
|
setFc(canvas);
|
|
32
34
|
initCanvasEvent(canvas);
|
|
33
35
|
usePeekControl(canvas, hideConfig);
|
|
34
|
-
|
|
36
|
+
setCanvasReady(true);
|
|
37
|
+
};
|
|
35
38
|
|
|
36
39
|
const createFcObjs = (canvas: any) => {
|
|
37
40
|
const ns = Math.floor((frame / 30) * 1000000);
|
|
@@ -216,10 +219,45 @@ const EditorCanvas = forwardRef(
|
|
|
216
219
|
}
|
|
217
220
|
};
|
|
218
221
|
|
|
219
|
-
|
|
220
|
-
|
|
222
|
+
const createEditObjes = async (canvas: any, time: number) => {
|
|
223
|
+
// 先删除所有旧的可编辑对象
|
|
224
|
+
const allObjects = canvas.getObjects();
|
|
225
|
+
const toRemove: any[] = [];
|
|
226
|
+
allObjects.forEach((obj: any) => {
|
|
227
|
+
if (obj?.clipData?.originClip) {
|
|
228
|
+
toRemove.push(obj);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
toRemove.forEach(obj => canvas.remove(obj));
|
|
232
|
+
|
|
233
|
+
const promises = editClips.map((clip: any) => {
|
|
234
|
+
if (clip.videoClip) {
|
|
235
|
+
return createImageFromClip(clip);
|
|
236
|
+
}
|
|
237
|
+
if (clip.textClip && !clip.audioClip) {
|
|
238
|
+
return createTextFromClip(clip);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const res = await Promise.allSettled(promises);
|
|
242
|
+
const objects: any = [];
|
|
243
|
+
res.forEach((item: any) => {
|
|
244
|
+
if (item.status === 'fulfilled' && item.value) {
|
|
245
|
+
objects.push(item.value);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
if (editRenderTime.current === time) {
|
|
249
|
+
canvas.add(...objects).renderAll();
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// 创建可编辑的videoClip
|
|
254
|
+
const createImageFromClip = (clip: any, fc2?: any) => {
|
|
255
|
+
return new Promise((resolve) => {
|
|
221
256
|
const canvas = fc || fc2;
|
|
222
|
-
if (canvas
|
|
257
|
+
if (!canvas || !canvasSize.width) {
|
|
258
|
+
resolve(null);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
223
261
|
const url = /video/g.test(clip.videoClip.mimeType) ? "" : clip.videoClip.sourceUrl;
|
|
224
262
|
fabric.Image.fromURL(url, (img: any) => {
|
|
225
263
|
const { dimension, posParam } = clip.videoClip;
|
|
@@ -253,100 +291,131 @@ const EditorCanvas = forwardRef(
|
|
|
253
291
|
},
|
|
254
292
|
visible: frame >= inFrame && frame < inFrame + durationFrame
|
|
255
293
|
});
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
vmmlConverterRef.current.updateClip(fObj);
|
|
261
|
-
});
|
|
294
|
+
img.on('modified', () => {
|
|
295
|
+
const fObj = convertToJSON(img);
|
|
296
|
+
fObj.src = "";
|
|
297
|
+
vmmlConverterRef.current.updateClip(fObj);
|
|
262
298
|
});
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
299
|
+
resolve(img);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}
|
|
267
303
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
304
|
+
const handleRedo = () => {
|
|
305
|
+
history.redo();
|
|
306
|
+
};
|
|
307
|
+
const handleUndo = () => {
|
|
308
|
+
history.undo();
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const onBatchModify = (fObj: any, canvas: any) => {
|
|
312
|
+
if (!canvas) return;
|
|
313
|
+
const textObjects = canvas.getObjects().filter((item: any) => item?.clipData?.type === "文字");
|
|
314
|
+
const { left, top, scaleX, scaleY, angle } = fObj;
|
|
315
|
+
|
|
316
|
+
// 批量更新每个文字对象的位置
|
|
317
|
+
textObjects.forEach((textObj: any) => {
|
|
318
|
+
textObj.set({
|
|
319
|
+
left,
|
|
320
|
+
top,
|
|
321
|
+
scaleX,
|
|
322
|
+
scaleY,
|
|
323
|
+
angle
|
|
324
|
+
});
|
|
325
|
+
textObj.setCoords();
|
|
326
|
+
const updatedFObj = convertToJSON(textObj);
|
|
327
|
+
vmmlConverterRef.current.updateClip(updatedFObj);
|
|
328
|
+
});
|
|
329
|
+
canvas.renderAll();
|
|
330
|
+
const event = new CustomEvent('editor-vmml-batch-change', {
|
|
331
|
+
detail: {
|
|
332
|
+
vmml: vmmlConverterRef.current?.vmml ?? null
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
window.dispatchEvent(event);
|
|
336
|
+
}
|
|
274
337
|
|
|
275
|
-
|
|
276
|
-
|
|
338
|
+
// 创建可编辑的textclip
|
|
339
|
+
const createTextFromClip = async (clip: any, fc2?: any) => {
|
|
340
|
+
return new Promise(async (resolve) => {
|
|
277
341
|
const canvas = fc || fc2;
|
|
278
|
-
if (canvas) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const { textContent, backgroundColor, textColor, posParam, fontAssetUrl, alignType } = clip.textClip;
|
|
282
|
-
const scaleX = posParam.scaleX * fontSize / 22 / widthScaleRef.current;
|
|
283
|
-
const scaleY = posParam.scaleY * fontSize / 22 / heightScaleRef.current;
|
|
284
|
-
const left = canvasSize.width * posParam.centerX;
|
|
285
|
-
const top = canvasSize.height * posParam.centerY;
|
|
286
|
-
const bgColor = backgroundColor ? argbToRgba(backgroundColor) : 'transparent';
|
|
287
|
-
const isAiError = textContent === '请输入文案' && textColor === '#00000000';
|
|
288
|
-
const textFill = argbToRgba(isAiError ? '#ffffffff' : (textColor || '#ffffffff'));
|
|
289
|
-
const textBasicInfo = {
|
|
290
|
-
isBack: backgroundColor ? true : false,
|
|
291
|
-
colorValue: textFill,
|
|
292
|
-
colorName: 'custom',
|
|
293
|
-
textAlign: alignType === 1 ? 'center' : (alignType === 2 ? 'right' : 'left')
|
|
294
|
-
}
|
|
295
|
-
const textImgData = await createTextImg({ textContent, bgColor, textColor: textFill, fontAssetUrl, textBasicInfo });
|
|
296
|
-
const fontJSON = localStorage.getItem("VMML_PLAYER_FONTSMAP");
|
|
297
|
-
let fontMap: any = {};
|
|
298
|
-
try {
|
|
299
|
-
fontMap = fontJSON ? JSON.parse(fontJSON) : {};
|
|
300
|
-
} catch {
|
|
301
|
-
fontMap = {};
|
|
302
|
-
}
|
|
303
|
-
const fontFamily = fontMap[fontAssetUrl] || '';
|
|
304
|
-
fabric.Image.fromURL(textImgData.base64Image, (imgData: any) => {
|
|
305
|
-
imgData.set({
|
|
306
|
-
left,
|
|
307
|
-
top,
|
|
308
|
-
width: textImgData.width,
|
|
309
|
-
height: textImgData.height,
|
|
310
|
-
scaleX,
|
|
311
|
-
scaleY,
|
|
312
|
-
angle: posParam.rotationZ,
|
|
313
|
-
originX: 'center',
|
|
314
|
-
originY: 'center',
|
|
315
|
-
clipData: {
|
|
316
|
-
id: uuidv4(),
|
|
317
|
-
inPoint: clip.inPoint,
|
|
318
|
-
inFrame: getFrames(clip.inPoint, 30),
|
|
319
|
-
type: "文字",
|
|
320
|
-
textColor: textFill,
|
|
321
|
-
text: textContent,
|
|
322
|
-
bgColor,
|
|
323
|
-
originClip: clip,
|
|
324
|
-
fontAssetUrl,
|
|
325
|
-
fontFamily,
|
|
326
|
-
textBasicInfo,
|
|
327
|
-
isAiError,
|
|
328
|
-
duration: clip.duration
|
|
329
|
-
},
|
|
330
|
-
})
|
|
331
|
-
imgData.on("selected", (options: any) => {
|
|
332
|
-
options.target.isSelected = -1;
|
|
333
|
-
});
|
|
334
|
-
imgData.on("moving", (options: any) => {
|
|
335
|
-
options.transform.target.isSelected = 0;
|
|
336
|
-
});
|
|
337
|
-
imgData.on('modified', () => {
|
|
338
|
-
const fObj = convertToJSON(imgData);
|
|
339
|
-
if (fObj.clipData.isAiError) {
|
|
340
|
-
fObj.clipData.textColor = 'rgba(0, 0, 0, 0)';
|
|
341
|
-
}
|
|
342
|
-
vmmlConverterRef.current.updateClip(fObj);
|
|
343
|
-
});
|
|
344
|
-
canvas.add(imgData).renderAll();
|
|
345
|
-
})
|
|
346
|
-
} else {
|
|
347
|
-
waitFcTasks.current.push(createTextFromClip.bind(this, clip));
|
|
342
|
+
if (!canvas) {
|
|
343
|
+
resolve(null);
|
|
344
|
+
return;
|
|
348
345
|
}
|
|
349
|
-
|
|
346
|
+
const { width, height } = vmml.template.dimension;
|
|
347
|
+
const fontSize = getFontSize(width, height);
|
|
348
|
+
const { textContent, backgroundColor, textColor, posParam, fontAssetUrl, alignType } = clip.textClip;
|
|
349
|
+
const scaleX = posParam.scaleX * fontSize / 22 / widthScaleRef.current;
|
|
350
|
+
const scaleY = posParam.scaleY * fontSize / 22 / heightScaleRef.current;
|
|
351
|
+
const left = canvasSize.width * posParam.centerX;
|
|
352
|
+
const top = canvasSize.height * posParam.centerY;
|
|
353
|
+
const bgColor = backgroundColor ? argbToRgba(backgroundColor) : 'transparent';
|
|
354
|
+
const isAiError = textContent === '请输入文案' && textColor === '#00000000';
|
|
355
|
+
const textFill = argbToRgba(isAiError ? '#ffffffff' : (textColor || '#ffffffff'));
|
|
356
|
+
const textBasicInfo = {
|
|
357
|
+
isBack: backgroundColor ? true : false,
|
|
358
|
+
colorValue: textFill,
|
|
359
|
+
colorName: 'custom',
|
|
360
|
+
textAlign: alignType === 1 ? 'center' : (alignType === 2 ? 'right' : 'left')
|
|
361
|
+
}
|
|
362
|
+
const textImgData = await createTextImg({ textContent, bgColor, textColor: textFill, fontAssetUrl, textBasicInfo });
|
|
363
|
+
const fontJSON = localStorage.getItem("VMML_PLAYER_FONTSMAP");
|
|
364
|
+
let fontMap: any = {};
|
|
365
|
+
try {
|
|
366
|
+
fontMap = fontJSON ? JSON.parse(fontJSON) : {};
|
|
367
|
+
} catch {
|
|
368
|
+
fontMap = {};
|
|
369
|
+
}
|
|
370
|
+
const fontFamily = fontMap[fontAssetUrl] || '';
|
|
371
|
+
fabric.Image.fromURL(textImgData.base64Image, (imgData: any) => {
|
|
372
|
+
imgData.set({
|
|
373
|
+
left,
|
|
374
|
+
top,
|
|
375
|
+
width: textImgData.width,
|
|
376
|
+
height: textImgData.height,
|
|
377
|
+
scaleX,
|
|
378
|
+
scaleY,
|
|
379
|
+
angle: posParam.rotationZ,
|
|
380
|
+
originX: 'center',
|
|
381
|
+
originY: 'center',
|
|
382
|
+
clipData: {
|
|
383
|
+
id: clip.id,
|
|
384
|
+
inPoint: clip.inPoint,
|
|
385
|
+
inFrame: getFrames(clip.inPoint, 30),
|
|
386
|
+
type: "文字",
|
|
387
|
+
textColor: textFill,
|
|
388
|
+
text: textContent,
|
|
389
|
+
bgColor,
|
|
390
|
+
originClip: clip,
|
|
391
|
+
fontAssetUrl,
|
|
392
|
+
fontFamily,
|
|
393
|
+
textBasicInfo,
|
|
394
|
+
isAiError,
|
|
395
|
+
duration: clip.duration
|
|
396
|
+
},
|
|
397
|
+
})
|
|
398
|
+
imgData.on("selected", (options: any) => {
|
|
399
|
+
options.target.isSelected = -1;
|
|
400
|
+
});
|
|
401
|
+
imgData.on("moving", (options: any) => {
|
|
402
|
+
options.transform.target.isSelected = 0;
|
|
403
|
+
});
|
|
404
|
+
imgData.on('modified', () => {
|
|
405
|
+
const fObj = convertToJSON(imgData);
|
|
406
|
+
if (fObj.clipData.isAiError) {
|
|
407
|
+
fObj.clipData.textColor = 'rgba(0, 0, 0, 0)';
|
|
408
|
+
}
|
|
409
|
+
if (isBatchModify) {
|
|
410
|
+
onBatchModify(fObj, canvas)
|
|
411
|
+
} else {
|
|
412
|
+
vmmlConverterRef.current.updateClip(fObj);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
resolve(imgData);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
}
|
|
350
419
|
|
|
351
420
|
// 生成简短的字体名称
|
|
352
421
|
const getFontFamilyName = (url: string) => {
|
|
@@ -538,11 +607,19 @@ const EditorCanvas = forwardRef(
|
|
|
538
607
|
}
|
|
539
608
|
}, [fc, dragState])
|
|
540
609
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
610
|
+
useEffect(() => {
|
|
611
|
+
if (canvasSize.width && canvasSize.height && !vmmlConverterRef.current) {
|
|
612
|
+
vmmlConverterRef.current = new VmmlConverter({ vmml, canvasSize });
|
|
613
|
+
}
|
|
614
|
+
}, [canvasSize, vmml]);
|
|
615
|
+
|
|
616
|
+
// 监听 editClips 变化,自动重新渲染(参考 timeline)
|
|
617
|
+
useEffect(() => {
|
|
618
|
+
if (editClips.length && canvasReady && fc) {
|
|
619
|
+
editRenderTime.current = Date.now();
|
|
620
|
+
createEditObjes(fc, editRenderTime.current);
|
|
621
|
+
}
|
|
622
|
+
}, [editClips, canvasReady]);
|
|
546
623
|
|
|
547
624
|
useEffect(() => {
|
|
548
625
|
if (fc) {
|
package/src/index.tsx
CHANGED
|
@@ -16,7 +16,7 @@ import { emotionIcon, wordIcon } from "./utils/const";
|
|
|
16
16
|
const historyClass = new HistoryClass();
|
|
17
17
|
const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
18
18
|
{
|
|
19
|
-
vmml,
|
|
19
|
+
vmml: propVmml,
|
|
20
20
|
pauseFrame = 0,
|
|
21
21
|
maxText = 10,
|
|
22
22
|
maxVideo = 5,
|
|
@@ -33,7 +33,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
33
33
|
}: any,
|
|
34
34
|
ref: any,
|
|
35
35
|
) => {
|
|
36
|
-
|
|
36
|
+
const [vmmlState, setVmmlState] = useState<any>(() => convertVmmlTextScaleByForbidden(propVmml));
|
|
37
37
|
const textMenuRef = useRef<any>(null);
|
|
38
38
|
const vmmlPlayerRef = useRef();
|
|
39
39
|
const canvasRef = useRef();
|
|
@@ -45,7 +45,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
45
45
|
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
|
46
46
|
const [showCanvas, setShowCanvas] = useState(false);
|
|
47
47
|
const [filterIds, setFilterIds] = useState<string[]>([]);
|
|
48
|
-
const [durationInFrames, setDurationInFrames] = useState(getFrames(
|
|
48
|
+
const [durationInFrames, setDurationInFrames] = useState(getFrames(vmmlState?.template?.duration || 1, fps));
|
|
49
49
|
const [previewState, setPreviewState] = useState<boolean>(true); // true预览态 false编辑态
|
|
50
50
|
const [menuState, setMenuState] = useState<string>(""); // text 文字菜单 video 表情包菜单 空不显示
|
|
51
51
|
const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0, top: 0 }); // 画布尺寸
|
|
@@ -58,6 +58,8 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
58
58
|
const [dragState, setDragState] = useState(0);
|
|
59
59
|
const vmmlConverterRef = useRef<any>(null);
|
|
60
60
|
const [initFcObjs, setInitFcObjs] = useState([]);
|
|
61
|
+
const [editClips, setEditClips] = useState<any[]>([]); // 可编辑的 clips
|
|
62
|
+
const [refreshEdit, setRefreshEdit] = useState(0); // 触发画布刷新
|
|
61
63
|
const vmmlFlag = useRef(false);
|
|
62
64
|
const needPlay = useRef(true);
|
|
63
65
|
|
|
@@ -65,9 +67,9 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
65
67
|
const { current }: any = vmmlPlayerRef;
|
|
66
68
|
if (!current) return;
|
|
67
69
|
if (!once.current) {
|
|
68
|
-
current.setVmml(
|
|
70
|
+
current.setVmml(vmmlState, pauseFrame);
|
|
69
71
|
} else {
|
|
70
|
-
current.setVmml(
|
|
72
|
+
current.setVmml(vmmlState, frame);
|
|
71
73
|
}
|
|
72
74
|
};
|
|
73
75
|
|
|
@@ -279,18 +281,9 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
279
281
|
|
|
280
282
|
// 初始化可编辑的clip
|
|
281
283
|
const initCanEditClips = (tracks: any = []) => {
|
|
282
|
-
if (editableArray.length) {
|
|
284
|
+
if (editableArray.length && tracks.length) {
|
|
283
285
|
const list = findEditClips(tracks);
|
|
284
|
-
|
|
285
|
-
const { current }: any = canvasRef;
|
|
286
|
-
list.forEach((clip: any) => {
|
|
287
|
-
if (clip.videoClip) {
|
|
288
|
-
current.createImageFromClip(clip);
|
|
289
|
-
} else {
|
|
290
|
-
current.createTextFromClip(clip);
|
|
291
|
-
}
|
|
292
|
-
})
|
|
293
|
-
}
|
|
286
|
+
setEditClips(list); // 直接更新 state,让 Canvas 组件自动处理
|
|
294
287
|
}
|
|
295
288
|
};
|
|
296
289
|
|
|
@@ -324,7 +317,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
324
317
|
// 获取更新后的vmml
|
|
325
318
|
const getVmml = () => {
|
|
326
319
|
try {
|
|
327
|
-
const tracks =
|
|
320
|
+
const tracks = vmmlState.template.tracks.filter((item: any) => item.editorType === "文字");
|
|
328
321
|
tracks.forEach((track: any) => {
|
|
329
322
|
track.clips.forEach((clip: any) => {
|
|
330
323
|
clip.fObj.src = '';
|
|
@@ -333,7 +326,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
333
326
|
} catch {
|
|
334
327
|
console.log("出错了")
|
|
335
328
|
}
|
|
336
|
-
return
|
|
329
|
+
return vmmlState
|
|
337
330
|
};
|
|
338
331
|
|
|
339
332
|
const getPlayer = () => {
|
|
@@ -354,19 +347,35 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
354
347
|
}, [previewState]);
|
|
355
348
|
|
|
356
349
|
useEffect(() => {
|
|
357
|
-
if (canvasSize.width && canvasSize.height &&
|
|
358
|
-
vmmlConverterRef.current
|
|
350
|
+
if (canvasSize.width && canvasSize.height && vmmlState) {
|
|
351
|
+
if (!vmmlConverterRef.current) {
|
|
352
|
+
vmmlConverterRef.current = new VmmlConverter({ vmml: vmmlState, canvasSize });
|
|
353
|
+
} else {
|
|
354
|
+
vmmlConverterRef.current.vmml = vmmlState;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}, [canvasSize, vmmlState]);
|
|
358
|
+
|
|
359
|
+
useEffect(() => {
|
|
360
|
+
if (editableArray.length && vmmlState?.template) {
|
|
361
|
+
initCanEditClips(vmmlState.template.tracks);
|
|
359
362
|
}
|
|
360
|
-
|
|
361
|
-
|
|
363
|
+
}, [editableArray, refreshEdit]);
|
|
364
|
+
|
|
365
|
+
useEffect(() => {
|
|
366
|
+
if (propVmml) {
|
|
367
|
+
const convertedVmml = convertVmmlTextScaleByForbidden(propVmml);
|
|
368
|
+
setVmmlState(convertedVmml);
|
|
369
|
+
setDurationInFrames(getFrames(propVmml?.template?.duration || 1, fps));
|
|
370
|
+
setRefreshEdit(Date.now());
|
|
362
371
|
}
|
|
363
|
-
}, [
|
|
372
|
+
}, [propVmml]);
|
|
364
373
|
|
|
365
374
|
useEffect(() => {
|
|
366
|
-
if (
|
|
367
|
-
initFcObjects(
|
|
375
|
+
if (vmmlState) {
|
|
376
|
+
initFcObjects(vmmlState.template.tracks);
|
|
368
377
|
}
|
|
369
|
-
}, [
|
|
378
|
+
}, [vmmlState]);
|
|
370
379
|
|
|
371
380
|
useEffect(() => {
|
|
372
381
|
if (player) {
|
|
@@ -404,7 +413,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
404
413
|
if (dragState === 2) {
|
|
405
414
|
needPlay.current = false;
|
|
406
415
|
const { current }: any = vmmlPlayerRef;
|
|
407
|
-
current.setVmml(
|
|
416
|
+
current.setVmml(vmmlState, frame, false);
|
|
408
417
|
setShowCanvas(false);
|
|
409
418
|
}
|
|
410
419
|
if (dragState === 3 || dragState === 4) {
|
|
@@ -412,6 +421,23 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
412
421
|
setPreviewState(false);
|
|
413
422
|
}
|
|
414
423
|
}, [dragState]);
|
|
424
|
+
|
|
425
|
+
const updateVmml = (v: any) => {
|
|
426
|
+
const { current: playerCurrent }: any = vmmlPlayerRef;
|
|
427
|
+
const { current: canvasCurrent }: any = canvasRef;
|
|
428
|
+
if (!playerCurrent) return;
|
|
429
|
+
|
|
430
|
+
const convertedVmml = convertVmmlTextScaleByForbidden(v);
|
|
431
|
+
setVmmlState(convertedVmml);
|
|
432
|
+
setDurationInFrames(getFrames(v?.template?.duration || 1, fps));
|
|
433
|
+
playerCurrent.setVmml(convertedVmml, pauseFrame);
|
|
434
|
+
|
|
435
|
+
setRefreshEdit(Date.now());
|
|
436
|
+
|
|
437
|
+
if (canvasCurrent) {
|
|
438
|
+
canvasCurrent.checkObjectInPoint(pauseFrame);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
415
441
|
|
|
416
442
|
useImperativeHandle(ref,
|
|
417
443
|
() => ({
|
|
@@ -420,9 +446,10 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
420
446
|
getVmml,
|
|
421
447
|
getPlayer,
|
|
422
448
|
texteditClose,
|
|
423
|
-
textFinish
|
|
449
|
+
textFinish,
|
|
450
|
+
updateVmml
|
|
424
451
|
}),
|
|
425
|
-
[
|
|
452
|
+
[vmmlState, player]
|
|
426
453
|
)
|
|
427
454
|
const texteditClose = ()=>{
|
|
428
455
|
if (textMenuRef) {
|
|
@@ -455,7 +482,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
455
482
|
<div className="vessel" onClick={onClickMain}>
|
|
456
483
|
<VmmlPlayer
|
|
457
484
|
ref={vmmlPlayerRef}
|
|
458
|
-
vmml={
|
|
485
|
+
vmml={vmmlState}
|
|
459
486
|
existenceBorderRadio
|
|
460
487
|
moveToBeginningWhenEnded
|
|
461
488
|
muted={true}
|
|
@@ -467,22 +494,23 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
467
494
|
filterIds={filterIds}
|
|
468
495
|
/>
|
|
469
496
|
</div>
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
497
|
+
<EditorCanvas
|
|
498
|
+
ref={canvasRef}
|
|
499
|
+
previewState={previewState}
|
|
500
|
+
showCanvas={showCanvas}
|
|
501
|
+
canvasSize={canvasSize}
|
|
502
|
+
enterPreview={enterPreview}
|
|
503
|
+
intoTextEdit={intoTextEdit}
|
|
504
|
+
frame={frame}
|
|
505
|
+
vmml={vmmlState}
|
|
506
|
+
dragState={dragState}
|
|
507
|
+
initFcObjs={initFcObjs}
|
|
508
|
+
editClips={editClips}
|
|
509
|
+
onVideoChange={onVideoChange}
|
|
510
|
+
isBatchModify={isBatchModify}
|
|
511
|
+
hideConfig={hideConfig}
|
|
512
|
+
// textInfoReset={textInfoReset}
|
|
513
|
+
/>
|
|
486
514
|
<div className="controls-box">
|
|
487
515
|
<Controls
|
|
488
516
|
player={player}
|