@versa_ai/vmml-editor 1.0.45 → 1.0.47
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 +40 -127
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +40 -127
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/EditorCanvas.tsx +44 -137
- package/src/index.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versa_ai/vmml-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.47",
|
|
4
4
|
"module": "dist/index.mjs",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.mts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"remotion": "4.0.166",
|
|
18
18
|
"uuid": "^10.0.0",
|
|
19
19
|
"zod": "^3.23.8",
|
|
20
|
-
"@versa_ai/vmml-player": "1.1.
|
|
20
|
+
"@versa_ai/vmml-player": "1.1.24",
|
|
21
21
|
"@versa_ai/vmml-utils": "1.0.15"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
@@ -5,7 +5,6 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
5
5
|
import HistoryClass from "../utils/HistoryClass";
|
|
6
6
|
import VmmlConverter from "../utils/VmmlConverter";
|
|
7
7
|
import { usePeekControl } from "../utils/usePeekControl";
|
|
8
|
-
import { toSvg } from 'dom-to-image'
|
|
9
8
|
|
|
10
9
|
const EditorCanvas = forwardRef(
|
|
11
10
|
({ previewState, showCanvas, canvasSize, enterPreview, intoTextEdit, frame, vmml, dragState, initFcObjs, editClips = [], onVideoChange, isBatchModify, hideConfig, textWarapCenter }: any, ref: any) => {
|
|
@@ -511,26 +510,6 @@ const EditorCanvas = forwardRef(
|
|
|
511
510
|
return null;
|
|
512
511
|
};
|
|
513
512
|
|
|
514
|
-
const embedFontInSVG = async (svgString: string, url: string, fontBase64?: any) => {
|
|
515
|
-
if (url) {
|
|
516
|
-
let res = fontBase64;
|
|
517
|
-
if (!res) await urlToBlob({ url });
|
|
518
|
-
const format = detectFontFormat(url);
|
|
519
|
-
const fontFamilyName = getFontFamilyName(url);
|
|
520
|
-
const srcValue = format ? `url('${res}') format('${format}')` : `url('${res}')`;
|
|
521
|
-
const fontFace = `
|
|
522
|
-
@font-face {
|
|
523
|
-
font-family: '${fontFamilyName}';
|
|
524
|
-
src: ${srcValue};
|
|
525
|
-
}
|
|
526
|
-
`;
|
|
527
|
-
const styleElement = `<style type="text/css"><![CDATA[${fontFace}]]></style>`;
|
|
528
|
-
const result = svgString.replace('</svg>', `${styleElement}</svg>`);
|
|
529
|
-
return result;
|
|
530
|
-
}
|
|
531
|
-
return svgString
|
|
532
|
-
}
|
|
533
|
-
|
|
534
513
|
const loadFont = async (url: any) => {
|
|
535
514
|
if (!url) return null
|
|
536
515
|
|
|
@@ -558,128 +537,56 @@ const EditorCanvas = forwardRef(
|
|
|
558
537
|
fontCacheRef.current.set(url, loadPromise as any);
|
|
559
538
|
return loadPromise;
|
|
560
539
|
}
|
|
561
|
-
|
|
562
|
-
const setTextAlign = (p: any, stroke: any, direction: 'left' | 'center' | 'right') => {
|
|
563
|
-
if (direction === 'center') {
|
|
564
|
-
p.style.justifyContent = 'center'
|
|
565
|
-
stroke.style.left = "50%";
|
|
566
|
-
stroke.style.top = "50%";
|
|
567
|
-
stroke.style.transform = 'translate(-50%, -50%)';
|
|
568
|
-
}
|
|
569
|
-
if (direction === 'left') {
|
|
570
|
-
stroke.style.left = "0";
|
|
571
|
-
stroke.style.top = "0";
|
|
572
|
-
stroke.style.transform = 'none';
|
|
573
|
-
}
|
|
574
|
-
if (direction === 'right') {
|
|
575
|
-
p.style.justifyContent = 'flex-end'
|
|
576
|
-
stroke.style.right = "0";
|
|
577
|
-
stroke.style.top = "50%";
|
|
578
|
-
stroke.style.transform = 'translateY(-50%)';
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
//文字转图片
|
|
583
|
-
const createTextImg = async ({ textContent, bgColor, textColor, stColor, strokeW, fontAssetUrl = null, textBasicInfo, letterSpacing }: any) => {
|
|
584
|
-
const fontBase64 = await loadFont(fontAssetUrl);
|
|
585
|
-
const container = document.createElement('div');
|
|
586
|
-
container.style.backgroundColor = bgColor
|
|
587
|
-
container.style.boxSizing = 'content-box'
|
|
588
|
-
container.style.display = 'inline-block'
|
|
589
|
-
container.style.textAlign = textBasicInfo.textAlign || 'left';
|
|
590
|
-
const lines = textContent.split('\n');
|
|
591
|
-
const fontFamily = fontAssetUrl ? getFontFamilyName(fontAssetUrl) : 'sansMedium';
|
|
592
|
-
lines.forEach((line: string) => {
|
|
593
|
-
const p = document.createElement('p');
|
|
594
|
-
p.style.position = 'relative';
|
|
595
|
-
p.style.display = 'flex';
|
|
596
|
-
p.style.fontSize = '22px';
|
|
597
|
-
p.style.lineHeight = '22px';
|
|
598
|
-
p.style.fontFamily = fontFamily;
|
|
599
|
-
p.style.whiteSpace = 'nowrap';
|
|
600
|
-
p.style.margin = '0';
|
|
601
|
-
p.style.padding = '0';
|
|
602
|
-
if (letterSpacing) p.style.letterSpacing = `${letterSpacing}px`;
|
|
603
|
-
p.style.zIndex = '2'
|
|
604
|
-
|
|
605
|
-
const fill = document.createElement('span');
|
|
606
|
-
fill.textContent = line || ' ';
|
|
607
|
-
fill.style.color = textColor;
|
|
608
|
-
fill.style.position = 'relative';
|
|
609
|
-
fill.style.zIndex = '1'
|
|
610
|
-
|
|
611
|
-
const stroke = document.createElement('span');
|
|
612
|
-
stroke.textContent = line || ' ';
|
|
613
|
-
stroke.style.position = 'absolute';
|
|
614
|
-
|
|
615
|
-
stroke.style.color = 'transparent';
|
|
616
|
-
stroke.style.webkitTextStrokeWidth = `${strokeW}px`;
|
|
617
|
-
stroke.style.webkitTextStrokeColor = stColor;
|
|
618
|
-
|
|
619
|
-
let dr = textBasicInfo.textAlign || 'left'
|
|
620
|
-
if (textWarapCenter) dr = 'center'
|
|
621
|
-
setTextAlign(p, stroke, dr)
|
|
622
|
-
|
|
623
|
-
p.appendChild(fill);
|
|
624
|
-
p.appendChild(stroke);
|
|
625
|
-
container.appendChild(p);
|
|
626
|
-
});
|
|
627
|
-
container.style.padding = '6.5px 7px 6.5px 7px'
|
|
628
|
-
// container.style.padding = '7.5px 7px 5.8px 7px'
|
|
629
|
-
container.style.borderRadius = '5px'
|
|
630
|
-
document.body.appendChild(container)
|
|
631
|
-
const { width, height } = container?.getBoundingClientRect() as any;
|
|
632
|
-
const dataurl = await toSvg(container);
|
|
633
|
-
document.body.removeChild(container);
|
|
634
|
-
const base64Image = await embedFontInSVG(dataurl, fontAssetUrl, fontBase64);
|
|
635
|
-
return { base64Image, height, width };
|
|
636
|
-
}
|
|
540
|
+
// 创建文字
|
|
637
541
|
const createText = async ({ textContent, bgColor, textColor, position, textBasicInfo, id }: any, fc2: any) => {
|
|
638
542
|
const canvas = fc || fc2;
|
|
639
543
|
const { left, top, angle, scaleX, scaleY, zoomX, zoomY } = position;
|
|
640
|
-
const
|
|
544
|
+
const { objects } = await createTextObjs({ strokeW: 0, stColor: '', fontAssetUrl: null, letterSpace: 0, bgColor, textContent, textBasicInfo, textColor});
|
|
545
|
+
|
|
641
546
|
return new Promise((resolve, reject) => {
|
|
642
|
-
fabric.
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
onVideoChange(imgData.clipData);
|
|
678
|
-
vmmlConverterRef.current.addTextClip(convertToJSON(imgData));
|
|
679
|
-
resolve(true);
|
|
547
|
+
const group = new fabric.Group([...objects], {
|
|
548
|
+
left,
|
|
549
|
+
top,
|
|
550
|
+
scaleX,
|
|
551
|
+
scaleY,
|
|
552
|
+
angle,
|
|
553
|
+
originX: 'center',
|
|
554
|
+
originY: 'center',
|
|
555
|
+
clipData: {
|
|
556
|
+
duration: vmmlConverterRef.current.vmml.template.duration,
|
|
557
|
+
id: uuidv4(),
|
|
558
|
+
inPoint: Math.floor((frame / 30) * 1000000),
|
|
559
|
+
inFrame: frame,
|
|
560
|
+
type: "文字",
|
|
561
|
+
textBasicInfo,
|
|
562
|
+
textColor: textColor,
|
|
563
|
+
text: textContent,
|
|
564
|
+
bgColor: bgColor,
|
|
565
|
+
visible: true
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
group.on("selected", (options: any) => {
|
|
569
|
+
options.target.isSelected = -1;
|
|
570
|
+
});
|
|
571
|
+
group.on("moving", (options: any) => {
|
|
572
|
+
options.transform.target.isSelected = 0;
|
|
573
|
+
});
|
|
574
|
+
group.on('modified', () => {
|
|
575
|
+
const fObj = convertToJSON(group)
|
|
576
|
+
vmmlConverterRef.current.updateClip(fObj);
|
|
577
|
+
});
|
|
578
|
+
canvas.centerObject(group);
|
|
579
|
+
canvas.add(group)
|
|
580
|
+
setTimeout(() => {
|
|
581
|
+
canvas.renderAll();
|
|
680
582
|
})
|
|
583
|
+
onVideoChange(group?.clipData??null);
|
|
584
|
+
vmmlConverterRef.current.addTextClip(convertToJSON(group));
|
|
585
|
+
resolve(true);
|
|
681
586
|
})
|
|
682
|
-
|
|
587
|
+
|
|
588
|
+
}
|
|
589
|
+
// 修改文字
|
|
683
590
|
const updateText = async ({ id, textContent, bgColor, textColor, textBasicInfo, fontAssetUrl }: any) => {
|
|
684
591
|
// 1. 找到目标对象
|
|
685
592
|
const target = fc.getObjects().find((item: any) => item.clipData?.id === id);
|
package/src/index.tsx
CHANGED
|
@@ -376,7 +376,7 @@ const EditorFn = <Schema extends AnyZodObject, Props>(
|
|
|
376
376
|
if (editableArray.length && vmmlState?.template) {
|
|
377
377
|
initCanEditClips(vmmlState.template.tracks);
|
|
378
378
|
}
|
|
379
|
-
}, [editableArray,
|
|
379
|
+
}, [editableArray, vmmlState]);
|
|
380
380
|
|
|
381
381
|
useEffect(() => {
|
|
382
382
|
if (vmmlState) {
|