@twick/canvas 0.15.12 → 0.15.14
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 +551 -224
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +551 -224
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
2
5
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
6
|
const fabric = require("fabric");
|
|
4
7
|
const react = require("react");
|
|
@@ -48,7 +51,9 @@ const CANVAS_OPERATIONS = {
|
|
|
48
51
|
/** Items have been ungrouped */
|
|
49
52
|
ITEM_UNGROUPED: "ITEM_UNGROUPED",
|
|
50
53
|
/** Caption properties have been updated */
|
|
51
|
-
CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED"
|
|
54
|
+
CAPTION_PROPS_UPDATED: "CAPTION_PROPS_UPDATED",
|
|
55
|
+
/** Watermark has been updated */
|
|
56
|
+
WATERMARK_UPDATED: "WATERMARK_UPDATED"
|
|
52
57
|
};
|
|
53
58
|
const ELEMENT_TYPES = {
|
|
54
59
|
/** Text element type */
|
|
@@ -62,7 +67,11 @@ const ELEMENT_TYPES = {
|
|
|
62
67
|
/** Rectangle element type */
|
|
63
68
|
RECT: "rect",
|
|
64
69
|
/** Circle element type */
|
|
65
|
-
CIRCLE: "circle"
|
|
70
|
+
CIRCLE: "circle",
|
|
71
|
+
/** Icon element type */
|
|
72
|
+
ICON: "icon",
|
|
73
|
+
/** Background color element type */
|
|
74
|
+
BACKGROUND_COLOR: "backgroundColor"
|
|
66
75
|
};
|
|
67
76
|
const isBrowser = typeof window !== "undefined";
|
|
68
77
|
const isCanvasSupported = isBrowser && !!window.HTMLCanvasElement;
|
|
@@ -124,6 +133,26 @@ const createCanvas = ({
|
|
|
124
133
|
canvasMetadata
|
|
125
134
|
};
|
|
126
135
|
};
|
|
136
|
+
function measureTextWidth(text, options) {
|
|
137
|
+
if (typeof document === "undefined" || !text) return 0;
|
|
138
|
+
const canvas = document.createElement("canvas");
|
|
139
|
+
const ctx = canvas.getContext("2d");
|
|
140
|
+
if (!ctx) return 0;
|
|
141
|
+
const {
|
|
142
|
+
fontSize,
|
|
143
|
+
fontFamily,
|
|
144
|
+
fontStyle = "normal",
|
|
145
|
+
fontWeight = "normal"
|
|
146
|
+
} = options;
|
|
147
|
+
ctx.font = `${fontStyle} ${String(fontWeight)} ${fontSize}px ${fontFamily}`;
|
|
148
|
+
const lines = text.split("\n");
|
|
149
|
+
let maxWidth = 0;
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
const { width } = ctx.measureText(line);
|
|
152
|
+
if (width > maxWidth) maxWidth = width;
|
|
153
|
+
}
|
|
154
|
+
return Math.ceil(maxWidth);
|
|
155
|
+
}
|
|
127
156
|
const reorderElementsByZIndex = (canvas) => {
|
|
128
157
|
if (!canvas) return;
|
|
129
158
|
const backgroundColor = canvas.backgroundColor;
|
|
@@ -171,6 +200,17 @@ const getCurrentFrameEffect = (item, seekTime) => {
|
|
|
171
200
|
}
|
|
172
201
|
return currentFrameEffect;
|
|
173
202
|
};
|
|
203
|
+
const hexToRgba = (hex, alpha) => {
|
|
204
|
+
const color = hex.replace(/^#/, "");
|
|
205
|
+
const fullHex = color.length === 3 ? color.split("").map((c) => c + c).join("") : color;
|
|
206
|
+
if (fullHex.length !== 6) {
|
|
207
|
+
return hex;
|
|
208
|
+
}
|
|
209
|
+
const r = parseInt(fullHex.slice(0, 2), 16);
|
|
210
|
+
const g = parseInt(fullHex.slice(2, 4), 16);
|
|
211
|
+
const b = parseInt(fullHex.slice(4, 6), 16);
|
|
212
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
213
|
+
};
|
|
174
214
|
const disabledControl = new fabric.Control({
|
|
175
215
|
/** X position offset */
|
|
176
216
|
x: 0,
|
|
@@ -602,40 +642,66 @@ const addTextElement = ({
|
|
|
602
642
|
canvas,
|
|
603
643
|
canvasMetadata
|
|
604
644
|
}) => {
|
|
605
|
-
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;
|
|
645
|
+
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;
|
|
606
646
|
const { x, y } = convertToCanvasPosition(
|
|
607
647
|
((_a = element.props) == null ? void 0 : _a.x) || 0,
|
|
608
648
|
((_b = element.props) == null ? void 0 : _b.y) || 0,
|
|
609
649
|
canvasMetadata
|
|
610
650
|
);
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
651
|
+
const fontSize = Math.floor(
|
|
652
|
+
(((_c = element.props) == null ? void 0 : _c.fontSize) || DEFAULT_TEXT_PROPS.size) * canvasMetadata.scaleX
|
|
653
|
+
);
|
|
654
|
+
const fontFamily = ((_d = element.props) == null ? void 0 : _d.fontFamily) || DEFAULT_TEXT_PROPS.family;
|
|
655
|
+
const fontStyle = ((_e = element.props) == null ? void 0 : _e.fontStyle) || "normal";
|
|
656
|
+
const fontWeight = ((_f = element.props) == null ? void 0 : _f.fontWeight) || "normal";
|
|
657
|
+
let width;
|
|
658
|
+
if (((_g = element.props) == null ? void 0 : _g.width) != null && element.props.width > 0) {
|
|
659
|
+
width = element.props.width * canvasMetadata.scaleX;
|
|
660
|
+
if ((_h = element.props) == null ? void 0 : _h.maxWidth) {
|
|
661
|
+
width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);
|
|
662
|
+
}
|
|
663
|
+
} else {
|
|
664
|
+
const textContent = ((_i = element.props) == null ? void 0 : _i.text) ?? element.t ?? "";
|
|
665
|
+
width = measureTextWidth(textContent, {
|
|
666
|
+
fontSize,
|
|
667
|
+
fontFamily,
|
|
668
|
+
fontStyle,
|
|
669
|
+
fontWeight
|
|
670
|
+
});
|
|
671
|
+
const padding = 4;
|
|
672
|
+
width = width + padding * 2;
|
|
673
|
+
if ((_j = element.props) == null ? void 0 : _j.maxWidth) {
|
|
674
|
+
width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);
|
|
675
|
+
}
|
|
676
|
+
if (width <= 0) width = 100;
|
|
614
677
|
}
|
|
615
|
-
const
|
|
678
|
+
const backgroundColor = ((_k = element.props) == null ? void 0 : _k.backgroundColor) ? hexToRgba(
|
|
679
|
+
element.props.backgroundColor,
|
|
680
|
+
((_l = element.props) == null ? void 0 : _l.backgroundOpacity) ?? 1
|
|
681
|
+
) : void 0;
|
|
682
|
+
const text = new fabric.Textbox(((_m = element.props) == null ? void 0 : _m.text) || element.t || "", {
|
|
616
683
|
left: x,
|
|
617
684
|
top: y,
|
|
618
685
|
originX: "center",
|
|
619
686
|
originY: "center",
|
|
620
|
-
angle: ((
|
|
621
|
-
fontSize
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
fill: ((_k = element.props) == null ? void 0 : _k.fill) || DEFAULT_TEXT_PROPS.fill,
|
|
628
|
-
opacity: ((_l = element.props) == null ? void 0 : _l.opacity) ?? 1,
|
|
687
|
+
angle: ((_n = element.props) == null ? void 0 : _n.rotation) || 0,
|
|
688
|
+
fontSize,
|
|
689
|
+
fontFamily,
|
|
690
|
+
fontStyle,
|
|
691
|
+
fontWeight,
|
|
692
|
+
fill: ((_o = element.props) == null ? void 0 : _o.fill) || DEFAULT_TEXT_PROPS.fill,
|
|
693
|
+
opacity: ((_p = element.props) == null ? void 0 : _p.opacity) ?? 1,
|
|
629
694
|
width,
|
|
630
695
|
splitByGrapheme: false,
|
|
631
|
-
textAlign: ((
|
|
632
|
-
stroke: ((
|
|
633
|
-
strokeWidth: ((
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
696
|
+
textAlign: ((_q = element.props) == null ? void 0 : _q.textAlign) || "center",
|
|
697
|
+
stroke: ((_r = element.props) == null ? void 0 : _r.stroke) || DEFAULT_TEXT_PROPS.stroke,
|
|
698
|
+
strokeWidth: ((_s = element.props) == null ? void 0 : _s.lineWidth) || DEFAULT_TEXT_PROPS.lineWidth,
|
|
699
|
+
...backgroundColor && { backgroundColor },
|
|
700
|
+
shadow: ((_t = element.props) == null ? void 0 : _t.shadowColor) ? new fabric.Shadow({
|
|
701
|
+
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,
|
|
702
|
+
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,
|
|
703
|
+
blur: (((_B = element.props) == null ? void 0 : _B.shadowBlur) || 2) / 2,
|
|
704
|
+
color: (_C = element.props) == null ? void 0 : _C.shadowColor
|
|
639
705
|
}) : void 0
|
|
640
706
|
});
|
|
641
707
|
text.set("id", element.id);
|
|
@@ -1007,12 +1073,419 @@ const addBackgroundColor = ({
|
|
|
1007
1073
|
canvas.add(bgRect);
|
|
1008
1074
|
return bgRect;
|
|
1009
1075
|
};
|
|
1076
|
+
const VideoElement = {
|
|
1077
|
+
name: ELEMENT_TYPES.VIDEO,
|
|
1078
|
+
async add(params) {
|
|
1079
|
+
var _a, _b;
|
|
1080
|
+
const {
|
|
1081
|
+
element,
|
|
1082
|
+
index,
|
|
1083
|
+
canvas,
|
|
1084
|
+
canvasMetadata,
|
|
1085
|
+
seekTime = 0,
|
|
1086
|
+
elementFrameMapRef,
|
|
1087
|
+
getCurrentFrameEffect: getFrameEffect
|
|
1088
|
+
} = params;
|
|
1089
|
+
if (!getFrameEffect || !elementFrameMapRef) return;
|
|
1090
|
+
const currentFrameEffect = getFrameEffect(element, seekTime);
|
|
1091
|
+
elementFrameMapRef.current[element.id] = currentFrameEffect;
|
|
1092
|
+
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);
|
|
1093
|
+
await addVideoElement({
|
|
1094
|
+
element,
|
|
1095
|
+
index,
|
|
1096
|
+
canvas,
|
|
1097
|
+
canvasMetadata,
|
|
1098
|
+
currentFrameEffect,
|
|
1099
|
+
snapTime
|
|
1100
|
+
});
|
|
1101
|
+
if (element.timelineType === "scene") {
|
|
1102
|
+
await addBackgroundColor({
|
|
1103
|
+
element,
|
|
1104
|
+
index,
|
|
1105
|
+
canvas,
|
|
1106
|
+
canvasMetadata
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
updateFromFabricObject(object, element, context) {
|
|
1111
|
+
const { x, y } = convertToVideoPosition(
|
|
1112
|
+
object.left,
|
|
1113
|
+
object.top,
|
|
1114
|
+
context.canvasMetadata,
|
|
1115
|
+
context.videoSize
|
|
1116
|
+
);
|
|
1117
|
+
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
1118
|
+
let updatedFrameSize;
|
|
1119
|
+
if (currentFrameEffect) {
|
|
1120
|
+
updatedFrameSize = [
|
|
1121
|
+
currentFrameEffect.props.frameSize[0] * object.scaleX,
|
|
1122
|
+
currentFrameEffect.props.frameSize[1] * object.scaleY
|
|
1123
|
+
];
|
|
1124
|
+
context.elementFrameMapRef.current[element.id] = {
|
|
1125
|
+
...currentFrameEffect,
|
|
1126
|
+
props: {
|
|
1127
|
+
...currentFrameEffect.props,
|
|
1128
|
+
framePosition: { x, y },
|
|
1129
|
+
frameSize: updatedFrameSize
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
return {
|
|
1133
|
+
element: {
|
|
1134
|
+
...element,
|
|
1135
|
+
frameEffects: (element.frameEffects || []).map(
|
|
1136
|
+
(fe) => fe.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
1137
|
+
...fe,
|
|
1138
|
+
props: {
|
|
1139
|
+
...fe.props,
|
|
1140
|
+
framePosition: { x, y },
|
|
1141
|
+
frameSize: updatedFrameSize
|
|
1142
|
+
}
|
|
1143
|
+
} : fe
|
|
1144
|
+
)
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
const frame = element.frame;
|
|
1149
|
+
updatedFrameSize = [
|
|
1150
|
+
(frame.size[0] ?? 0) * object.scaleX,
|
|
1151
|
+
(frame.size[1] ?? 0) * object.scaleY
|
|
1152
|
+
];
|
|
1153
|
+
return {
|
|
1154
|
+
element: {
|
|
1155
|
+
...element,
|
|
1156
|
+
frame: {
|
|
1157
|
+
...frame,
|
|
1158
|
+
rotation: object.angle,
|
|
1159
|
+
size: updatedFrameSize,
|
|
1160
|
+
x,
|
|
1161
|
+
y
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
const ImageElement = {
|
|
1168
|
+
name: ELEMENT_TYPES.IMAGE,
|
|
1169
|
+
async add(params) {
|
|
1170
|
+
const { element, index, canvas, canvasMetadata } = params;
|
|
1171
|
+
await addImageElement({
|
|
1172
|
+
element,
|
|
1173
|
+
index,
|
|
1174
|
+
canvas,
|
|
1175
|
+
canvasMetadata
|
|
1176
|
+
});
|
|
1177
|
+
if (element.timelineType === "scene") {
|
|
1178
|
+
await addBackgroundColor({
|
|
1179
|
+
element,
|
|
1180
|
+
index,
|
|
1181
|
+
canvas,
|
|
1182
|
+
canvasMetadata
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
updateFromFabricObject(object, element, context) {
|
|
1187
|
+
var _a, _b;
|
|
1188
|
+
const { x, y } = convertToVideoPosition(
|
|
1189
|
+
object.left,
|
|
1190
|
+
object.top,
|
|
1191
|
+
context.canvasMetadata,
|
|
1192
|
+
context.videoSize
|
|
1193
|
+
);
|
|
1194
|
+
const currentFrameEffect = context.elementFrameMapRef.current[element.id];
|
|
1195
|
+
if (object.type === "group") {
|
|
1196
|
+
let updatedFrameSize;
|
|
1197
|
+
if (currentFrameEffect) {
|
|
1198
|
+
updatedFrameSize = [
|
|
1199
|
+
currentFrameEffect.props.frameSize[0] * object.scaleX,
|
|
1200
|
+
currentFrameEffect.props.frameSize[1] * object.scaleY
|
|
1201
|
+
];
|
|
1202
|
+
context.elementFrameMapRef.current[element.id] = {
|
|
1203
|
+
...currentFrameEffect,
|
|
1204
|
+
props: {
|
|
1205
|
+
...currentFrameEffect.props,
|
|
1206
|
+
framePosition: { x, y },
|
|
1207
|
+
frameSize: updatedFrameSize
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
return {
|
|
1211
|
+
element: {
|
|
1212
|
+
...element,
|
|
1213
|
+
frameEffects: (element.frameEffects || []).map(
|
|
1214
|
+
(fe) => fe.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
1215
|
+
...fe,
|
|
1216
|
+
props: {
|
|
1217
|
+
...fe.props,
|
|
1218
|
+
framePosition: { x, y },
|
|
1219
|
+
frameSize: updatedFrameSize
|
|
1220
|
+
}
|
|
1221
|
+
} : fe
|
|
1222
|
+
)
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
const frame = element.frame;
|
|
1227
|
+
updatedFrameSize = [
|
|
1228
|
+
(frame.size[0] ?? 0) * object.scaleX,
|
|
1229
|
+
(frame.size[1] ?? 0) * object.scaleY
|
|
1230
|
+
];
|
|
1231
|
+
return {
|
|
1232
|
+
element: {
|
|
1233
|
+
...element,
|
|
1234
|
+
frame: {
|
|
1235
|
+
...frame,
|
|
1236
|
+
rotation: object.angle,
|
|
1237
|
+
size: updatedFrameSize,
|
|
1238
|
+
x,
|
|
1239
|
+
y
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
return {
|
|
1245
|
+
element: {
|
|
1246
|
+
...element,
|
|
1247
|
+
props: {
|
|
1248
|
+
...element.props,
|
|
1249
|
+
rotation: object.angle,
|
|
1250
|
+
width: (((_a = element.props) == null ? void 0 : _a.width) ?? 0) * object.scaleX,
|
|
1251
|
+
height: (((_b = element.props) == null ? void 0 : _b.height) ?? 0) * object.scaleY,
|
|
1252
|
+
x,
|
|
1253
|
+
y
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
const RectElement = {
|
|
1260
|
+
name: ELEMENT_TYPES.RECT,
|
|
1261
|
+
async add(params) {
|
|
1262
|
+
const { element, index, canvas, canvasMetadata } = params;
|
|
1263
|
+
await addRectElement({
|
|
1264
|
+
element,
|
|
1265
|
+
index,
|
|
1266
|
+
canvas,
|
|
1267
|
+
canvasMetadata
|
|
1268
|
+
});
|
|
1269
|
+
},
|
|
1270
|
+
updateFromFabricObject(object, element, context) {
|
|
1271
|
+
var _a, _b;
|
|
1272
|
+
const { x, y } = convertToVideoPosition(
|
|
1273
|
+
object.left,
|
|
1274
|
+
object.top,
|
|
1275
|
+
context.canvasMetadata,
|
|
1276
|
+
context.videoSize
|
|
1277
|
+
);
|
|
1278
|
+
return {
|
|
1279
|
+
element: {
|
|
1280
|
+
...element,
|
|
1281
|
+
props: {
|
|
1282
|
+
...element.props,
|
|
1283
|
+
rotation: object.angle,
|
|
1284
|
+
width: (((_a = element.props) == null ? void 0 : _a.width) ?? 0) * object.scaleX,
|
|
1285
|
+
height: (((_b = element.props) == null ? void 0 : _b.height) ?? 0) * object.scaleY,
|
|
1286
|
+
x,
|
|
1287
|
+
y
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
const CircleElement = {
|
|
1294
|
+
name: ELEMENT_TYPES.CIRCLE,
|
|
1295
|
+
async add(params) {
|
|
1296
|
+
const { element, index, canvas, canvasMetadata } = params;
|
|
1297
|
+
await addCircleElement({
|
|
1298
|
+
element,
|
|
1299
|
+
index,
|
|
1300
|
+
canvas,
|
|
1301
|
+
canvasMetadata
|
|
1302
|
+
});
|
|
1303
|
+
},
|
|
1304
|
+
updateFromFabricObject(object, element, context) {
|
|
1305
|
+
var _a;
|
|
1306
|
+
const { x, y } = convertToVideoPosition(
|
|
1307
|
+
object.left,
|
|
1308
|
+
object.top,
|
|
1309
|
+
context.canvasMetadata,
|
|
1310
|
+
context.videoSize
|
|
1311
|
+
);
|
|
1312
|
+
const radius = Number(
|
|
1313
|
+
((((_a = element.props) == null ? void 0 : _a.radius) ?? 0) * object.scaleX).toFixed(2)
|
|
1314
|
+
);
|
|
1315
|
+
return {
|
|
1316
|
+
element: {
|
|
1317
|
+
...element,
|
|
1318
|
+
props: {
|
|
1319
|
+
...element.props,
|
|
1320
|
+
rotation: object.angle,
|
|
1321
|
+
radius,
|
|
1322
|
+
height: radius * 2,
|
|
1323
|
+
width: radius * 2,
|
|
1324
|
+
x,
|
|
1325
|
+
y
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
const TextElement = {
|
|
1332
|
+
name: ELEMENT_TYPES.TEXT,
|
|
1333
|
+
async add(params) {
|
|
1334
|
+
const { element, index, canvas, canvasMetadata } = params;
|
|
1335
|
+
await addTextElement({
|
|
1336
|
+
element,
|
|
1337
|
+
index,
|
|
1338
|
+
canvas,
|
|
1339
|
+
canvasMetadata
|
|
1340
|
+
});
|
|
1341
|
+
},
|
|
1342
|
+
updateFromFabricObject(object, element, context) {
|
|
1343
|
+
const { x, y } = convertToVideoPosition(
|
|
1344
|
+
object.left,
|
|
1345
|
+
object.top,
|
|
1346
|
+
context.canvasMetadata,
|
|
1347
|
+
context.videoSize
|
|
1348
|
+
);
|
|
1349
|
+
return {
|
|
1350
|
+
element: {
|
|
1351
|
+
...element,
|
|
1352
|
+
props: {
|
|
1353
|
+
...element.props,
|
|
1354
|
+
rotation: object.angle,
|
|
1355
|
+
x,
|
|
1356
|
+
y
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
const CaptionElement = {
|
|
1363
|
+
name: ELEMENT_TYPES.CAPTION,
|
|
1364
|
+
async add(params) {
|
|
1365
|
+
const { element, index, canvas, captionProps, canvasMetadata } = params;
|
|
1366
|
+
await addCaptionElement({
|
|
1367
|
+
element,
|
|
1368
|
+
index,
|
|
1369
|
+
canvas,
|
|
1370
|
+
captionProps: captionProps ?? {},
|
|
1371
|
+
canvasMetadata
|
|
1372
|
+
});
|
|
1373
|
+
},
|
|
1374
|
+
updateFromFabricObject(object, element, context) {
|
|
1375
|
+
var _a;
|
|
1376
|
+
const { x, y } = convertToVideoPosition(
|
|
1377
|
+
object.left,
|
|
1378
|
+
object.top,
|
|
1379
|
+
context.canvasMetadata,
|
|
1380
|
+
context.videoSize
|
|
1381
|
+
);
|
|
1382
|
+
if ((_a = context.captionPropsRef.current) == null ? void 0 : _a.applyToAll) {
|
|
1383
|
+
return {
|
|
1384
|
+
element,
|
|
1385
|
+
operation: CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED,
|
|
1386
|
+
payload: {
|
|
1387
|
+
element,
|
|
1388
|
+
props: {
|
|
1389
|
+
...context.captionPropsRef.current,
|
|
1390
|
+
x,
|
|
1391
|
+
y
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
return {
|
|
1397
|
+
element: {
|
|
1398
|
+
...element,
|
|
1399
|
+
props: {
|
|
1400
|
+
...element.props,
|
|
1401
|
+
x,
|
|
1402
|
+
y
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
const WatermarkElement = {
|
|
1409
|
+
name: "watermark",
|
|
1410
|
+
async add(params) {
|
|
1411
|
+
const { element, index, canvas, canvasMetadata, watermarkPropsRef } = params;
|
|
1412
|
+
if (element.type === ELEMENT_TYPES.TEXT) {
|
|
1413
|
+
if (watermarkPropsRef) watermarkPropsRef.current = element.props;
|
|
1414
|
+
await addTextElement({
|
|
1415
|
+
element,
|
|
1416
|
+
index,
|
|
1417
|
+
canvas,
|
|
1418
|
+
canvasMetadata
|
|
1419
|
+
});
|
|
1420
|
+
} else if (element.type === ELEMENT_TYPES.IMAGE) {
|
|
1421
|
+
await addImageElement({
|
|
1422
|
+
element,
|
|
1423
|
+
index,
|
|
1424
|
+
canvas,
|
|
1425
|
+
canvasMetadata
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
},
|
|
1429
|
+
updateFromFabricObject(object, element, context) {
|
|
1430
|
+
const { x, y } = convertToVideoPosition(
|
|
1431
|
+
object.left,
|
|
1432
|
+
object.top,
|
|
1433
|
+
context.canvasMetadata,
|
|
1434
|
+
context.videoSize
|
|
1435
|
+
);
|
|
1436
|
+
const rotation = object.angle != null ? object.angle : void 0;
|
|
1437
|
+
const opacity = object.opacity != null ? object.opacity : void 0;
|
|
1438
|
+
const baseProps = element.type === ELEMENT_TYPES.TEXT ? context.watermarkPropsRef.current ?? element.props ?? {} : { ...element.props };
|
|
1439
|
+
const props = element.type === ELEMENT_TYPES.IMAGE && (object.scaleX != null || object.scaleY != null) ? {
|
|
1440
|
+
...baseProps,
|
|
1441
|
+
width: baseProps.width != null && object.scaleX != null ? baseProps.width * object.scaleX : baseProps.width,
|
|
1442
|
+
height: baseProps.height != null && object.scaleY != null ? baseProps.height * object.scaleY : baseProps.height
|
|
1443
|
+
} : baseProps;
|
|
1444
|
+
const payload = {
|
|
1445
|
+
position: { x, y },
|
|
1446
|
+
...rotation != null && { rotation },
|
|
1447
|
+
...opacity != null && { opacity },
|
|
1448
|
+
...Object.keys(props).length > 0 && { props }
|
|
1449
|
+
};
|
|
1450
|
+
return {
|
|
1451
|
+
element: { ...element, props: { ...element.props, x, y, rotation, opacity, ...props } },
|
|
1452
|
+
operation: CANVAS_OPERATIONS.WATERMARK_UPDATED,
|
|
1453
|
+
payload
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
class ElementController {
|
|
1458
|
+
constructor() {
|
|
1459
|
+
__publicField(this, "elements", /* @__PURE__ */ new Map());
|
|
1460
|
+
}
|
|
1461
|
+
register(handler) {
|
|
1462
|
+
this.elements.set(handler.name, handler);
|
|
1463
|
+
}
|
|
1464
|
+
get(name) {
|
|
1465
|
+
return this.elements.get(name);
|
|
1466
|
+
}
|
|
1467
|
+
list() {
|
|
1468
|
+
return Array.from(this.elements.keys());
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const elementController = new ElementController();
|
|
1472
|
+
function registerElements() {
|
|
1473
|
+
elementController.register(VideoElement);
|
|
1474
|
+
elementController.register(ImageElement);
|
|
1475
|
+
elementController.register(RectElement);
|
|
1476
|
+
elementController.register(CircleElement);
|
|
1477
|
+
elementController.register(TextElement);
|
|
1478
|
+
elementController.register(CaptionElement);
|
|
1479
|
+
elementController.register(WatermarkElement);
|
|
1480
|
+
}
|
|
1481
|
+
registerElements();
|
|
1010
1482
|
const useTwickCanvas = ({
|
|
1011
1483
|
onCanvasReady,
|
|
1012
1484
|
onCanvasOperation
|
|
1013
1485
|
}) => {
|
|
1014
1486
|
const [twickCanvas, setTwickCanvas] = react.useState(null);
|
|
1015
1487
|
const elementMap = react.useRef({});
|
|
1488
|
+
const watermarkPropsRef = react.useRef(null);
|
|
1016
1489
|
const elementFrameMap = react.useRef({});
|
|
1017
1490
|
const twickCanvasRef = react.useRef(null);
|
|
1018
1491
|
const videoSizeRef = react.useRef({ width: 1, height: 1 });
|
|
@@ -1112,141 +1585,33 @@ const useTwickCanvas = ({
|
|
|
1112
1585
|
case "scale":
|
|
1113
1586
|
case "scaleX":
|
|
1114
1587
|
case "scaleY":
|
|
1115
|
-
case "rotate":
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
canvasMetadataRef.current,
|
|
1120
|
-
videoSizeRef.current
|
|
1588
|
+
case "rotate": {
|
|
1589
|
+
const currentElement = elementMap.current[elementId];
|
|
1590
|
+
const handler = elementController.get(
|
|
1591
|
+
elementId === "e-watermark" ? "watermark" : currentElement == null ? void 0 : currentElement.type
|
|
1121
1592
|
);
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
});
|
|
1132
|
-
} else {
|
|
1133
|
-
elementMap.current[elementId] = {
|
|
1134
|
-
...elementMap.current[elementId],
|
|
1135
|
-
props: {
|
|
1136
|
-
...elementMap.current[elementId].props,
|
|
1137
|
-
x,
|
|
1138
|
-
y
|
|
1139
|
-
}
|
|
1140
|
-
};
|
|
1141
|
-
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
1142
|
-
CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
1143
|
-
elementMap.current[elementId]
|
|
1144
|
-
);
|
|
1145
|
-
}
|
|
1146
|
-
} else {
|
|
1147
|
-
if ((object == null ? void 0 : object.type) === "group") {
|
|
1148
|
-
const currentFrameEffect = elementFrameMap.current[elementId];
|
|
1149
|
-
let updatedFrameSize;
|
|
1150
|
-
if (currentFrameEffect) {
|
|
1151
|
-
updatedFrameSize = [
|
|
1152
|
-
currentFrameEffect.props.frameSize[0] * object.scaleX,
|
|
1153
|
-
currentFrameEffect.props.frameSize[1] * object.scaleY
|
|
1154
|
-
];
|
|
1155
|
-
} else {
|
|
1156
|
-
updatedFrameSize = [
|
|
1157
|
-
elementMap.current[elementId].frame.size[0] * object.scaleX,
|
|
1158
|
-
elementMap.current[elementId].frame.size[1] * object.scaleY
|
|
1159
|
-
];
|
|
1160
|
-
}
|
|
1161
|
-
if (currentFrameEffect) {
|
|
1162
|
-
elementMap.current[elementId] = {
|
|
1163
|
-
...elementMap.current[elementId],
|
|
1164
|
-
frameEffects: (elementMap.current[elementId].frameEffects || []).map(
|
|
1165
|
-
(frameEffect) => frameEffect.id === (currentFrameEffect == null ? void 0 : currentFrameEffect.id) ? {
|
|
1166
|
-
...frameEffect,
|
|
1167
|
-
props: {
|
|
1168
|
-
...frameEffect.props,
|
|
1169
|
-
framePosition: {
|
|
1170
|
-
x,
|
|
1171
|
-
y
|
|
1172
|
-
},
|
|
1173
|
-
frameSize: updatedFrameSize
|
|
1174
|
-
}
|
|
1175
|
-
} : frameEffect
|
|
1176
|
-
)
|
|
1177
|
-
};
|
|
1178
|
-
elementFrameMap.current[elementId] = {
|
|
1179
|
-
...elementFrameMap.current[elementId],
|
|
1180
|
-
framePosition: {
|
|
1181
|
-
x,
|
|
1182
|
-
y
|
|
1183
|
-
},
|
|
1184
|
-
frameSize: updatedFrameSize
|
|
1185
|
-
};
|
|
1186
|
-
} else {
|
|
1187
|
-
elementMap.current[elementId] = {
|
|
1188
|
-
...elementMap.current[elementId],
|
|
1189
|
-
frame: {
|
|
1190
|
-
...elementMap.current[elementId].frame,
|
|
1191
|
-
rotation: object.angle,
|
|
1192
|
-
size: updatedFrameSize,
|
|
1193
|
-
x,
|
|
1194
|
-
y
|
|
1195
|
-
}
|
|
1196
|
-
};
|
|
1197
|
-
}
|
|
1198
|
-
} else {
|
|
1199
|
-
if ((object == null ? void 0 : object.type) === "text") {
|
|
1200
|
-
elementMap.current[elementId] = {
|
|
1201
|
-
...elementMap.current[elementId],
|
|
1202
|
-
props: {
|
|
1203
|
-
...elementMap.current[elementId].props,
|
|
1204
|
-
rotation: object.angle,
|
|
1205
|
-
x,
|
|
1206
|
-
y
|
|
1207
|
-
}
|
|
1208
|
-
};
|
|
1209
|
-
} else if ((object == null ? void 0 : object.type) === "circle") {
|
|
1210
|
-
const radius = Number(
|
|
1211
|
-
(elementMap.current[elementId].props.radius * object.scaleX).toFixed(2)
|
|
1212
|
-
);
|
|
1213
|
-
elementMap.current[elementId] = {
|
|
1214
|
-
...elementMap.current[elementId],
|
|
1215
|
-
props: {
|
|
1216
|
-
...elementMap.current[elementId].props,
|
|
1217
|
-
rotation: object.angle,
|
|
1218
|
-
radius,
|
|
1219
|
-
height: radius * 2,
|
|
1220
|
-
width: radius * 2,
|
|
1221
|
-
x,
|
|
1222
|
-
y
|
|
1223
|
-
}
|
|
1224
|
-
};
|
|
1225
|
-
} else {
|
|
1226
|
-
elementMap.current[elementId] = {
|
|
1227
|
-
...elementMap.current[elementId],
|
|
1228
|
-
props: {
|
|
1229
|
-
...elementMap.current[elementId].props,
|
|
1230
|
-
rotation: object.angle,
|
|
1231
|
-
width: elementMap.current[elementId].props.width * object.scaleX,
|
|
1232
|
-
height: elementMap.current[elementId].props.height * object.scaleY,
|
|
1233
|
-
x,
|
|
1234
|
-
y
|
|
1235
|
-
}
|
|
1236
|
-
};
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1593
|
+
const result = (_c = handler == null ? void 0 : handler.updateFromFabricObject) == null ? void 0 : _c.call(handler, object, currentElement ?? { id: elementId, type: "text", props: {} }, {
|
|
1594
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
1595
|
+
videoSize: videoSizeRef.current,
|
|
1596
|
+
elementFrameMapRef: elementFrameMap,
|
|
1597
|
+
captionPropsRef,
|
|
1598
|
+
watermarkPropsRef
|
|
1599
|
+
});
|
|
1600
|
+
if (result) {
|
|
1601
|
+
elementMap.current[elementId] = result.element;
|
|
1239
1602
|
onCanvasOperation == null ? void 0 : onCanvasOperation(
|
|
1240
|
-
CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
1241
|
-
|
|
1603
|
+
result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,
|
|
1604
|
+
result.payload ?? result.element
|
|
1242
1605
|
);
|
|
1243
1606
|
}
|
|
1244
1607
|
break;
|
|
1608
|
+
}
|
|
1245
1609
|
}
|
|
1246
1610
|
}
|
|
1247
1611
|
};
|
|
1248
1612
|
const setCanvasElements = async ({
|
|
1249
1613
|
elements,
|
|
1614
|
+
watermark,
|
|
1250
1615
|
seekTime = 0,
|
|
1251
1616
|
captionProps,
|
|
1252
1617
|
cleanAndAdd = false
|
|
@@ -1277,6 +1642,11 @@ const useTwickCanvas = ({
|
|
|
1277
1642
|
}
|
|
1278
1643
|
})
|
|
1279
1644
|
);
|
|
1645
|
+
if (watermark) {
|
|
1646
|
+
addWatermarkToCanvas({
|
|
1647
|
+
element: watermark
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1280
1650
|
reorderElementsByZIndex(twickCanvas);
|
|
1281
1651
|
} catch {
|
|
1282
1652
|
}
|
|
@@ -1288,97 +1658,53 @@ const useTwickCanvas = ({
|
|
|
1288
1658
|
seekTime,
|
|
1289
1659
|
captionProps
|
|
1290
1660
|
}) => {
|
|
1291
|
-
var _a, _b;
|
|
1292
1661
|
if (!twickCanvas) return;
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
canvasMetadata: canvasMetadataRef.current,
|
|
1306
|
-
currentFrameEffect,
|
|
1307
|
-
snapTime
|
|
1308
|
-
});
|
|
1309
|
-
if (element.timelineType === "scene") {
|
|
1310
|
-
await addBackgroundColor({
|
|
1311
|
-
element,
|
|
1312
|
-
index,
|
|
1313
|
-
canvas: twickCanvas,
|
|
1314
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
break;
|
|
1318
|
-
case ELEMENT_TYPES.IMAGE:
|
|
1319
|
-
await addImageElement({
|
|
1320
|
-
element,
|
|
1321
|
-
index,
|
|
1322
|
-
canvas: twickCanvas,
|
|
1323
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1324
|
-
});
|
|
1325
|
-
if (element.timelineType === "scene") {
|
|
1326
|
-
await addBackgroundColor({
|
|
1327
|
-
element,
|
|
1328
|
-
index,
|
|
1329
|
-
canvas: twickCanvas,
|
|
1330
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1331
|
-
});
|
|
1332
|
-
}
|
|
1333
|
-
break;
|
|
1334
|
-
case ELEMENT_TYPES.RECT:
|
|
1335
|
-
await addRectElement({
|
|
1336
|
-
element,
|
|
1337
|
-
index,
|
|
1338
|
-
canvas: twickCanvas,
|
|
1339
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1340
|
-
});
|
|
1341
|
-
break;
|
|
1342
|
-
case ELEMENT_TYPES.CIRCLE:
|
|
1343
|
-
await addCircleElement({
|
|
1344
|
-
element,
|
|
1345
|
-
index,
|
|
1346
|
-
canvas: twickCanvas,
|
|
1347
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1348
|
-
});
|
|
1349
|
-
break;
|
|
1350
|
-
case ELEMENT_TYPES.TEXT:
|
|
1351
|
-
await addTextElement({
|
|
1352
|
-
element,
|
|
1353
|
-
index,
|
|
1354
|
-
canvas: twickCanvas,
|
|
1355
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1356
|
-
});
|
|
1357
|
-
break;
|
|
1358
|
-
case ELEMENT_TYPES.CAPTION:
|
|
1359
|
-
await addCaptionElement({
|
|
1360
|
-
element,
|
|
1361
|
-
index,
|
|
1362
|
-
canvas: twickCanvas,
|
|
1363
|
-
captionProps,
|
|
1364
|
-
canvasMetadata: canvasMetadataRef.current
|
|
1365
|
-
});
|
|
1366
|
-
break;
|
|
1662
|
+
const handler = elementController.get(element.type);
|
|
1663
|
+
if (handler) {
|
|
1664
|
+
await handler.add({
|
|
1665
|
+
element,
|
|
1666
|
+
index,
|
|
1667
|
+
canvas: twickCanvas,
|
|
1668
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
1669
|
+
seekTime,
|
|
1670
|
+
captionProps: captionProps ?? null,
|
|
1671
|
+
elementFrameMapRef: elementFrameMap,
|
|
1672
|
+
getCurrentFrameEffect
|
|
1673
|
+
});
|
|
1367
1674
|
}
|
|
1368
1675
|
elementMap.current[element.id] = element;
|
|
1369
1676
|
if (reorder) {
|
|
1370
1677
|
reorderElementsByZIndex(twickCanvas);
|
|
1371
1678
|
}
|
|
1372
1679
|
};
|
|
1680
|
+
const addWatermarkToCanvas = ({
|
|
1681
|
+
element
|
|
1682
|
+
}) => {
|
|
1683
|
+
if (!twickCanvas) return;
|
|
1684
|
+
const handler = elementController.get("watermark");
|
|
1685
|
+
if (handler) {
|
|
1686
|
+
handler.add({
|
|
1687
|
+
element,
|
|
1688
|
+
index: Object.keys(elementMap.current).length,
|
|
1689
|
+
canvas: twickCanvas,
|
|
1690
|
+
canvasMetadata: canvasMetadataRef.current,
|
|
1691
|
+
watermarkPropsRef
|
|
1692
|
+
});
|
|
1693
|
+
elementMap.current[element.id] = element;
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1373
1696
|
return {
|
|
1374
1697
|
twickCanvas,
|
|
1375
1698
|
buildCanvas,
|
|
1376
1699
|
onVideoSizeChange,
|
|
1700
|
+
addWatermarkToCanvas,
|
|
1377
1701
|
addElementToCanvas,
|
|
1378
1702
|
setCanvasElements
|
|
1379
1703
|
};
|
|
1380
1704
|
};
|
|
1381
1705
|
exports.CANVAS_OPERATIONS = CANVAS_OPERATIONS;
|
|
1706
|
+
exports.ELEMENT_TYPES = ELEMENT_TYPES;
|
|
1707
|
+
exports.ElementController = ElementController;
|
|
1382
1708
|
exports.addBackgroundColor = addBackgroundColor;
|
|
1383
1709
|
exports.addCaptionElement = addCaptionElement;
|
|
1384
1710
|
exports.addImageElement = addImageElement;
|
|
@@ -1389,6 +1715,7 @@ exports.convertToCanvasPosition = convertToCanvasPosition;
|
|
|
1389
1715
|
exports.convertToVideoPosition = convertToVideoPosition;
|
|
1390
1716
|
exports.createCanvas = createCanvas;
|
|
1391
1717
|
exports.disabledControl = disabledControl;
|
|
1718
|
+
exports.elementController = elementController;
|
|
1392
1719
|
exports.getCurrentFrameEffect = getCurrentFrameEffect;
|
|
1393
1720
|
exports.reorderElementsByZIndex = reorderElementsByZIndex;
|
|
1394
1721
|
exports.rotateControl = rotateControl;
|