@twick/canvas 0.15.13 → 0.15.15
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/dist/index.d.ts +24 -0
- package/dist/index.js +804 -245
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +805 -246
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { Canvas, Control, controlsUtils, FabricImage, Rect, Textbox, Shadow, Group, Circle, ActiveSelection } from "fabric";
|
|
2
5
|
import { useState, useRef } from "react";
|
|
3
6
|
const DEFAULT_TEXT_PROPS = {
|
|
4
7
|
/** Font family for text elements */
|
|
@@ -46,7 +49,13 @@ const CANVAS_OPERATIONS = {
|
|
|
46
49
|
/** Items have been ungrouped */
|
|
47
50
|
ITEM_UNGROUPED: "ITEM_UNGROUPED",
|
|
48
51
|
/** Caption properties have been updated */
|
|
49
|
-
CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED"
|
|
52
|
+
CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED",
|
|
53
|
+
/** Watermark has been updated */
|
|
54
|
+
WATERMARK_UPDATED: "WATERMARK_UPDATED",
|
|
55
|
+
/** A new element was added via drop on canvas; payload is { element } */
|
|
56
|
+
ADDED_NEW_ELEMENT: "ADDED_NEW_ELEMENT",
|
|
57
|
+
/** Z-order changed (bring to front / send to back). Payload is { elementId, direction }. Timeline should reorder tracks. */
|
|
58
|
+
Z_ORDER_CHANGED: "Z_ORDER_CHANGED"
|
|
50
59
|
};
|
|
51
60
|
const ELEMENT_TYPES = {
|
|
52
61
|
/** Text element type */
|
|
@@ -60,7 +69,11 @@ const ELEMENT_TYPES = {
|
|
|
60
69
|
/** Rectangle element type */
|
|
61
70
|
RECT: "rect",
|
|
62
71
|
/** Circle element type */
|
|
63
|
-
CIRCLE: "circle"
|
|
72
|
+
CIRCLE: "circle",
|
|
73
|
+
/** Icon element type */
|
|
74
|
+
ICON: "icon",
|
|
75
|
+
/** Background color element type */
|
|
76
|
+
BACKGROUND_COLOR: "backgroundColor"
|
|
64
77
|
};
|
|
65
78
|
const isBrowser = typeof window !== "undefined";
|
|
66
79
|
const isCanvasSupported = isBrowser && !!window.HTMLCanvasElement;
|
|
@@ -122,6 +135,26 @@ const createCanvas = ({
|
|
|
122
135
|
canvasMetadata
|
|
123
136
|
};
|
|
124
137
|
};
|
|
138
|
+
function measureTextWidth(text, options) {
|
|
139
|
+
if (typeof document === "undefined" || !text) return 0;
|
|
140
|
+
const canvas = document.createElement("canvas");
|
|
141
|
+
const ctx = canvas.getContext("2d");
|
|
142
|
+
if (!ctx) return 0;
|
|
143
|
+
const {
|
|
144
|
+
fontSize,
|
|
145
|
+
fontFamily,
|
|
146
|
+
fontStyle = "normal",
|
|
147
|
+
fontWeight = "normal"
|
|
148
|
+
} = options;
|
|
149
|
+
ctx.font = `${fontStyle} ${String(fontWeight)} ${fontSize}px ${fontFamily}`;
|
|
150
|
+
const lines = text.split("\n");
|
|
151
|
+
let maxWidth = 0;
|
|
152
|
+
for (const line of lines) {
|
|
153
|
+
const { width } = ctx.measureText(line);
|
|
154
|
+
if (width > maxWidth) maxWidth = width;
|
|
155
|
+
}
|
|
156
|
+
return Math.ceil(maxWidth);
|
|
157
|
+
}
|
|
125
158
|
const reorderElementsByZIndex = (canvas) => {
|
|
126
159
|
if (!canvas) return;
|
|
127
160
|
const backgroundColor = canvas.backgroundColor;
|
|
@@ -132,6 +165,49 @@ const reorderElementsByZIndex = (canvas) => {
|
|
|
132
165
|
objects.forEach((obj) => canvas.add(obj));
|
|
133
166
|
canvas.renderAll();
|
|
134
167
|
};
|
|
168
|
+
const changeZOrder = (canvas, elementId, direction) => {
|
|
169
|
+
var _a, _b;
|
|
170
|
+
if (!canvas) return null;
|
|
171
|
+
const objects = canvas.getObjects();
|
|
172
|
+
const sorted = [...objects].sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));
|
|
173
|
+
const idx = sorted.findIndex((obj2) => {
|
|
174
|
+
var _a2;
|
|
175
|
+
return ((_a2 = obj2.get) == null ? void 0 : _a2.call(obj2, "id")) === elementId;
|
|
176
|
+
});
|
|
177
|
+
if (idx < 0) return null;
|
|
178
|
+
const minZ = ((_a = sorted[0]) == null ? void 0 : _a.zIndex) ?? 0;
|
|
179
|
+
const maxZ = ((_b = sorted[sorted.length - 1]) == null ? void 0 : _b.zIndex) ?? 0;
|
|
180
|
+
const obj = sorted[idx];
|
|
181
|
+
if (direction === "front") {
|
|
182
|
+
obj.set("zIndex", maxZ + 1);
|
|
183
|
+
reorderElementsByZIndex(canvas);
|
|
184
|
+
return maxZ + 1;
|
|
185
|
+
}
|
|
186
|
+
if (direction === "back") {
|
|
187
|
+
obj.set("zIndex", minZ - 1);
|
|
188
|
+
reorderElementsByZIndex(canvas);
|
|
189
|
+
return minZ - 1;
|
|
190
|
+
}
|
|
191
|
+
if (direction === "forward" && idx < sorted.length - 1) {
|
|
192
|
+
const next = sorted[idx + 1];
|
|
193
|
+
const myZ = obj.zIndex ?? idx;
|
|
194
|
+
const nextZ = next.zIndex ?? idx + 1;
|
|
195
|
+
obj.set("zIndex", nextZ);
|
|
196
|
+
next.set("zIndex", myZ);
|
|
197
|
+
reorderElementsByZIndex(canvas);
|
|
198
|
+
return nextZ;
|
|
199
|
+
}
|
|
200
|
+
if (direction === "backward" && idx > 0) {
|
|
201
|
+
const prev = sorted[idx - 1];
|
|
202
|
+
const myZ = obj.zIndex ?? idx;
|
|
203
|
+
const prevZ = prev.zIndex ?? idx - 1;
|
|
204
|
+
obj.set("zIndex", prevZ);
|
|
205
|
+
prev.set("zIndex", myZ);
|
|
206
|
+
reorderElementsByZIndex(canvas);
|
|
207
|
+
return prevZ;
|
|
208
|
+
}
|
|
209
|
+
return obj.zIndex ?? idx;
|
|
210
|
+
};
|
|
135
211
|
const getCanvasContext = (canvas) => {
|
|
136
212
|
var _a, _b, _c, _d;
|
|
137
213
|
if (!canvas || !((_b = (_a = canvas.elements) == null ? void 0 : _a.lower) == null ? void 0 : _b.ctx)) return;
|
|
@@ -152,12 +228,31 @@ const convertToCanvasPosition = (x, y, canvasMetadata) => {
|
|
|
152
228
|
y: y * canvasMetadata.scaleY + canvasMetadata.height / 2
|
|
153
229
|
};
|
|
154
230
|
};
|
|
231
|
+
const getObjectCanvasCenter = (obj) => {
|
|
232
|
+
if (obj.getCenterPoint) {
|
|
233
|
+
const p = obj.getCenterPoint();
|
|
234
|
+
return { x: p.x, y: p.y };
|
|
235
|
+
}
|
|
236
|
+
return { x: obj.left ?? 0, y: obj.top ?? 0 };
|
|
237
|
+
};
|
|
238
|
+
const getObjectCanvasAngle = (obj) => {
|
|
239
|
+
if (typeof obj.getTotalAngle === "function") {
|
|
240
|
+
return obj.getTotalAngle();
|
|
241
|
+
}
|
|
242
|
+
return obj.angle ?? 0;
|
|
243
|
+
};
|
|
155
244
|
const convertToVideoPosition = (x, y, canvasMetadata, videoSize) => {
|
|
156
245
|
return {
|
|
157
246
|
x: Number((x / canvasMetadata.scaleX - videoSize.width / 2).toFixed(2)),
|
|
158
247
|
y: Number((y / canvasMetadata.scaleY - videoSize.height / 2).toFixed(2))
|
|
159
248
|
};
|
|
160
249
|
};
|
|
250
|
+
const convertToVideoDimensions = (widthCanvas, heightCanvas, canvasMetadata) => {
|
|
251
|
+
return {
|
|
252
|
+
width: Number((widthCanvas / canvasMetadata.scaleX).toFixed(2)),
|
|
253
|
+
height: Number((heightCanvas / canvasMetadata.scaleY).toFixed(2))
|
|
254
|
+
};
|
|
255
|
+
};
|
|
161
256
|
const getCurrentFrameEffect = (item, seekTime) => {
|
|
162
257
|
var _a;
|
|
163
258
|
let currentFrameEffect;
|
|
@@ -169,6 +264,17 @@ const getCurrentFrameEffect = (item, seekTime) => {
|
|
|
169
264
|
}
|
|
170
265
|
return currentFrameEffect;
|
|
171
266
|
};
|
|
267
|
+
const hexToRgba = (hex, alpha) => {
|
|
268
|
+
const color = hex.replace(/^#/, "");
|
|
269
|
+
const fullHex = color.length === 3 ? color.split("").map((c) => c + c).join("") : color;
|
|
270
|
+
if (fullHex.length !== 6) {
|
|
271
|
+
return hex;
|
|
272
|
+
}
|
|
273
|
+
const r = parseInt(fullHex.slice(0, 2), 16);
|
|
274
|
+
const g = parseInt(fullHex.slice(2, 4), 16);
|
|
275
|
+
const b = parseInt(fullHex.slice(4, 6), 16);
|
|
276
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
277
|
+
};
|
|
172
278
|
const disabledControl = new Control({
|
|
173
279
|
/** X position offset */
|
|
174
280
|
x: 0,
|
|
@@ -600,40 +706,66 @@ const addTextElement = ({
|
|
|
600
706
|
canvas,
|
|
601
707
|
canvasMetadata
|
|
602
708
|
}) => {
|
|
603
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
709
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
|
|
604
710
|
const { x, y } = convertToCanvasPosition(
|
|
605
711
|
((_a = element.props) == null ? void 0 : _a.x) || 0,
|
|
606
712
|
((_b = element.props) == null ? void 0 : _b.y) || 0,
|
|
607
713
|
canvasMetadata
|
|
608
714
|
);
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
715
|
+
const fontSize = Math.floor(
|
|
716
|
+
(((_c = element.props) == null ? void 0 : _c.fontSize) || DEFAULT_TEXT_PROPS.size) * canvasMetadata.scaleX
|
|
717
|
+
);
|
|
718
|
+
const fontFamily = ((_d = element.props) == null ? void 0 : _d.fontFamily) || DEFAULT_TEXT_PROPS.family;
|
|
719
|
+
const fontStyle = ((_e = element.props) == null ? void 0 : _e.fontStyle) || "normal";
|
|
720
|
+
const fontWeight = ((_f = element.props) == null ? void 0 : _f.fontWeight) || "normal";
|
|
721
|
+
let width;
|
|
722
|
+
if (((_g = element.props) == null ? void 0 : _g.width) != null && element.props.width > 0) {
|
|
723
|
+
width = element.props.width * canvasMetadata.scaleX;
|
|
724
|
+
if ((_h = element.props) == null ? void 0 : _h.maxWidth) {
|
|
725
|
+
width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);
|
|
726
|
+
}
|
|
727
|
+
} else {
|
|
728
|
+
const textContent = ((_i = element.props) == null ? void 0 : _i.text) ?? element.t ?? "";
|
|
729
|
+
width = measureTextWidth(textContent, {
|
|
730
|
+
fontSize,
|
|
731
|
+
fontFamily,
|
|
732
|
+
fontStyle,
|
|
733
|
+
fontWeight
|
|
734
|
+
});
|
|
735
|
+
const padding = 4;
|
|
736
|
+
width = width + padding * 2;
|
|
737
|
+
if ((_j = element.props) == null ? void 0 : _j.maxWidth) {
|
|
738
|
+
width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);
|
|
739
|
+
}
|
|
740
|
+
if (width <= 0) width = 100;
|
|
612
741
|
}
|
|
613
|
-
const
|
|
742
|
+
const backgroundColor = ((_k = element.props) == null ? void 0 : _k.backgroundColor) ? hexToRgba(
|
|
743
|
+
element.props.backgroundColor,
|
|
744
|
+
((_l = element.props) == null ? void 0 : _l.backgroundOpacity) ?? 1
|
|
745
|
+
) : void 0;
|
|
746
|
+
const text = new Textbox(((_m = element.props) == null ? void 0 : _m.text) || element.t || "", {
|
|
614
747
|
left: x,
|
|
615
748
|
top: y,
|
|
616
749
|
originX: "center",
|
|
617
750
|
originY: "center",
|
|
618
|
-
angle: ((
|
|
619
|
-
fontSize
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
fill: ((_k = element.props) == null ? void 0 : _k.fill) || DEFAULT_TEXT_PROPS.fill,
|
|
626
|
-
opacity: ((_l = element.props) == null ? void 0 : _l.opacity) ?? 1,
|
|
751
|
+
angle: ((_n = element.props) == null ? void 0 : _n.rotation) || 0,
|
|
752
|
+
fontSize,
|
|
753
|
+
fontFamily,
|
|
754
|
+
fontStyle,
|
|
755
|
+
fontWeight,
|
|
756
|
+
fill: ((_o = element.props) == null ? void 0 : _o.fill) || DEFAULT_TEXT_PROPS.fill,
|
|
757
|
+
opacity: ((_p = element.props) == null ? void 0 : _p.opacity) ?? 1,
|
|
627
758
|
width,
|
|
628
759
|
splitByGrapheme: false,
|
|
629
|
-
textAlign: ((
|
|
630
|
-
stroke: ((
|
|
631
|
-
strokeWidth: ((
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
760
|
+
textAlign: ((_q = element.props) == null ? void 0 : _q.textAlign) || "center",
|
|
761
|
+
stroke: ((_r = element.props) == null ? void 0 : _r.stroke) || DEFAULT_TEXT_PROPS.stroke,
|
|
762
|
+
strokeWidth: ((_s = element.props) == null ? void 0 : _s.lineWidth) || DEFAULT_TEXT_PROPS.lineWidth,
|
|
763
|
+
...backgroundColor && { backgroundColor },
|
|
764
|
+
shadow: ((_t = element.props) == null ? void 0 : _t.shadowColor) ? new Shadow({
|
|
765
|
+
offsetX: ((_v = (_u = element.props) == null ? void 0 : _u.shadowOffset) == null ? void 0 : _v.length) && ((_x = (_w = element.props) == null ? void 0 : _w.shadowOffset) == null ? void 0 : _x.length) > 1 ? element.props.shadowOffset[0] / 2 : 1,
|
|
766
|
+
offsetY: ((_z = (_y = element.props) == null ? void 0 : _y.shadowOffset) == null ? void 0 : _z.length) && ((_A = element.props) == null ? void 0 : _A.shadowOffset.length) > 1 ? element.props.shadowOffset[1] / 2 : 1,
|
|
767
|
+
blur: (((_B = element.props) == null ? void 0 : _B.shadowBlur) || 2) / 2,
|
|
768
|
+
color: (_C = element.props) == null ? void 0 : _C.shadowColor
|
|
637
769
|
}) : void 0
|
|
638
770
|
});
|
|
639
771
|
text.set("id", element.id);
|
|
@@ -654,7 +786,8 @@ const setImageProps = ({
|
|
|
654
786
|
img,
|
|
655
787
|
element,
|
|
656
788
|
index,
|
|
657
|
-
canvasMetadata
|
|
789
|
+
canvasMetadata,
|
|
790
|
+
lockAspectRatio = true
|
|
658
791
|
}) => {
|
|
659
792
|
var _a, _b, _c, _d, _e;
|
|
660
793
|
const width = (((_a = element.props) == null ? void 0 : _a.width) || 0) * canvasMetadata.scaleX || canvasMetadata.width;
|
|
@@ -674,13 +807,15 @@ const setImageProps = ({
|
|
|
674
807
|
img.set("selectable", true);
|
|
675
808
|
img.set("hasControls", true);
|
|
676
809
|
img.set("touchAction", "all");
|
|
810
|
+
img.set("lockUniScaling", lockAspectRatio);
|
|
677
811
|
};
|
|
678
812
|
const addCaptionElement = ({
|
|
679
813
|
element,
|
|
680
814
|
index,
|
|
681
815
|
canvas,
|
|
682
816
|
captionProps,
|
|
683
|
-
canvasMetadata
|
|
817
|
+
canvasMetadata,
|
|
818
|
+
lockAspectRatio = false
|
|
684
819
|
}) => {
|
|
685
820
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J;
|
|
686
821
|
const { x, y } = convertToCanvasPosition(
|
|
@@ -719,6 +854,7 @@ const addCaptionElement = ({
|
|
|
719
854
|
});
|
|
720
855
|
caption.set("id", element.id);
|
|
721
856
|
caption.set("zIndex", index);
|
|
857
|
+
caption.set("lockUniScaling", lockAspectRatio);
|
|
722
858
|
caption.controls.mt = disabledControl;
|
|
723
859
|
caption.controls.mb = disabledControl;
|
|
724
860
|
caption.controls.ml = disabledControl;
|
|
@@ -766,7 +902,8 @@ const addImageElement = async ({
|
|
|
766
902
|
index,
|
|
767
903
|
canvas,
|
|
768
904
|
canvasMetadata,
|
|
769
|
-
currentFrameEffect
|
|
905
|
+
currentFrameEffect,
|
|
906
|
+
lockAspectRatio = true
|
|
770
907
|
}) => {
|
|
771
908
|
try {
|
|
772
909
|
const img = await FabricImage.fromURL(imageUrl || element.props.src || "");
|
|
@@ -775,7 +912,7 @@ const addImageElement = async ({
|
|
|
775
912
|
originY: "center",
|
|
776
913
|
lockMovementX: false,
|
|
777
914
|
lockMovementY: false,
|
|
778
|
-
lockUniScaling:
|
|
915
|
+
lockUniScaling: lockAspectRatio,
|
|
779
916
|
hasControls: false,
|
|
780
917
|
selectable: false
|
|
781
918
|
});
|
|
@@ -786,10 +923,11 @@ const addImageElement = async ({
|
|
|
786
923
|
index,
|
|
787
924
|
canvas,
|
|
788
925
|
canvasMetadata,
|
|
789
|
-
currentFrameEffect
|
|
926
|
+
currentFrameEffect,
|
|
927
|
+
lockAspectRatio
|
|
790
928
|
});
|
|
791
929
|
} else {
|
|
792
|
-
setImageProps({ img, element, index, canvasMetadata });
|
|
930
|
+
setImageProps({ img, element, index, canvasMetadata, lockAspectRatio });
|
|
793
931
|
canvas.add(img);
|
|
794
932
|
return img;
|
|
795
933
|
}
|
|
@@ -802,7 +940,8 @@ const addMediaGroup = ({
|
|
|
802
940
|
index,
|
|
803
941
|
canvas,
|
|
804
942
|
canvasMetadata,
|
|
805
|
-
currentFrameEffect
|
|
943
|
+
currentFrameEffect,
|
|
944
|
+
lockAspectRatio = true
|
|
806
945
|
}) => {
|
|
807
946
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
808
947
|
let frameSize;
|
|
@@ -891,6 +1030,7 @@ const addMediaGroup = ({
|
|
|
891
1030
|
group.controls.mtr = rotateControl;
|
|
892
1031
|
group.set("id", element.id);
|
|
893
1032
|
group.set("zIndex", index);
|
|
1033
|
+
group.set("lockUniScaling", lockAspectRatio);
|
|
894
1034
|
canvas.add(group);
|
|
895
1035
|
return group;
|
|
896
1036
|
};
|
|
@@ -898,7 +1038,8 @@ const addRectElement = ({
|
|
|
898
1038
|
element,
|
|
899
1039
|
index,
|
|
900
1040
|
canvas,
|
|
901
|
-
canvasMetadata
|
|
1041
|
+
canvasMetadata,
|
|
1042
|
+
lockAspectRatio = false
|
|
902
1043
|
}) => {
|
|
903
1044
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
904
1045
|
const { x, y } = convertToCanvasPosition(
|
|
@@ -936,6 +1077,7 @@ const addRectElement = ({
|
|
|
936
1077
|
});
|
|
937
1078
|
rect.set("id", element.id);
|
|
938
1079
|
rect.set("zIndex", index);
|
|
1080
|
+
rect.set("lockUniScaling", lockAspectRatio);
|
|
939
1081
|
rect.controls.mtr = rotateControl;
|
|
940
1082
|
canvas.add(rect);
|
|
941
1083
|
return rect;
|
|
@@ -944,9 +1086,10 @@ const addCircleElement = ({
|
|
|
944
1086
|
element,
|
|
945
1087
|
index,
|
|
946
1088
|
canvas,
|
|
947
|
-
canvasMetadata
|
|
1089
|
+
canvasMetadata,
|
|
1090
|
+
lockAspectRatio = true
|
|
948
1091
|
}) => {
|
|
949
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1092
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
950
1093
|
const { x, y } = convertToCanvasPosition(
|
|
951
1094
|
((_a = element.props) == null ? void 0 : _a.x) || 0,
|
|
952
1095
|
((_b = element.props) == null ? void 0 : _b.y) || 0,
|
|
@@ -962,7 +1105,9 @@ const addCircleElement = ({
|
|
|
962
1105
|
stroke: ((_e = element.props) == null ? void 0 : _e.stroke) || "#000000",
|
|
963
1106
|
strokeWidth: (((_f = element.props) == null ? void 0 : _f.lineWidth) || 0) * canvasMetadata.scaleX,
|
|
964
1107
|
originX: "center",
|
|
965
|
-
originY: "center"
|
|
1108
|
+
originY: "center",
|
|
1109
|
+
// Respect element opacity (0–1). Defaults to fully opaque.
|
|
1110
|
+
opacity: ((_g = element.props) == null ? void 0 : _g.opacity) ?? 1
|
|
966
1111
|
});
|
|
967
1112
|
circle.controls.mt = disabledControl;
|
|
968
1113
|
circle.controls.mb = disabledControl;
|
|
@@ -971,6 +1116,7 @@ const addCircleElement = ({
|
|
|
971
1116
|
circle.controls.mtr = disabledControl;
|
|
972
1117
|
circle.set("id", element.id);
|
|
973
1118
|
circle.set("zIndex", index);
|
|
1119
|
+
circle.set("lockUniScaling", lockAspectRatio);
|
|
974
1120
|
canvas.add(circle);
|
|
975
1121
|
return circle;
|
|
976
1122
|
};
|
|
@@ -1005,17 +1151,462 @@ const addBackgroundColor = ({
|
|
|
1005
1151
|
canvas.add(bgRect);
|
|
1006
1152
|
return bgRect;
|
|
1007
1153
|
};
|
|
1154
|
+
const VideoElement = {
|
|
1155
|
+
name: ELEMENT_TYPES.VIDEO,
|
|
1156
|
+
async add(params) {
|
|
1157
|
+
var _a, _b;
|
|
1158
|
+
const {
|
|
1159
|
+
element,
|
|
1160
|
+
index,
|
|
1161
|
+
canvas,
|
|
1162
|
+
canvasMetadata,
|
|
1163
|
+
seekTime = 0,
|
|
1164
|
+
elementFrameMapRef,
|
|
1165
|
+
getCurrentFrameEffect: getFrameEffect
|
|
1166
|
+
} = params;
|
|
1167
|
+
if (!getFrameEffect || !elementFrameMapRef) return;
|
|
1168
|
+
const currentFrameEffect = getFrameEffect(element, seekTime);
|
|
1169
|
+
elementFrameMapRef.current[element.id] = currentFrameEffect;
|
|
1170
|
+
const snapTime = (seekTime - ((element == null ? void 0 : element.s) ?? 0)) * (((_a = element == null ? void 0 : element.props) == null ? void 0 : _a.playbackRate) ?? 1) + (((_b = element == null ? void 0 : element.props) == null ? void 0 : _b.time) ?? 0);
|
|
1171
|
+
await addVideoElement({
|
|
1172
|
+
element,
|
|
1173
|
+
index,
|
|
1174
|
+
canvas,
|
|
1175
|
+
canvasMetadata,
|
|
1176
|
+
currentFrameEffect,
|
|
1177
|
+
snapTime
|
|
1178
|
+
});
|
|
1179
|
+
if (element.timelineType === "scene") {
|
|
1180
|
+
await addBackgroundColor({
|
|
1181
|
+
element,
|
|
1182
|
+
index,
|
|
1183
|
+
canvas,
|
|
1184
|
+
canvasMetadata
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
},
|
|
1188
|
+
updateFromFabricObject(object, element, context) {
|
|
1189
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
1190
|
+
const { x, y } = convertToVideoPosition(
|
|
1191
|
+
canvasCenter.x,
|
|
1192
|
+
canvasCenter.y,
|
|
1193
|
+
context.canvasMetadata,
|
|
1194
|
+
context.videoSize
|
|
1195
|
+
);
|
|
1196
|
+
const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
1197
|
+
const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
1198
|
+
const { width: fw, height: fh } = convertToVideoDimensions(
|
|
1199
|
+
scaledW,
|
|
1200
|
+
scaledH,
|
|
1201
|
+
context.canvasMetadata
|
|
1202
|
+
);
|
|
1203
|
+
const updatedFrameSize = [fw, fh];
|
|
1204
|
+
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
1205
|
+
if (currentFrameEffect) {
|
|
1206
|
+
context.elementFrameMapRef.current[element.id] = {
|
|
1207
|
+
...currentFrameEffect,
|
|
1208
|
+
props: {
|
|
1209
|
+
...currentFrameEffect.props,
|
|
1210
|
+
framePosition: { x, y },
|
|
1211
|
+
frameSize: updatedFrameSize
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
return {
|
|
1215
|
+
element: {
|
|
1216
|
+
...element,
|
|
1217
|
+
frameEffects: (element.frameEffects || []).map(
|
|
1218
|
+
(fe) => fe.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
1219
|
+
...fe,
|
|
1220
|
+
props: {
|
|
1221
|
+
...fe.props,
|
|
1222
|
+
framePosition: { x, y },
|
|
1223
|
+
frameSize: updatedFrameSize
|
|
1224
|
+
}
|
|
1225
|
+
} : fe
|
|
1226
|
+
)
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
const frame = element.frame;
|
|
1231
|
+
return {
|
|
1232
|
+
element: {
|
|
1233
|
+
...element,
|
|
1234
|
+
frame: {
|
|
1235
|
+
...frame,
|
|
1236
|
+
rotation: getObjectCanvasAngle(object),
|
|
1237
|
+
size: updatedFrameSize,
|
|
1238
|
+
x,
|
|
1239
|
+
y
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
const ImageElement = {
|
|
1246
|
+
name: ELEMENT_TYPES.IMAGE,
|
|
1247
|
+
async add(params) {
|
|
1248
|
+
var _a;
|
|
1249
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
1250
|
+
await addImageElement({
|
|
1251
|
+
element,
|
|
1252
|
+
index,
|
|
1253
|
+
canvas,
|
|
1254
|
+
canvasMetadata,
|
|
1255
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
1256
|
+
});
|
|
1257
|
+
if (element.timelineType === "scene") {
|
|
1258
|
+
await addBackgroundColor({
|
|
1259
|
+
element,
|
|
1260
|
+
index,
|
|
1261
|
+
canvas,
|
|
1262
|
+
canvasMetadata
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
updateFromFabricObject(object, element, context) {
|
|
1267
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
1268
|
+
const { x, y } = convertToVideoPosition(
|
|
1269
|
+
canvasCenter.x,
|
|
1270
|
+
canvasCenter.y,
|
|
1271
|
+
context.canvasMetadata,
|
|
1272
|
+
context.videoSize
|
|
1273
|
+
);
|
|
1274
|
+
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
1275
|
+
if (object.type === "group") {
|
|
1276
|
+
const scaledW2 = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
1277
|
+
const scaledH2 = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
1278
|
+
const { width: fw, height: fh } = convertToVideoDimensions(
|
|
1279
|
+
scaledW2,
|
|
1280
|
+
scaledH2,
|
|
1281
|
+
context.canvasMetadata
|
|
1282
|
+
);
|
|
1283
|
+
const updatedFrameSize = [fw, fh];
|
|
1284
|
+
if (currentFrameEffect) {
|
|
1285
|
+
context.elementFrameMapRef.current[element.id] = {
|
|
1286
|
+
...currentFrameEffect,
|
|
1287
|
+
props: {
|
|
1288
|
+
...currentFrameEffect.props,
|
|
1289
|
+
framePosition: { x, y },
|
|
1290
|
+
frameSize: updatedFrameSize
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
return {
|
|
1294
|
+
element: {
|
|
1295
|
+
...element,
|
|
1296
|
+
// Keep the base frame in sync with the active frame effect
|
|
1297
|
+
// so visualizer `Rect {...element.frame}` reflects the same size/position.
|
|
1298
|
+
frame: element.frame ? {
|
|
1299
|
+
...element.frame,
|
|
1300
|
+
rotation: getObjectCanvasAngle(object),
|
|
1301
|
+
size: updatedFrameSize,
|
|
1302
|
+
x,
|
|
1303
|
+
y
|
|
1304
|
+
} : element.frame,
|
|
1305
|
+
frameEffects: (element.frameEffects || []).map(
|
|
1306
|
+
(fe) => fe.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
1307
|
+
...fe,
|
|
1308
|
+
props: {
|
|
1309
|
+
...fe.props,
|
|
1310
|
+
framePosition: { x, y },
|
|
1311
|
+
frameSize: updatedFrameSize
|
|
1312
|
+
}
|
|
1313
|
+
} : fe
|
|
1314
|
+
)
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
const frame = element.frame;
|
|
1319
|
+
return {
|
|
1320
|
+
element: {
|
|
1321
|
+
...element,
|
|
1322
|
+
frame: {
|
|
1323
|
+
...frame,
|
|
1324
|
+
rotation: getObjectCanvasAngle(object),
|
|
1325
|
+
size: updatedFrameSize,
|
|
1326
|
+
x,
|
|
1327
|
+
y
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);
|
|
1333
|
+
const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);
|
|
1334
|
+
const { width, height } = convertToVideoDimensions(
|
|
1335
|
+
scaledW,
|
|
1336
|
+
scaledH,
|
|
1337
|
+
context.canvasMetadata
|
|
1338
|
+
);
|
|
1339
|
+
return {
|
|
1340
|
+
element: {
|
|
1341
|
+
...element,
|
|
1342
|
+
props: {
|
|
1343
|
+
...element.props,
|
|
1344
|
+
rotation: getObjectCanvasAngle(object),
|
|
1345
|
+
width,
|
|
1346
|
+
height,
|
|
1347
|
+
x,
|
|
1348
|
+
y
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
const RectElement = {
|
|
1355
|
+
name: ELEMENT_TYPES.RECT,
|
|
1356
|
+
async add(params) {
|
|
1357
|
+
var _a;
|
|
1358
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
1359
|
+
await addRectElement({
|
|
1360
|
+
element,
|
|
1361
|
+
index,
|
|
1362
|
+
canvas,
|
|
1363
|
+
canvasMetadata,
|
|
1364
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
1365
|
+
});
|
|
1366
|
+
},
|
|
1367
|
+
updateFromFabricObject(object, element, context) {
|
|
1368
|
+
var _a, _b;
|
|
1369
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
1370
|
+
const { x, y } = convertToVideoPosition(
|
|
1371
|
+
canvasCenter.x,
|
|
1372
|
+
canvasCenter.y,
|
|
1373
|
+
context.canvasMetadata,
|
|
1374
|
+
context.videoSize
|
|
1375
|
+
);
|
|
1376
|
+
return {
|
|
1377
|
+
element: {
|
|
1378
|
+
...element,
|
|
1379
|
+
props: {
|
|
1380
|
+
...element.props,
|
|
1381
|
+
rotation: getObjectCanvasAngle(object),
|
|
1382
|
+
width: (((_a = element.props) == null ? void 0 : _a.width) ?? 0) * object.scaleX,
|
|
1383
|
+
height: (((_b = element.props) == null ? void 0 : _b.height) ?? 0) * object.scaleY,
|
|
1384
|
+
x,
|
|
1385
|
+
y
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
const CircleElement = {
|
|
1392
|
+
name: ELEMENT_TYPES.CIRCLE,
|
|
1393
|
+
async add(params) {
|
|
1394
|
+
var _a;
|
|
1395
|
+
const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;
|
|
1396
|
+
await addCircleElement({
|
|
1397
|
+
element,
|
|
1398
|
+
index,
|
|
1399
|
+
canvas,
|
|
1400
|
+
canvasMetadata,
|
|
1401
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
1402
|
+
});
|
|
1403
|
+
},
|
|
1404
|
+
updateFromFabricObject(object, element, context) {
|
|
1405
|
+
var _a, _b;
|
|
1406
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
1407
|
+
const { x, y } = convertToVideoPosition(
|
|
1408
|
+
canvasCenter.x,
|
|
1409
|
+
canvasCenter.y,
|
|
1410
|
+
context.canvasMetadata,
|
|
1411
|
+
context.videoSize
|
|
1412
|
+
);
|
|
1413
|
+
const radius = Number(
|
|
1414
|
+
((((_a = element.props) == null ? void 0 : _a.radius) ?? 0) * object.scaleX).toFixed(2)
|
|
1415
|
+
);
|
|
1416
|
+
const opacity = object.opacity != null ? object.opacity : (_b = element.props) == null ? void 0 : _b.opacity;
|
|
1417
|
+
return {
|
|
1418
|
+
element: {
|
|
1419
|
+
...element,
|
|
1420
|
+
props: {
|
|
1421
|
+
...element.props,
|
|
1422
|
+
rotation: getObjectCanvasAngle(object),
|
|
1423
|
+
radius,
|
|
1424
|
+
height: radius * 2,
|
|
1425
|
+
width: radius * 2,
|
|
1426
|
+
x,
|
|
1427
|
+
y,
|
|
1428
|
+
...opacity != null && { opacity }
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
const TextElement = {
|
|
1435
|
+
name: ELEMENT_TYPES.TEXT,
|
|
1436
|
+
async add(params) {
|
|
1437
|
+
const { element, index, canvas, canvasMetadata } = params;
|
|
1438
|
+
await addTextElement({
|
|
1439
|
+
element,
|
|
1440
|
+
index,
|
|
1441
|
+
canvas,
|
|
1442
|
+
canvasMetadata
|
|
1443
|
+
});
|
|
1444
|
+
},
|
|
1445
|
+
updateFromFabricObject(object, element, context) {
|
|
1446
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
1447
|
+
const { x, y } = convertToVideoPosition(
|
|
1448
|
+
canvasCenter.x,
|
|
1449
|
+
canvasCenter.y,
|
|
1450
|
+
context.canvasMetadata,
|
|
1451
|
+
context.videoSize
|
|
1452
|
+
);
|
|
1453
|
+
return {
|
|
1454
|
+
element: {
|
|
1455
|
+
...element,
|
|
1456
|
+
props: {
|
|
1457
|
+
...element.props,
|
|
1458
|
+
rotation: getObjectCanvasAngle(object),
|
|
1459
|
+
x,
|
|
1460
|
+
y
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1466
|
+
const CaptionElement = {
|
|
1467
|
+
name: ELEMENT_TYPES.CAPTION,
|
|
1468
|
+
async add(params) {
|
|
1469
|
+
var _a;
|
|
1470
|
+
const { element, index, canvas, captionProps, canvasMetadata, lockAspectRatio } = params;
|
|
1471
|
+
await addCaptionElement({
|
|
1472
|
+
element,
|
|
1473
|
+
index,
|
|
1474
|
+
canvas,
|
|
1475
|
+
captionProps: captionProps ?? {},
|
|
1476
|
+
canvasMetadata,
|
|
1477
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
1478
|
+
});
|
|
1479
|
+
},
|
|
1480
|
+
updateFromFabricObject(object, element, context) {
|
|
1481
|
+
var _a;
|
|
1482
|
+
const canvasCenter = getObjectCanvasCenter(object);
|
|
1483
|
+
const { x, y } = convertToVideoPosition(
|
|
1484
|
+
canvasCenter.x,
|
|
1485
|
+
canvasCenter.y,
|
|
1486
|
+
context.canvasMetadata,
|
|
1487
|
+
context.videoSize
|
|
1488
|
+
);
|
|
1489
|
+
if ((_a = context.captionPropsRef.current) == null ? void 0 : _a.applyToAll) {
|
|
1490
|
+
return {
|
|
1491
|
+
element,
|
|
1492
|
+
operation: CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED,
|
|
1493
|
+
payload: {
|
|
1494
|
+
element,
|
|
1495
|
+
props: {
|
|
1496
|
+
...context.captionPropsRef.current,
|
|
1497
|
+
x,
|
|
1498
|
+
y
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
return {
|
|
1504
|
+
element: {
|
|
1505
|
+
...element,
|
|
1506
|
+
props: {
|
|
1507
|
+
...element.props,
|
|
1508
|
+
x,
|
|
1509
|
+
y
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const WatermarkElement = {
|
|
1516
|
+
name: "watermark",
|
|
1517
|
+
async add(params) {
|
|
1518
|
+
const { element, index, canvas, canvasMetadata, watermarkPropsRef } = params;
|
|
1519
|
+
if (element.type === ELEMENT_TYPES.TEXT) {
|
|
1520
|
+
if (watermarkPropsRef) watermarkPropsRef.current = element.props;
|
|
1521
|
+
await addTextElement({
|
|
1522
|
+
element,
|
|
1523
|
+
index,
|
|
1524
|
+
canvas,
|
|
1525
|
+
canvasMetadata
|
|
1526
|
+
});
|
|
1527
|
+
} else if (element.type === ELEMENT_TYPES.IMAGE) {
|
|
1528
|
+
await addImageElement({
|
|
1529
|
+
element,
|
|
1530
|
+
index,
|
|
1531
|
+
canvas,
|
|
1532
|
+
canvasMetadata
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
},
|
|
1536
|
+
updateFromFabricObject(object, element, context) {
|
|
1537
|
+
const { x, y } = convertToVideoPosition(
|
|
1538
|
+
object.left,
|
|
1539
|
+
object.top,
|
|
1540
|
+
context.canvasMetadata,
|
|
1541
|
+
context.videoSize
|
|
1542
|
+
);
|
|
1543
|
+
const rotation = object.angle != null ? object.angle : void 0;
|
|
1544
|
+
const opacity = object.opacity != null ? object.opacity : void 0;
|
|
1545
|
+
const baseProps = element.type === ELEMENT_TYPES.TEXT ? context.watermarkPropsRef.current ?? element.props ?? {} : { ...element.props };
|
|
1546
|
+
const props = element.type === ELEMENT_TYPES.IMAGE && (object.scaleX != null || object.scaleY != null) ? {
|
|
1547
|
+
...baseProps,
|
|
1548
|
+
width: baseProps.width != null && object.scaleX != null ? baseProps.width * object.scaleX : baseProps.width,
|
|
1549
|
+
height: baseProps.height != null && object.scaleY != null ? baseProps.height * object.scaleY : baseProps.height
|
|
1550
|
+
} : baseProps;
|
|
1551
|
+
const payload = {
|
|
1552
|
+
position: { x, y },
|
|
1553
|
+
...rotation != null && { rotation },
|
|
1554
|
+
...opacity != null && { opacity },
|
|
1555
|
+
...Object.keys(props).length > 0 && { props }
|
|
1556
|
+
};
|
|
1557
|
+
return {
|
|
1558
|
+
element: { ...element, props: { ...element.props, x, y, rotation, opacity, ...props } },
|
|
1559
|
+
operation: CANVAS_OPERATIONS.WATERMARK_UPDATED,
|
|
1560
|
+
payload
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
class ElementController {
|
|
1565
|
+
constructor() {
|
|
1566
|
+
__publicField(this, "elements", /* @__PURE__ */ new Map());
|
|
1567
|
+
}
|
|
1568
|
+
register(handler) {
|
|
1569
|
+
this.elements.set(handler.name, handler);
|
|
1570
|
+
}
|
|
1571
|
+
get(name) {
|
|
1572
|
+
return this.elements.get(name);
|
|
1573
|
+
}
|
|
1574
|
+
list() {
|
|
1575
|
+
return Array.from(this.elements.keys());
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
const elementController = new ElementController();
|
|
1579
|
+
function registerElements() {
|
|
1580
|
+
elementController.register(VideoElement);
|
|
1581
|
+
elementController.register(ImageElement);
|
|
1582
|
+
elementController.register(RectElement);
|
|
1583
|
+
elementController.register(CircleElement);
|
|
1584
|
+
elementController.register(TextElement);
|
|
1585
|
+
elementController.register(CaptionElement);
|
|
1586
|
+
elementController.register(WatermarkElement);
|
|
1587
|
+
}
|
|
1588
|
+
registerElements();
|
|
1008
1589
|
const useTwickCanvas = ({
|
|
1009
1590
|
onCanvasReady,
|
|
1010
|
-
onCanvasOperation
|
|
1591
|
+
onCanvasOperation,
|
|
1592
|
+
/**
|
|
1593
|
+
* When true, holding Shift while dragging an object will lock movement to
|
|
1594
|
+
* the dominant axis (horizontal or vertical). This mirrors behavior in
|
|
1595
|
+
* professional editors and improves precise alignment.
|
|
1596
|
+
*
|
|
1597
|
+
* Default: false (opt‑in to avoid surprising existing consumers).
|
|
1598
|
+
*/
|
|
1599
|
+
enableShiftAxisLock = false
|
|
1011
1600
|
}) => {
|
|
1012
1601
|
const [twickCanvas, setTwickCanvas] = useState(null);
|
|
1013
1602
|
const elementMap = useRef({});
|
|
1603
|
+
const watermarkPropsRef = useRef(null);
|
|
1014
1604
|
const elementFrameMap = useRef({});
|
|
1015
1605
|
const twickCanvasRef = useRef(null);
|
|
1016
1606
|
const videoSizeRef = useRef({ width: 1, height: 1 });
|
|
1017
1607
|
const canvasResolutionRef = useRef({ width: 1, height: 1 });
|
|
1018
1608
|
const captionPropsRef = useRef(null);
|
|
1609
|
+
const axisLockStateRef = useRef(null);
|
|
1019
1610
|
const canvasMetadataRef = useRef({
|
|
1020
1611
|
width: 0,
|
|
1021
1612
|
height: 0,
|
|
@@ -1030,6 +1621,57 @@ const useTwickCanvas = ({
|
|
|
1030
1621
|
canvasMetadataRef.current.scaleY = canvasMetadataRef.current.height / videoSize.height;
|
|
1031
1622
|
}
|
|
1032
1623
|
};
|
|
1624
|
+
const handleObjectMoving = (event) => {
|
|
1625
|
+
var _a;
|
|
1626
|
+
if (!enableShiftAxisLock) return;
|
|
1627
|
+
const target = event == null ? void 0 : event.target;
|
|
1628
|
+
const transform = event == null ? void 0 : event.transform;
|
|
1629
|
+
const pointerEvent = event == null ? void 0 : event.e;
|
|
1630
|
+
if (!target || !transform || !pointerEvent) {
|
|
1631
|
+
axisLockStateRef.current = null;
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
if (!pointerEvent.shiftKey) {
|
|
1635
|
+
axisLockStateRef.current = null;
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
const original = transform.original;
|
|
1639
|
+
if (!original || typeof target.left !== "number" || typeof target.top !== "number") {
|
|
1640
|
+
axisLockStateRef.current = null;
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
if (!axisLockStateRef.current) {
|
|
1644
|
+
const dx = Math.abs(target.left - original.left);
|
|
1645
|
+
const dy = Math.abs(target.top - original.top);
|
|
1646
|
+
axisLockStateRef.current = {
|
|
1647
|
+
axis: dx >= dy ? "x" : "y"
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
if (axisLockStateRef.current.axis === "x") {
|
|
1651
|
+
target.top = original.top;
|
|
1652
|
+
} else {
|
|
1653
|
+
target.left = original.left;
|
|
1654
|
+
}
|
|
1655
|
+
(_a = target.canvas) == null ? void 0 : _a.requestRenderAll();
|
|
1656
|
+
};
|
|
1657
|
+
const applyMarqueeSelectionControls = () => {
|
|
1658
|
+
const canvasInstance = twickCanvasRef.current;
|
|
1659
|
+
if (!canvasInstance) return;
|
|
1660
|
+
const activeObject = canvasInstance.getActiveObject();
|
|
1661
|
+
if (!activeObject) return;
|
|
1662
|
+
if (activeObject instanceof ActiveSelection) {
|
|
1663
|
+
activeObject.controls.mt = disabledControl;
|
|
1664
|
+
activeObject.controls.mb = disabledControl;
|
|
1665
|
+
activeObject.controls.ml = disabledControl;
|
|
1666
|
+
activeObject.controls.mr = disabledControl;
|
|
1667
|
+
activeObject.controls.bl = disabledControl;
|
|
1668
|
+
activeObject.controls.br = disabledControl;
|
|
1669
|
+
activeObject.controls.tl = disabledControl;
|
|
1670
|
+
activeObject.controls.tr = disabledControl;
|
|
1671
|
+
activeObject.controls.mtr = rotateControl;
|
|
1672
|
+
canvasInstance.requestRenderAll();
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1033
1675
|
const buildCanvas = ({
|
|
1034
1676
|
videoSize,
|
|
1035
1677
|
canvasSize,
|
|
@@ -1049,6 +1691,9 @@ const useTwickCanvas = ({
|
|
|
1049
1691
|
if (twickCanvasRef.current) {
|
|
1050
1692
|
twickCanvasRef.current.off("mouse:up", handleMouseUp);
|
|
1051
1693
|
twickCanvasRef.current.off("text:editing:exited", onTextEdit);
|
|
1694
|
+
twickCanvasRef.current.off("object:moving", handleObjectMoving);
|
|
1695
|
+
twickCanvasRef.current.off("selection:created", applyMarqueeSelectionControls);
|
|
1696
|
+
twickCanvasRef.current.off("selection:updated", applyMarqueeSelectionControls);
|
|
1052
1697
|
twickCanvasRef.current.dispose();
|
|
1053
1698
|
}
|
|
1054
1699
|
const { canvas, canvasMetadata } = createCanvas({
|
|
@@ -1066,6 +1711,9 @@ const useTwickCanvas = ({
|
|
|
1066
1711
|
videoSizeRef.current = videoSize;
|
|
1067
1712
|
canvas == null ? void 0 : canvas.on("mouse:up", handleMouseUp);
|
|
1068
1713
|
canvas == null ? void 0 : canvas.on("text:editing:exited", onTextEdit);
|
|
1714
|
+
canvas == null ? void 0 : canvas.on("object:moving", handleObjectMoving);
|
|
1715
|
+
canvas == null ? void 0 : canvas.on("selection:created", applyMarqueeSelectionControls);
|
|
1716
|
+
canvas == null ? void 0 : canvas.on("selection:updated", applyMarqueeSelectionControls);
|
|
1069
1717
|
canvasResolutionRef.current = canvasSize;
|
|
1070
1718
|
setTwickCanvas(canvas);
|
|
1071
1719
|
twickCanvasRef.current = canvas;
|
|
@@ -1095,7 +1743,8 @@ const useTwickCanvas = ({
|
|
|
1095
1743
|
if (event.target) {
|
|
1096
1744
|
const object = event.target;
|
|
1097
1745
|
const elementId = object.get("id");
|
|
1098
|
-
|
|
1746
|
+
const action = (_a = event.transform) == null ? void 0 : _a.action;
|
|
1747
|
+
if (action === "drag") {
|
|
1099
1748
|
const original = event.transform.original;
|
|
1100
1749
|
if (object.left === original.left && object.top === original.top) {
|
|
1101
1750
|
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
@@ -1105,149 +1754,67 @@ const useTwickCanvas = ({
|
|
|
1105
1754
|
return;
|
|
1106
1755
|
}
|
|
1107
1756
|
}
|
|
1108
|
-
|
|
1757
|
+
const context = {
|
|
1758
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
1759
|
+
videoSize: videoSizeRef.current,
|
|
1760
|
+
elementFrameMapRef: elementFrameMap,
|
|
1761
|
+
captionPropsRef,
|
|
1762
|
+
watermarkPropsRef
|
|
1763
|
+
};
|
|
1764
|
+
if (object instanceof ActiveSelection && (action === "drag" || action === "rotate")) {
|
|
1765
|
+
const objects = object.getObjects();
|
|
1766
|
+
for (const fabricObj of objects) {
|
|
1767
|
+
const id = fabricObj.get("id");
|
|
1768
|
+
if (!id || id === "e-watermark") continue;
|
|
1769
|
+
const currentElement = elementMap.current[id];
|
|
1770
|
+
if (!currentElement) continue;
|
|
1771
|
+
const handler = elementController.get(currentElement.type);
|
|
1772
|
+
const result = (_b = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _b.call(
|
|
1773
|
+
handler,
|
|
1774
|
+
fabricObj,
|
|
1775
|
+
currentElement,
|
|
1776
|
+
context
|
|
1777
|
+
);
|
|
1778
|
+
if (result) {
|
|
1779
|
+
elementMap.current[id] = result.element;
|
|
1780
|
+
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
1781
|
+
result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
1782
|
+
result.payload ?? result.element
|
|
1783
|
+
);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
switch (action) {
|
|
1109
1789
|
case "drag":
|
|
1110
1790
|
case "scale":
|
|
1111
1791
|
case "scaleX":
|
|
1112
1792
|
case "scaleY":
|
|
1113
|
-
case "rotate":
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
canvasMetadataRef.current,
|
|
1118
|
-
videoSizeRef.current
|
|
1793
|
+
case "rotate": {
|
|
1794
|
+
const currentElement = elementMap.current[elementId];
|
|
1795
|
+
const handler = elementController.get(
|
|
1796
|
+
elementId === "e-watermark" ? "watermark" : currentElement == null ? void 0 : currentElement.type
|
|
1119
1797
|
);
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
element: elementMap.current[elementId],
|
|
1124
|
-
props: {
|
|
1125
|
-
...captionPropsRef.current,
|
|
1126
|
-
x,
|
|
1127
|
-
y
|
|
1128
|
-
}
|
|
1129
|
-
});
|
|
1130
|
-
} else {
|
|
1131
|
-
elementMap.current[elementId] = {
|
|
1132
|
-
...elementMap.current[elementId],
|
|
1133
|
-
props: {
|
|
1134
|
-
...elementMap.current[elementId].props,
|
|
1135
|
-
x,
|
|
1136
|
-
y
|
|
1137
|
-
}
|
|
1138
|
-
};
|
|
1139
|
-
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
1140
|
-
CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
1141
|
-
elementMap.current[elementId]
|
|
1142
|
-
);
|
|
1143
|
-
}
|
|
1144
|
-
} else {
|
|
1145
|
-
if ((object == null ? void 0 : object.type) === "group") {
|
|
1146
|
-
const currentFrameEffect = elementFrameMap.current[elementId];
|
|
1147
|
-
let updatedFrameSize;
|
|
1148
|
-
if (currentFrameEffect) {
|
|
1149
|
-
updatedFrameSize = [
|
|
1150
|
-
currentFrameEffect.props.frameSize[0] * object.scaleX,
|
|
1151
|
-
currentFrameEffect.props.frameSize[1] * object.scaleY
|
|
1152
|
-
];
|
|
1153
|
-
} else {
|
|
1154
|
-
updatedFrameSize = [
|
|
1155
|
-
elementMap.current[elementId].frame.size[0] * object.scaleX,
|
|
1156
|
-
elementMap.current[elementId].frame.size[1] * object.scaleY
|
|
1157
|
-
];
|
|
1158
|
-
}
|
|
1159
|
-
if (currentFrameEffect) {
|
|
1160
|
-
elementMap.current[elementId] = {
|
|
1161
|
-
...elementMap.current[elementId],
|
|
1162
|
-
frameEffects: (elementMap.current[elementId].frameEffects || []).map(
|
|
1163
|
-
(frameEffect) => frameEffect.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
1164
|
-
...frameEffect,
|
|
1165
|
-
props: {
|
|
1166
|
-
...frameEffect.props,
|
|
1167
|
-
framePosition: {
|
|
1168
|
-
x,
|
|
1169
|
-
y
|
|
1170
|
-
},
|
|
1171
|
-
frameSize: updatedFrameSize
|
|
1172
|
-
}
|
|
1173
|
-
} : frameEffect
|
|
1174
|
-
)
|
|
1175
|
-
};
|
|
1176
|
-
elementFrameMap.current[elementId] = {
|
|
1177
|
-
...elementFrameMap.current[elementId],
|
|
1178
|
-
framePosition: {
|
|
1179
|
-
x,
|
|
1180
|
-
y
|
|
1181
|
-
},
|
|
1182
|
-
frameSize: updatedFrameSize
|
|
1183
|
-
};
|
|
1184
|
-
} else {
|
|
1185
|
-
elementMap.current[elementId] = {
|
|
1186
|
-
...elementMap.current[elementId],
|
|
1187
|
-
frame: {
|
|
1188
|
-
...elementMap.current[elementId].frame,
|
|
1189
|
-
rotation: object.angle,
|
|
1190
|
-
size: updatedFrameSize,
|
|
1191
|
-
x,
|
|
1192
|
-
y
|
|
1193
|
-
}
|
|
1194
|
-
};
|
|
1195
|
-
}
|
|
1196
|
-
} else {
|
|
1197
|
-
if ((object == null ? void 0 : object.type) === "text") {
|
|
1198
|
-
elementMap.current[elementId] = {
|
|
1199
|
-
...elementMap.current[elementId],
|
|
1200
|
-
props: {
|
|
1201
|
-
...elementMap.current[elementId].props,
|
|
1202
|
-
rotation: object.angle,
|
|
1203
|
-
x,
|
|
1204
|
-
y
|
|
1205
|
-
}
|
|
1206
|
-
};
|
|
1207
|
-
} else if ((object == null ? void 0 : object.type) === "circle") {
|
|
1208
|
-
const radius = Number(
|
|
1209
|
-
(elementMap.current[elementId].props.radius * object.scaleX).toFixed(2)
|
|
1210
|
-
);
|
|
1211
|
-
elementMap.current[elementId] = {
|
|
1212
|
-
...elementMap.current[elementId],
|
|
1213
|
-
props: {
|
|
1214
|
-
...elementMap.current[elementId].props,
|
|
1215
|
-
rotation: object.angle,
|
|
1216
|
-
radius,
|
|
1217
|
-
height: radius * 2,
|
|
1218
|
-
width: radius * 2,
|
|
1219
|
-
x,
|
|
1220
|
-
y
|
|
1221
|
-
}
|
|
1222
|
-
};
|
|
1223
|
-
} else {
|
|
1224
|
-
elementMap.current[elementId] = {
|
|
1225
|
-
...elementMap.current[elementId],
|
|
1226
|
-
props: {
|
|
1227
|
-
...elementMap.current[elementId].props,
|
|
1228
|
-
rotation: object.angle,
|
|
1229
|
-
width: elementMap.current[elementId].props.width * object.scaleX,
|
|
1230
|
-
height: elementMap.current[elementId].props.height * object.scaleY,
|
|
1231
|
-
x,
|
|
1232
|
-
y
|
|
1233
|
-
}
|
|
1234
|
-
};
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1798
|
+
const result = (_c = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _c.call(handler, object, currentElement ?? { id: elementId, type: "text", props: {} }, context);
|
|
1799
|
+
if (result) {
|
|
1800
|
+
elementMap.current[elementId] = result.element;
|
|
1237
1801
|
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
1238
|
-
CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
1239
|
-
|
|
1802
|
+
result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
1803
|
+
result.payload ?? result.element
|
|
1240
1804
|
);
|
|
1241
1805
|
}
|
|
1242
1806
|
break;
|
|
1807
|
+
}
|
|
1243
1808
|
}
|
|
1244
1809
|
}
|
|
1245
1810
|
};
|
|
1246
1811
|
const setCanvasElements = async ({
|
|
1247
1812
|
elements,
|
|
1813
|
+
watermark,
|
|
1248
1814
|
seekTime = 0,
|
|
1249
1815
|
captionProps,
|
|
1250
|
-
cleanAndAdd = false
|
|
1816
|
+
cleanAndAdd = false,
|
|
1817
|
+
lockAspectRatio
|
|
1251
1818
|
}) => {
|
|
1252
1819
|
if (!twickCanvas || !getCanvasContext(twickCanvas)) return;
|
|
1253
1820
|
try {
|
|
@@ -1260,21 +1827,36 @@ const useTwickCanvas = ({
|
|
|
1260
1827
|
}
|
|
1261
1828
|
}
|
|
1262
1829
|
captionPropsRef.current = captionProps;
|
|
1830
|
+
const uniqueElements = [];
|
|
1831
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1832
|
+
for (const el of elements) {
|
|
1833
|
+
if (!el || !el.id) continue;
|
|
1834
|
+
if (seenIds.has(el.id)) continue;
|
|
1835
|
+
seenIds.add(el.id);
|
|
1836
|
+
uniqueElements.push(el);
|
|
1837
|
+
}
|
|
1263
1838
|
await Promise.all(
|
|
1264
|
-
|
|
1839
|
+
uniqueElements.map(async (element, index) => {
|
|
1265
1840
|
try {
|
|
1266
1841
|
if (!element) return;
|
|
1842
|
+
const zOrder = element.zIndex ?? index;
|
|
1267
1843
|
await addElementToCanvas({
|
|
1268
1844
|
element,
|
|
1269
|
-
index,
|
|
1845
|
+
index: zOrder,
|
|
1270
1846
|
reorder: false,
|
|
1271
1847
|
seekTime,
|
|
1272
|
-
captionProps
|
|
1848
|
+
captionProps,
|
|
1849
|
+
lockAspectRatio
|
|
1273
1850
|
});
|
|
1274
1851
|
} catch {
|
|
1275
1852
|
}
|
|
1276
1853
|
})
|
|
1277
1854
|
);
|
|
1855
|
+
if (watermark) {
|
|
1856
|
+
addWatermarkToCanvas({
|
|
1857
|
+
element: watermark
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1278
1860
|
reorderElementsByZIndex(twickCanvas);
|
|
1279
1861
|
} catch {
|
|
1280
1862
|
}
|
|
@@ -1284,100 +1866,76 @@ const useTwickCanvas = ({
|
|
|
1284
1866
|
index,
|
|
1285
1867
|
reorder = true,
|
|
1286
1868
|
seekTime,
|
|
1287
|
-
captionProps
|
|
1869
|
+
captionProps,
|
|
1870
|
+
lockAspectRatio
|
|
1288
1871
|
}) => {
|
|
1289
|
-
var _a
|
|
1872
|
+
var _a;
|
|
1290
1873
|
if (!twickCanvas) return;
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
currentFrameEffect,
|
|
1305
|
-
snapTime
|
|
1306
|
-
});
|
|
1307
|
-
if (element.timelineType === "scene") {
|
|
1308
|
-
await addBackgroundColor({
|
|
1309
|
-
element,
|
|
1310
|
-
index,
|
|
1311
|
-
canvas: twickCanvas,
|
|
1312
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1313
|
-
});
|
|
1314
|
-
}
|
|
1315
|
-
break;
|
|
1316
|
-
case ELEMENT_TYPES.IMAGE:
|
|
1317
|
-
await addImageElement({
|
|
1318
|
-
element,
|
|
1319
|
-
index,
|
|
1320
|
-
canvas: twickCanvas,
|
|
1321
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1322
|
-
});
|
|
1323
|
-
if (element.timelineType === "scene") {
|
|
1324
|
-
await addBackgroundColor({
|
|
1325
|
-
element,
|
|
1326
|
-
index,
|
|
1327
|
-
canvas: twickCanvas,
|
|
1328
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
break;
|
|
1332
|
-
case ELEMENT_TYPES.RECT:
|
|
1333
|
-
await addRectElement({
|
|
1334
|
-
element,
|
|
1335
|
-
index,
|
|
1336
|
-
canvas: twickCanvas,
|
|
1337
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1338
|
-
});
|
|
1339
|
-
break;
|
|
1340
|
-
case ELEMENT_TYPES.CIRCLE:
|
|
1341
|
-
await addCircleElement({
|
|
1342
|
-
element,
|
|
1343
|
-
index,
|
|
1344
|
-
canvas: twickCanvas,
|
|
1345
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1346
|
-
});
|
|
1347
|
-
break;
|
|
1348
|
-
case ELEMENT_TYPES.TEXT:
|
|
1349
|
-
await addTextElement({
|
|
1350
|
-
element,
|
|
1351
|
-
index,
|
|
1352
|
-
canvas: twickCanvas,
|
|
1353
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1354
|
-
});
|
|
1355
|
-
break;
|
|
1356
|
-
case ELEMENT_TYPES.CAPTION:
|
|
1357
|
-
await addCaptionElement({
|
|
1358
|
-
element,
|
|
1359
|
-
index,
|
|
1360
|
-
canvas: twickCanvas,
|
|
1361
|
-
captionProps,
|
|
1362
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1363
|
-
});
|
|
1364
|
-
break;
|
|
1874
|
+
const handler = elementController.get(element.type);
|
|
1875
|
+
if (handler) {
|
|
1876
|
+
await handler.add({
|
|
1877
|
+
element,
|
|
1878
|
+
index,
|
|
1879
|
+
canvas: twickCanvas,
|
|
1880
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
1881
|
+
seekTime,
|
|
1882
|
+
captionProps: captionProps ?? null,
|
|
1883
|
+
elementFrameMapRef: elementFrameMap,
|
|
1884
|
+
getCurrentFrameEffect,
|
|
1885
|
+
lockAspectRatio: lockAspectRatio ?? ((_a = element.props) == null ? void 0 : _a.lockAspectRatio)
|
|
1886
|
+
});
|
|
1365
1887
|
}
|
|
1366
|
-
elementMap.current[element.id] = element;
|
|
1888
|
+
elementMap.current[element.id] = { ...element, zIndex: element.zIndex ?? index };
|
|
1367
1889
|
if (reorder) {
|
|
1368
1890
|
reorderElementsByZIndex(twickCanvas);
|
|
1369
1891
|
}
|
|
1370
1892
|
};
|
|
1893
|
+
const addWatermarkToCanvas = ({
|
|
1894
|
+
element
|
|
1895
|
+
}) => {
|
|
1896
|
+
if (!twickCanvas) return;
|
|
1897
|
+
const handler = elementController.get("watermark");
|
|
1898
|
+
if (handler) {
|
|
1899
|
+
handler.add({
|
|
1900
|
+
element,
|
|
1901
|
+
index: Object.keys(elementMap.current).length,
|
|
1902
|
+
canvas: twickCanvas,
|
|
1903
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
1904
|
+
watermarkPropsRef
|
|
1905
|
+
});
|
|
1906
|
+
elementMap.current[element.id] = element;
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
const applyZOrder = (elementId, direction) => {
|
|
1910
|
+
if (!twickCanvas) return false;
|
|
1911
|
+
const newZIndex = changeZOrder(twickCanvas, elementId, direction);
|
|
1912
|
+
if (newZIndex == null) return false;
|
|
1913
|
+
const element = elementMap.current[elementId];
|
|
1914
|
+
if (element) elementMap.current[elementId] = { ...element, zIndex: newZIndex };
|
|
1915
|
+
onCanvasOperation == null ? void 0 : onCanvasOperation(CANVAS_OPERATIONS.Z_ORDER_CHANGED, { elementId, direction });
|
|
1916
|
+
return true;
|
|
1917
|
+
};
|
|
1918
|
+
const bringToFront = (elementId) => applyZOrder(elementId, "front");
|
|
1919
|
+
const sendToBack = (elementId) => applyZOrder(elementId, "back");
|
|
1920
|
+
const bringForward = (elementId) => applyZOrder(elementId, "forward");
|
|
1921
|
+
const sendBackward = (elementId) => applyZOrder(elementId, "backward");
|
|
1371
1922
|
return {
|
|
1372
1923
|
twickCanvas,
|
|
1373
1924
|
buildCanvas,
|
|
1374
1925
|
onVideoSizeChange,
|
|
1926
|
+
addWatermarkToCanvas,
|
|
1375
1927
|
addElementToCanvas,
|
|
1376
|
-
setCanvasElements
|
|
1928
|
+
setCanvasElements,
|
|
1929
|
+
bringToFront,
|
|
1930
|
+
sendToBack,
|
|
1931
|
+
bringForward,
|
|
1932
|
+
sendBackward
|
|
1377
1933
|
};
|
|
1378
1934
|
};
|
|
1379
1935
|
export {
|
|
1380
1936
|
CANVAS_OPERATIONS,
|
|
1937
|
+
ELEMENT_TYPES,
|
|
1938
|
+
ElementController,
|
|
1381
1939
|
addBackgroundColor,
|
|
1382
1940
|
addCaptionElement,
|
|
1383
1941
|
addImageElement,
|
|
@@ -1388,6 +1946,7 @@ export {
|
|
|
1388
1946
|
convertToVideoPosition,
|
|
1389
1947
|
createCanvas,
|
|
1390
1948
|
disabledControl,
|
|
1949
|
+
elementController,
|
|
1391
1950
|
getCurrentFrameEffect,
|
|
1392
1951
|
reorderElementsByZIndex,
|
|
1393
1952
|
rotateControl,
|