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.
@@ -33,6 +33,8 @@ export declare const toCurvedTextObject: (object: CurvedText) => {
33
33
  pathSide: any;
34
34
  positionAngle: any;
35
35
  isAllCapital: any;
36
+ startAngle: any;
37
+ endAngle: any;
36
38
  };
37
39
  };
38
40
  export default class CurvedText extends fabric.Group {
@@ -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
- const group = new fabric_1.fabric.Group([this.rectObject, this.textObject]);
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
- let width = this.width * this.scaleX;
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(width, height);
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({ strokeWidth: 0 });
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({ strokeWidth: 0 });
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 (changeSpeed = 0.1) {
77
- let { fontSize, lengthRatio, maxFontSize } = this.textObject;
78
- fontSize = Number(fontSize || 0);
79
- lengthRatio = Number(lengthRatio || 0.4);
80
- maxFontSize = Number(maxFontSize || 200);
81
- const length = (0, svg_util_1.getEllipseCircumference)(this.width / 2, this.height / 2) * lengthRatio;
82
- let maxLineWidth = Math.max(...this.textObject.__lineWidths);
83
- while (length <= maxLineWidth) {
84
- fontSize -= changeSpeed;
85
- this.textObject.set({
86
- fontSize,
87
- });
88
- this.canvas?.renderAll?.();
89
- maxLineWidth = Math.max(...this.textObject.__lineWidths);
90
- }
91
- while (maxFontSize > fontSize.toFixed(1) && length > maxLineWidth) {
92
- fontSize += changeSpeed;
93
- this.textObject.set({
94
- fontSize,
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 (width, height) {
167
- let { pathSide, positionAngle } = this.textObject;
168
- positionAngle = Number(positionAngle || 0);
169
- const textHeight = 0;
170
- const rx = (width - textHeight) / 2;
171
- const ry = (height - textHeight) / 2;
172
- const path = new fabric_1.fabric.Path((0, svg_util_1.getEllipsePath)(rx, ry, pathSide), {
173
- fill: undefined,
174
- stroke: "black",
175
- objectCaching: false,
176
- originX: "center",
177
- originY: "center",
178
- visible: this.pathVisible,
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.pathObject = path;
181
- const pathInfo = fabric_1.fabric.util?.getPathSegmentsInfo?.(path.path);
182
- const pathLength = pathInfo?.[(pathInfo?.length ?? 0) - 1]?.length;
183
- const pathStartOffset = ((positionAngle % 360) * pathLength) / 360;
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
- path,
186
- pathLength,
187
- pathStartOffset,
216
+ left: pathSide === "left" ? x : -x,
217
+ top: pathSide === "left" ? y : -y,
188
218
  });
189
- this?.canvas?.renderAll();
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,5 @@
1
+ export declare const ellipseArcPath: (rx: number, ry: number, startAngle: number, endAngle: number, flip?: boolean) => string;
2
+ export declare const getEllipseArcCenterPoint: (rx: number, ry: number, a: number, b: number) => {
3
+ x: number;
4
+ y: number;
5
+ };
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "customized-fabric",
3
- "version": "2.0.20",
3
+ "version": "2.0.22",
4
4
  "description": "Customized fabric",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",