customized-fabric 2.0.20 → 2.0.22
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.
@@ -5,8 +5,8 @@ const fabric_1 = require("fabric");
|
|
5
5
|
const constants_1 = require("./constants");
|
6
6
|
const utils_1 = require("../utils");
|
7
7
|
const objectId_1 = require("../utils/objectId");
|
8
|
-
const svg_util_1 = require("../utils/svg.util");
|
9
8
|
const common_util_1 = require("../utils/common.util");
|
9
|
+
const utils_2 = require("./utils");
|
10
10
|
const CurvedTextClass = fabric_1.fabric.util.createClass(fabric_1.fabric.Group, {
|
11
11
|
initialize: function (options) {
|
12
12
|
let { text, ...rest } = options ?? {};
|
@@ -33,10 +33,21 @@ const CurvedTextClass = fabric_1.fabric.util.createClass(fabric_1.fabric.Group,
|
|
33
33
|
positionAngle: 0,
|
34
34
|
maxFontSize: 200,
|
35
35
|
strokeLineJoin: "round",
|
36
|
+
startAngle: 45,
|
37
|
+
endAngle: 135,
|
36
38
|
...text,
|
37
39
|
text: isAllCapital ? fullText.toUpperCase() : fullText,
|
38
40
|
});
|
39
|
-
|
41
|
+
this.ellipseObject = new fabric_1.fabric.Ellipse({
|
42
|
+
stroke: "black",
|
43
|
+
strokeDashArray: [10, 10],
|
44
|
+
fill: "transparent",
|
45
|
+
});
|
46
|
+
const group = new fabric_1.fabric.Group([
|
47
|
+
this.rectObject,
|
48
|
+
this.ellipseObject,
|
49
|
+
this.textObject,
|
50
|
+
]);
|
40
51
|
this.set({
|
41
52
|
...group,
|
42
53
|
_id: new objectId_1.ObjectId().toString(),
|
@@ -48,54 +59,70 @@ const CurvedTextClass = fabric_1.fabric.util.createClass(fabric_1.fabric.Group,
|
|
48
59
|
pathVisible: !(options?.isOriginal || options?.hideStroke),
|
49
60
|
});
|
50
61
|
this.on("scaling", () => {
|
51
|
-
|
52
|
-
let height = this.height * this.scaleY;
|
62
|
+
const { width, height, scaleX, scaleY } = this;
|
53
63
|
const attributes = {
|
54
64
|
scaleX: 1,
|
55
65
|
scaleY: 1,
|
56
|
-
width,
|
57
|
-
height,
|
66
|
+
width: width * scaleX,
|
67
|
+
height: height * scaleY,
|
58
68
|
};
|
69
|
+
const { fontSize, maxFontSize, fontSizeScalable, strokeWidth } = this.textObject;
|
70
|
+
if (fontSizeScalable) {
|
71
|
+
// font scaling
|
72
|
+
const fontSizeRatio = maxFontSize / Math.min(width, height);
|
73
|
+
const newMaxFontSize = fontSizeRatio * Math.min(attributes.width, attributes.height);
|
74
|
+
// stroke width scaling
|
75
|
+
const strokeWidthRatio = strokeWidth / Math.min(width, height);
|
76
|
+
const newStrokeWidth = strokeWidthRatio * Math.min(attributes.width, attributes.height);
|
77
|
+
this.textObject.set({
|
78
|
+
maxFontSize: newMaxFontSize,
|
79
|
+
fontSize: Math.min(fontSize, newMaxFontSize),
|
80
|
+
strokeWidth: newStrokeWidth,
|
81
|
+
});
|
82
|
+
}
|
59
83
|
this.set(attributes);
|
60
84
|
this.rectObject.set(attributes);
|
61
|
-
this.calculateTextPath(
|
85
|
+
this.calculateTextPath();
|
62
86
|
this.autoChangeFontSize(0.1);
|
87
|
+
this.updateEllipsePath();
|
63
88
|
this.canvas?.renderAll?.();
|
64
89
|
});
|
65
90
|
if (options?.isOriginal) {
|
66
|
-
this.rectObject?.set({
|
91
|
+
this.rectObject?.set({ visible: false });
|
92
|
+
this.ellipseObject.set({ visible: false });
|
67
93
|
}
|
68
94
|
else {
|
69
95
|
if (options?.hideStroke) {
|
70
|
-
this.rectObject?.set({
|
96
|
+
this.rectObject?.set({ visible: false });
|
97
|
+
this.ellipseObject.set({ visible: false });
|
71
98
|
}
|
72
99
|
this.setFontFamily(text?.fontFamily ?? "", options?.fontUrl);
|
73
100
|
}
|
74
101
|
this.fire("scaling");
|
75
102
|
},
|
76
|
-
autoChangeFontSize: function (
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
let
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
this.
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
}
|
96
|
-
this.canvas?.renderAll?.();
|
97
|
-
maxLineWidth = Math.max(...this.textObject.__lineWidths);
|
103
|
+
autoChangeFontSize: function (deltaFontSize = 0.1) {
|
104
|
+
const textPath = this.textObject.path;
|
105
|
+
if (!textPath)
|
106
|
+
return;
|
107
|
+
const pathLength = textPath.segmentsInfo?.at(-1)?.length || 0;
|
108
|
+
let low = 0;
|
109
|
+
let high = this.textObject.maxFontSize;
|
110
|
+
let bestFit = this.textObject.fontSize;
|
111
|
+
while (high - low > deltaFontSize) {
|
112
|
+
const mid = (low + high) / 2;
|
113
|
+
this.textObject.set({ fontSize: mid });
|
114
|
+
this.canvas?.renderAll();
|
115
|
+
const textWidth = Math.max(...this.textObject.__lineWidths);
|
116
|
+
if (textWidth <= pathLength) {
|
117
|
+
bestFit = mid;
|
118
|
+
low = mid;
|
119
|
+
}
|
120
|
+
else {
|
121
|
+
high = mid;
|
122
|
+
}
|
98
123
|
}
|
124
|
+
this.textObject.set({ fontSize: bestFit });
|
125
|
+
this.canvas?.renderAll();
|
99
126
|
},
|
100
127
|
setText: function (text) {
|
101
128
|
this.textObject.set({
|
@@ -163,30 +190,34 @@ const CurvedTextClass = fabric_1.fabric.util.createClass(fabric_1.fabric.Group,
|
|
163
190
|
}
|
164
191
|
return this.textObject.get(attribute);
|
165
192
|
},
|
166
|
-
calculateTextPath: function (
|
167
|
-
|
168
|
-
|
169
|
-
const
|
170
|
-
const
|
171
|
-
const
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
193
|
+
calculateTextPath: function () {
|
194
|
+
const { width: layerWidth, height: layerHeight } = this;
|
195
|
+
const rx = layerWidth / 2;
|
196
|
+
const ry = layerHeight / 2;
|
197
|
+
const { startAngle, endAngle, pathSide } = this.textObject;
|
198
|
+
const pathString = (0, utils_2.ellipseArcPath)(rx, ry, startAngle, endAngle, pathSide === "left" ? false : true);
|
199
|
+
this.textObject.set({
|
200
|
+
path: new fabric_1.fabric.Path(pathString, {
|
201
|
+
stroke: "red",
|
202
|
+
strokeWidth: 4,
|
203
|
+
fill: "transparent",
|
204
|
+
visible: this.pathVisible,
|
205
|
+
}),
|
179
206
|
});
|
180
|
-
this.
|
181
|
-
|
182
|
-
|
183
|
-
const
|
207
|
+
this.canvas?.renderAll();
|
208
|
+
},
|
209
|
+
updateEllipsePath() {
|
210
|
+
const { width: layerWidth, height: layerHeight } = this;
|
211
|
+
const rx = layerWidth / 2;
|
212
|
+
const ry = layerHeight / 2;
|
213
|
+
const { startAngle, endAngle, pathSide } = this.textObject;
|
214
|
+
const { x, y } = (0, utils_2.getEllipseArcCenterPoint)(rx, ry, startAngle, endAngle);
|
184
215
|
this.textObject.set({
|
185
|
-
|
186
|
-
|
187
|
-
pathStartOffset,
|
216
|
+
left: pathSide === "left" ? x : -x,
|
217
|
+
top: pathSide === "left" ? y : -y,
|
188
218
|
});
|
189
|
-
this
|
219
|
+
this.ellipseObject.set({ rx, ry, top: -ry, left: -rx });
|
220
|
+
this.canvas?.requestRenderAll();
|
190
221
|
},
|
191
222
|
});
|
192
223
|
const toCurvedTextObject = (object) => {
|
@@ -223,6 +254,8 @@ const toCurvedTextObject = (object) => {
|
|
223
254
|
pathSide: textObject?.pathSide,
|
224
255
|
positionAngle: textObject?.positionAngle,
|
225
256
|
isAllCapital: textObject?.isAllCapital,
|
257
|
+
startAngle: textObject?.startAngle,
|
258
|
+
endAngle: textObject?.endAngle,
|
226
259
|
},
|
227
260
|
};
|
228
261
|
};
|
@@ -0,0 +1,54 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getEllipseArcCenterPoint = exports.ellipseArcPath = void 0;
|
4
|
+
const degreeToRad = (deg) => (deg * Math.PI) / 180;
|
5
|
+
const ellipseArcPath = (rx, ry, startAngle, endAngle, flip = false) => {
|
6
|
+
if (flip) {
|
7
|
+
startAngle = (startAngle + 180) % 360;
|
8
|
+
endAngle = (endAngle + 180) % 360;
|
9
|
+
}
|
10
|
+
const x1 = -rx * Math.cos(degreeToRad(startAngle));
|
11
|
+
const y1 = ry * Math.sin(degreeToRad(startAngle));
|
12
|
+
const x2 = -rx * Math.cos(degreeToRad(endAngle));
|
13
|
+
const y2 = ry * Math.sin(degreeToRad(endAngle));
|
14
|
+
const angleDiff = (endAngle - startAngle + 360) % 360;
|
15
|
+
const largeArcFlag = angleDiff > 180 ? 1 : 0;
|
16
|
+
const sweepFlag = 1;
|
17
|
+
return `M ${x1} ${y1} A ${rx} ${ry} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
|
18
|
+
};
|
19
|
+
exports.ellipseArcPath = ellipseArcPath;
|
20
|
+
const twoPi = 2 * Math.PI;
|
21
|
+
const normalize = (angle) => ((angle % twoPi) + twoPi) % twoPi;
|
22
|
+
function isAngleInArc(theta, a, b) {
|
23
|
+
theta = normalize(theta);
|
24
|
+
a = normalize(a);
|
25
|
+
b = normalize(b);
|
26
|
+
if (a < b)
|
27
|
+
return a <= theta && theta <= b;
|
28
|
+
return a <= theta || theta <= b;
|
29
|
+
}
|
30
|
+
const getEllipseArcCenterPoint = (rx, ry, a, b) => {
|
31
|
+
a = degreeToRad(a);
|
32
|
+
b = degreeToRad(b);
|
33
|
+
const criticalAngles = [0, Math.PI / 2, Math.PI, (3 * Math.PI) / 2];
|
34
|
+
const points = [
|
35
|
+
[rx * Math.cos(a), -ry * Math.sin(a)],
|
36
|
+
[rx * Math.cos(b), -ry * Math.sin(b)],
|
37
|
+
];
|
38
|
+
for (const theta of criticalAngles) {
|
39
|
+
if (isAngleInArc(theta, a, b)) {
|
40
|
+
points.push([rx * Math.cos(theta), -ry * Math.sin(theta)]);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
const xs = points.map((p) => p[0]);
|
44
|
+
const ys = points.map((p) => p[1]);
|
45
|
+
const minX = Math.min(...xs);
|
46
|
+
const maxX = Math.max(...xs);
|
47
|
+
const minY = Math.min(...ys);
|
48
|
+
const maxY = Math.max(...ys);
|
49
|
+
return {
|
50
|
+
x: (minX + maxX) / 2,
|
51
|
+
y: (minY + maxY) / 2,
|
52
|
+
};
|
53
|
+
};
|
54
|
+
exports.getEllipseArcCenterPoint = getEllipseArcCenterPoint;
|