@versa_ai/vmml-editor 1.0.16 → 1.0.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versa_ai/vmml-editor",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "module": "dist/index.mjs",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -16,7 +16,7 @@
16
16
  "remotion": "4.0.166",
17
17
  "uuid": "^10.0.0",
18
18
  "zod": "^3.23.8",
19
- "@versa_ai/vmml-player": "1.1.15",
19
+ "@versa_ai/vmml-player": "1.1.16",
20
20
  "@versa_ai/vmml-utils": "1.0.15"
21
21
  },
22
22
  "devDependencies": {
@@ -220,16 +220,6 @@ const EditorCanvas = forwardRef(
220
220
  };
221
221
 
222
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
223
  const promises = editClips.map((clip: any) => {
234
224
  if (clip.videoClip) {
235
225
  return createImageFromClip(clip);
@@ -345,12 +335,14 @@ const EditorCanvas = forwardRef(
345
335
  }
346
336
  const { width, height } = vmml.template.dimension;
347
337
  const fontSize = getFontSize(width, height);
348
- const { textContent, backgroundColor, textColor, posParam, fontAssetUrl, alignType } = clip.textClip;
338
+ const { textContent, backgroundColor, textColor, posParam, fontAssetUrl, alignType, strokeColor, strokeWidth } = clip.textClip;
349
339
  const scaleX = posParam.scaleX * fontSize / 22 / widthScaleRef.current;
350
340
  const scaleY = posParam.scaleY * fontSize / 22 / heightScaleRef.current;
341
+ const strokeW = strokeWidth ? (strokeWidth * 22 / fontSize) : 0;
351
342
  const left = canvasSize.width * posParam.centerX;
352
343
  const top = canvasSize.height * posParam.centerY;
353
344
  const bgColor = backgroundColor ? argbToRgba(backgroundColor) : 'transparent';
345
+ const stColor = strokeColor ? argbToRgba(strokeColor) : 'transparent';
354
346
  const isAiError = textContent === '请输入文案' && textColor === '#00000000';
355
347
  const textFill = argbToRgba(isAiError ? '#ffffffff' : (textColor || '#ffffffff'));
356
348
  const textBasicInfo = {
@@ -359,7 +351,7 @@ const EditorCanvas = forwardRef(
359
351
  colorName: 'custom',
360
352
  textAlign: alignType === 1 ? 'center' : (alignType === 2 ? 'right' : 'left')
361
353
  }
362
- const textImgData = await createTextImg({ textContent, bgColor, textColor: textFill, fontAssetUrl, textBasicInfo });
354
+ const textImgData = await createTextImg({ textContent, bgColor, stColor, strokeW, textColor: textFill, fontAssetUrl, textBasicInfo });
363
355
  const fontJSON = localStorage.getItem("VMML_PLAYER_FONTSMAP");
364
356
  let fontMap: any = {};
365
357
  try {
@@ -417,23 +409,22 @@ const EditorCanvas = forwardRef(
417
409
  });
418
410
  }
419
411
 
420
- // 生成简短的字体名称
421
- const getFontFamilyName = (url: string) => {
422
- // 从URL中提取文件名作为字体名称
423
- const filename = url.split('/').pop() || '';
424
- const name = filename.replace(/\.(ttf|otf|woff2?|eot)$/i, '');
425
- return `CustomFont_${name.substring(0, 20)}`; // 限制长度
426
- };
412
+ // 生成简短的字体名称
413
+ const getFontFamilyName = (url: string) => {
414
+ const filename = url.split('/').pop() || '';
415
+ const name = filename.replace(/\.(ttf|otf|woff2?|eot)$/i, '');
416
+ return `CustomFont_${name.substring(0, 20)}`; // 限制长度
417
+ };
427
418
 
428
- // 检测字体格式
429
- const detectFontFormat = (url: string) => {
430
- const lower = url.toLowerCase();
431
- if (lower.includes('.woff2')) return 'woff2';
432
- if (lower.includes('.woff')) return 'woff';
433
- if (lower.includes('.otf')) return 'opentype';
434
- if (lower.includes('.ttf')) return 'truetype';
435
- return null;
436
- };
419
+ // 检测字体格式
420
+ const detectFontFormat = (url: string) => {
421
+ const lower = url.toLowerCase();
422
+ if (lower.includes('.woff2')) return 'woff2';
423
+ if (lower.includes('.woff')) return 'woff';
424
+ if (lower.includes('.otf')) return 'opentype';
425
+ if (lower.includes('.ttf')) return 'truetype';
426
+ return null;
427
+ };
437
428
 
438
429
  const embedFontInSVG = async (svgString: string, url: string) => {
439
430
  if (url) {
@@ -455,7 +446,7 @@ const EditorCanvas = forwardRef(
455
446
  }
456
447
 
457
448
  //文字转图片
458
- const createTextImg = async ({ textContent, bgColor, textColor, fontAssetUrl = null, textBasicInfo }: any) => {
449
+ const createTextImg = async ({ textContent, bgColor, textColor, stColor, strokeW, fontAssetUrl = null, textBasicInfo }: any) => {
459
450
  const container = document.createElement('div');
460
451
  container.style.backgroundColor = bgColor
461
452
  // container.style.width = `fit-content`
@@ -472,8 +463,12 @@ const EditorCanvas = forwardRef(
472
463
  p.style.lineHeight = '22px'
473
464
  p.style.fontFamily = fontFamily;
474
465
  p.style.whiteSpace = "nowrap"
475
- // p.style.backgroundColor = bgColor;
476
466
  p.style.padding = '0';
467
+ p.style.margin = '0';
468
+
469
+ p.style.webkitTextStrokeWidth = `${strokeW ?? 0}px`;
470
+ p.style.webkitTextStrokeColor = stColor;
471
+
477
472
  p.textContent = line || " "
478
473
  container.appendChild(p);
479
474
  })
@@ -571,7 +566,6 @@ const EditorCanvas = forwardRef(
571
566
  return fc.getObjects();
572
567
  }
573
568
  }
574
-
575
569
  const styles: any = useMemo(() => {
576
570
  return {
577
571
  position: "absolute",
@@ -632,6 +626,10 @@ const EditorCanvas = forwardRef(
632
626
  }
633
627
  }, [fc]);
634
628
 
629
+ const getCanvasCtx = () => {
630
+ return fc
631
+ }
632
+
635
633
  useImperativeHandle(ref, () => ({
636
634
  createImage,
637
635
  createText,
@@ -645,8 +643,9 @@ const EditorCanvas = forwardRef(
645
643
  checkObjectInPoint,
646
644
  createImageFromClip,
647
645
  createTextFromClip,
648
- changeObjectVisible
649
- }));
646
+ changeObjectVisible,
647
+ getCanvasCtx
648
+ }), [fc]);
650
649
 
651
650
  const getActions = () => {
652
651
  if (history) {
package/src/index.tsx CHANGED
@@ -55,7 +55,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
55
55
  const videoList = useRef<any>([]);
56
56
  const [signList, setSignList] = useState<any>([]);
57
57
  const [tips, setTips] = useState("");
58
- const [dragState, setDragState] = useState(0);
58
+ const [dragState, setDragState] = useState(0); // 1: onPointerDown 2: onPointerMove 3: onPointerUp 4: onClickSign
59
59
  const vmmlConverterRef = useRef<any>(null);
60
60
  const [initFcObjs, setInitFcObjs] = useState([]);
61
61
  const [editClips, setEditClips] = useState<any[]>([]); // 可编辑的 clips
@@ -76,10 +76,12 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
76
76
  const onPlayerReady = () => {
77
77
  const { current }: any = vmmlPlayerRef;
78
78
  vmmlFlag.current = false;
79
+ let show = false
79
80
  if (current && current.playerRef) {
80
81
  setPlayer(current.playerRef);
81
82
  current.unmute();
82
83
  if (!once.current) {
84
+ show = true
83
85
  once.current = true;
84
86
  } else {
85
87
  if (needPlay.current) {
@@ -89,7 +91,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
89
91
  }
90
92
  }
91
93
  }
92
- setShowCanvas(false);
94
+ setShowCanvas(show);
93
95
  setLoading(false);
94
96
  };
95
97
 
@@ -264,9 +266,11 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
264
266
  setFrame(e.detail.frame)
265
267
  };
266
268
  const onPlay = () => {
269
+ setShowCanvas(false)
267
270
  setIsPlaying(true);
268
271
  };
269
272
  const onPause = () => {
273
+ setShowCanvas(true)
270
274
  setIsPlaying(false);
271
275
  };
272
276
  const onWaiting = () => {
@@ -426,7 +430,8 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
426
430
  const { current: playerCurrent }: any = vmmlPlayerRef;
427
431
  const { current: canvasCurrent }: any = canvasRef;
428
432
  if (!playerCurrent) return;
429
-
433
+ canvasCurrent?.getCanvasCtx()?.clear?.()
434
+
430
435
  const convertedVmml = convertVmmlTextScaleByForbidden(v);
431
436
  setVmmlState(convertedVmml);
432
437
  setDurationInFrames(getFrames(v?.template?.duration || 1, fps));
@@ -28,7 +28,7 @@ class VmmlConverter {
28
28
  //更新位置
29
29
  private setPosParam(fObj: any): object {
30
30
  const {
31
- clipData: { type },
31
+ clipData: { type, originClip },
32
32
  centerPoint,
33
33
  scaleX,
34
34
  scaleY,
@@ -36,28 +36,41 @@ class VmmlConverter {
36
36
  width,
37
37
  height,
38
38
  } = fObj;
39
- let _scaleX, _scaleY, _centerX, _centerY;
40
-
39
+ let _scaleX, _scaleY, _centerX, _centerY, _scaleZ, _centerZ, _rotationX, _rotationY;
40
+ let key = '';
41
41
  if (type === "文字") {
42
42
  // const [rect, textbox] = fObj.objects;
43
43
  _scaleX = (22 * scaleX * this.widthScale) / this.fontSize;
44
44
  _scaleY = (22 * scaleY * this.heightScale) / this.fontSize;
45
45
  _centerX = centerPoint.x / this.canvasSize.width;
46
46
  _centerY = centerPoint.y / this.canvasSize.height;
47
+
48
+ key = 'textClip'
47
49
  // fObj.clipData.lineSpacing = 22 * textbox.lineHeight * scaleY * this.heightScale;
48
50
  } else if (type === "表情包") {
49
51
  _scaleX = (width * scaleX * this.widthScale) / width;
50
52
  _scaleY = (height * scaleY * this.heightScale) / height;
51
53
  _centerX = centerPoint.x / this.canvasSize.width;
52
54
  _centerY = centerPoint.y / this.canvasSize.height;
55
+
56
+ key = 'videoClip'
53
57
  }
54
58
 
59
+ _scaleZ = originClip?.[key]?.posParam?.scaleZ ?? 1;
60
+ _centerZ = originClip?.[key]?.posParam?.centerZ ?? 0.5;
61
+ _rotationX = originClip?.[key]?.posParam?.rotationX ?? 0;
62
+ _rotationY = originClip?.[key]?.posParam?.rotationY ?? 0;
63
+
55
64
  return {
56
65
  scaleX: _scaleX,
57
66
  scaleY: _scaleY,
67
+ scaleZ: _scaleZ,
58
68
  centerX: _centerX,
59
69
  centerY: _centerY,
70
+ centerZ: _centerZ,
60
71
  rotationZ: angle,
72
+ rotationX: _rotationX,
73
+ rotationY: _rotationY,
61
74
  };
62
75
  }
63
76