customized-fabric 2.0.21 → 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(),
@@ -71,44 +82,47 @@ const CurvedTextClass = fabric_1.fabric.util.createClass(fabric_1.fabric.Group,
71
82
  }
72
83
  this.set(attributes);
73
84
  this.rectObject.set(attributes);
74
- this.calculateTextPath(width, height);
85
+ this.calculateTextPath();
75
86
  this.autoChangeFontSize(0.1);
87
+ this.updateEllipsePath();
76
88
  this.canvas?.renderAll?.();
77
89
  });
78
90
  if (options?.isOriginal) {
79
- this.rectObject?.set({ strokeWidth: 0 });
91
+ this.rectObject?.set({ visible: false });
92
+ this.ellipseObject.set({ visible: false });
80
93
  }
81
94
  else {
82
95
  if (options?.hideStroke) {
83
- this.rectObject?.set({ strokeWidth: 0 });
96
+ this.rectObject?.set({ visible: false });
97
+ this.ellipseObject.set({ visible: false });
84
98
  }
85
99
  this.setFontFamily(text?.fontFamily ?? "", options?.fontUrl);
86
100
  }
87
101
  this.fire("scaling");
88
102
  },
89
- autoChangeFontSize: function (changeSpeed = 0.1) {
90
- let { fontSize, lengthRatio, maxFontSize } = this.textObject;
91
- fontSize = Number(fontSize || 0);
92
- lengthRatio = Number(lengthRatio || 0.4);
93
- maxFontSize = Number(maxFontSize || 200);
94
- const length = (0, svg_util_1.getEllipseCircumference)(this.width / 2, this.height / 2) * lengthRatio;
95
- let maxLineWidth = Math.max(...this.textObject.__lineWidths);
96
- while (length <= maxLineWidth) {
97
- fontSize -= changeSpeed;
98
- this.textObject.set({
99
- fontSize,
100
- });
101
- this.canvas?.renderAll?.();
102
- maxLineWidth = Math.max(...this.textObject.__lineWidths);
103
- }
104
- while (maxFontSize > fontSize.toFixed(1) && length > maxLineWidth) {
105
- fontSize += changeSpeed;
106
- this.textObject.set({
107
- fontSize,
108
- });
109
- this.canvas?.renderAll?.();
110
- 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
+ }
111
123
  }
124
+ this.textObject.set({ fontSize: bestFit });
125
+ this.canvas?.renderAll();
112
126
  },
113
127
  setText: function (text) {
114
128
  this.textObject.set({
@@ -176,30 +190,34 @@ const CurvedTextClass = fabric_1.fabric.util.createClass(fabric_1.fabric.Group,
176
190
  }
177
191
  return this.textObject.get(attribute);
178
192
  },
179
- calculateTextPath: function (width, height) {
180
- let { pathSide, positionAngle } = this.textObject;
181
- positionAngle = Number(positionAngle || 0);
182
- const textHeight = 0;
183
- const rx = (width - textHeight) / 2;
184
- const ry = (height - textHeight) / 2;
185
- const path = new fabric_1.fabric.Path((0, svg_util_1.getEllipsePath)(rx, ry, pathSide), {
186
- fill: undefined,
187
- stroke: "black",
188
- objectCaching: false,
189
- originX: "center",
190
- originY: "center",
191
- 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
+ }),
192
206
  });
193
- this.pathObject = path;
194
- const pathInfo = fabric_1.fabric.util?.getPathSegmentsInfo?.(path.path);
195
- const pathLength = pathInfo?.[(pathInfo?.length ?? 0) - 1]?.length;
196
- 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);
197
215
  this.textObject.set({
198
- path,
199
- pathLength,
200
- pathStartOffset,
216
+ left: pathSide === "left" ? x : -x,
217
+ top: pathSide === "left" ? y : -y,
201
218
  });
202
- this?.canvas?.renderAll();
219
+ this.ellipseObject.set({ rx, ry, top: -ry, left: -rx });
220
+ this.canvas?.requestRenderAll();
203
221
  },
204
222
  });
205
223
  const toCurvedTextObject = (object) => {
@@ -236,6 +254,8 @@ const toCurvedTextObject = (object) => {
236
254
  pathSide: textObject?.pathSide,
237
255
  positionAngle: textObject?.positionAngle,
238
256
  isAllCapital: textObject?.isAllCapital,
257
+ startAngle: textObject?.startAngle,
258
+ endAngle: textObject?.endAngle,
239
259
  },
240
260
  };
241
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.21",
3
+ "version": "2.0.22",
4
4
  "description": "Customized fabric",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",