@nasser-sw/fabric 7.0.1-beta3 → 7.0.1-beta4

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.
Files changed (105) hide show
  1. package/0 +0 -0
  2. package/dist/index.js +323 -155
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.min.js +1 -1
  5. package/dist/index.min.js.map +1 -1
  6. package/dist/index.min.mjs +1 -1
  7. package/dist/index.min.mjs.map +1 -1
  8. package/dist/index.mjs +323 -155
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/index.node.cjs +323 -155
  11. package/dist/index.node.cjs.map +1 -1
  12. package/dist/index.node.mjs +323 -155
  13. package/dist/index.node.mjs.map +1 -1
  14. package/dist/package.json.min.mjs +1 -1
  15. package/dist/package.json.mjs +1 -1
  16. package/dist/src/shapes/Line.d.ts +31 -86
  17. package/dist/src/shapes/Line.d.ts.map +1 -1
  18. package/dist/src/shapes/Line.min.mjs +1 -1
  19. package/dist/src/shapes/Line.min.mjs.map +1 -1
  20. package/dist/src/shapes/Line.mjs +323 -154
  21. package/dist/src/shapes/Line.mjs.map +1 -1
  22. package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
  23. package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
  24. package/dist-extensions/src/shapes/Line.d.ts +31 -86
  25. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  26. package/fabric-test-editor.html +157 -8
  27. package/fabric-test2.html +513 -0
  28. package/fabric.ts +182 -182
  29. package/package.json +1 -1
  30. package/src/shapes/Line.ts +372 -158
  31. package/debug/konva/CHANGELOG.md +0 -1474
  32. package/debug/konva/LICENSE +0 -22
  33. package/debug/konva/README.md +0 -205
  34. package/debug/konva/gulpfile.mjs +0 -110
  35. package/debug/konva/package.json +0 -139
  36. package/debug/konva/release.sh +0 -65
  37. package/debug/konva/resources/doc-includes/ContainerParams.txt +0 -6
  38. package/debug/konva/resources/doc-includes/NodeParams.txt +0 -20
  39. package/debug/konva/resources/doc-includes/ShapeParams.txt +0 -53
  40. package/debug/konva/resources/jsdoc.conf.json +0 -28
  41. package/debug/konva/rollup.config.mjs +0 -32
  42. package/debug/konva/src/Animation.ts +0 -237
  43. package/debug/konva/src/BezierFunctions.ts +0 -826
  44. package/debug/konva/src/Canvas.ts +0 -193
  45. package/debug/konva/src/Container.ts +0 -649
  46. package/debug/konva/src/Context.ts +0 -1017
  47. package/debug/konva/src/Core.ts +0 -5
  48. package/debug/konva/src/DragAndDrop.ts +0 -173
  49. package/debug/konva/src/Factory.ts +0 -246
  50. package/debug/konva/src/FastLayer.ts +0 -29
  51. package/debug/konva/src/Global.ts +0 -210
  52. package/debug/konva/src/Group.ts +0 -31
  53. package/debug/konva/src/Layer.ts +0 -546
  54. package/debug/konva/src/Node.ts +0 -3477
  55. package/debug/konva/src/PointerEvents.ts +0 -67
  56. package/debug/konva/src/Shape.ts +0 -2081
  57. package/debug/konva/src/Stage.ts +0 -1000
  58. package/debug/konva/src/Tween.ts +0 -811
  59. package/debug/konva/src/Util.ts +0 -1123
  60. package/debug/konva/src/Validators.ts +0 -210
  61. package/debug/konva/src/_CoreInternals.ts +0 -85
  62. package/debug/konva/src/_FullInternals.ts +0 -171
  63. package/debug/konva/src/canvas-backend.ts +0 -36
  64. package/debug/konva/src/filters/Blur.ts +0 -388
  65. package/debug/konva/src/filters/Brighten.ts +0 -48
  66. package/debug/konva/src/filters/Brightness.ts +0 -30
  67. package/debug/konva/src/filters/Contrast.ts +0 -75
  68. package/debug/konva/src/filters/Emboss.ts +0 -207
  69. package/debug/konva/src/filters/Enhance.ts +0 -154
  70. package/debug/konva/src/filters/Grayscale.ts +0 -25
  71. package/debug/konva/src/filters/HSL.ts +0 -108
  72. package/debug/konva/src/filters/HSV.ts +0 -106
  73. package/debug/konva/src/filters/Invert.ts +0 -23
  74. package/debug/konva/src/filters/Kaleidoscope.ts +0 -274
  75. package/debug/konva/src/filters/Mask.ts +0 -220
  76. package/debug/konva/src/filters/Noise.ts +0 -44
  77. package/debug/konva/src/filters/Pixelate.ts +0 -107
  78. package/debug/konva/src/filters/Posterize.ts +0 -46
  79. package/debug/konva/src/filters/RGB.ts +0 -82
  80. package/debug/konva/src/filters/RGBA.ts +0 -103
  81. package/debug/konva/src/filters/Sepia.ts +0 -27
  82. package/debug/konva/src/filters/Solarize.ts +0 -29
  83. package/debug/konva/src/filters/Threshold.ts +0 -44
  84. package/debug/konva/src/index.ts +0 -3
  85. package/debug/konva/src/shapes/Arc.ts +0 -176
  86. package/debug/konva/src/shapes/Arrow.ts +0 -231
  87. package/debug/konva/src/shapes/Circle.ts +0 -76
  88. package/debug/konva/src/shapes/Ellipse.ts +0 -121
  89. package/debug/konva/src/shapes/Image.ts +0 -319
  90. package/debug/konva/src/shapes/Label.ts +0 -386
  91. package/debug/konva/src/shapes/Line.ts +0 -364
  92. package/debug/konva/src/shapes/Path.ts +0 -1013
  93. package/debug/konva/src/shapes/Rect.ts +0 -79
  94. package/debug/konva/src/shapes/RegularPolygon.ts +0 -167
  95. package/debug/konva/src/shapes/Ring.ts +0 -94
  96. package/debug/konva/src/shapes/Sprite.ts +0 -370
  97. package/debug/konva/src/shapes/Star.ts +0 -125
  98. package/debug/konva/src/shapes/Text.ts +0 -1065
  99. package/debug/konva/src/shapes/TextPath.ts +0 -583
  100. package/debug/konva/src/shapes/Transformer.ts +0 -1889
  101. package/debug/konva/src/shapes/Wedge.ts +0 -129
  102. package/debug/konva/src/skia-backend.ts +0 -35
  103. package/debug/konva/src/types.ts +0 -84
  104. package/debug/konva/tsconfig.json +0 -31
  105. package/debug/konva/tsconfig.test.json +0 -7
@@ -10,12 +10,12 @@ import type { ObjectEvents } from '../EventTypeDefs';
10
10
  import { makeBoundingBoxFromPoints } from '../util';
11
11
  import { CENTER, LEFT, TOP } from '../constants';
12
12
  import type { CSSRules } from '../parser/typedefs';
13
-
14
- // @TODO this code is terrible and Line should be a special case of polyline.
13
+ import { Control } from '../controls/Control';
14
+ import type { TPointerEvent, Transform } from '../EventTypeDefs';
15
15
 
16
16
  const coordProps = ['x1', 'x2', 'y1', 'y2'] as const;
17
17
 
18
- interface UniqueLineProps {
18
+ interface UniqueLineCoords {
19
19
  x1: number;
20
20
  x2: number;
21
21
  y1: number;
@@ -24,149 +24,402 @@ interface UniqueLineProps {
24
24
 
25
25
  export interface SerializedLineProps
26
26
  extends SerializedObjectProps,
27
- UniqueLineProps {}
28
-
29
- /**
30
- * A Class to draw a line
31
- * A bunch of methods will be added to Polyline to handle the line case
32
- * The line class is very strange to work with, is all special, it hardly aligns
33
- * to what a developer want everytime there is an angle
34
- * @deprecated
35
- */
36
- export class Line<
27
+ UniqueLineCoords {}
28
+
29
+ export class Line<
37
30
  Props extends TOptions<FabricObjectProps> = Partial<FabricObjectProps>,
38
31
  SProps extends SerializedLineProps = SerializedLineProps,
39
- EventSpec extends ObjectEvents = ObjectEvents,
32
+ EventSpec extends ObjectEvents = ObjectEvents
40
33
  >
41
34
  extends FabricObject<Props, SProps, EventSpec>
42
- implements UniqueLineProps
35
+ implements UniqueLineCoords
43
36
  {
44
- /**
45
- * x value or first line edge
46
- * @type number
47
- */
48
37
  declare x1: number;
49
-
50
- /**
51
- * y value or first line edge
52
- * @type number
53
- */
54
38
  declare y1: number;
55
-
56
- /**
57
- * x value or second line edge
58
- * @type number
59
- */
60
39
  declare x2: number;
61
-
62
- /**
63
- * y value or second line edge
64
- * @type number
65
- */
66
40
  declare y2: number;
67
41
 
68
- static type = 'Line';
42
+ hitStrokeWidth: number | 'auto' = 'auto';
69
43
 
44
+ private _updatingEndpoints = false;
45
+ private _useEndpointCoords = true;
46
+
47
+ static type = 'Line';
70
48
  static cacheProperties = [...cacheProperties, ...coordProps];
71
- /**
72
- * Constructor
73
- * @param {Array} [points] Array of points
74
- * @param {Object} [options] Options object
75
- * @return {Line} thisArg
76
- */
77
- constructor([x1, y1, x2, y2] = [0, 0, 0, 0], options: Partial<Props> = {}) {
49
+
50
+ constructor([x1, y1, x2, y2] = [0, 0, 100, 0], options: Partial<Props & {hitStrokeWidth?: number | 'auto'}> = {}) {
78
51
  super();
79
- Object.assign(this, Line.ownDefaults);
80
52
  this.setOptions(options);
81
53
  this.x1 = x1;
82
54
  this.x2 = x2;
83
55
  this.y1 = y1;
84
56
  this.y2 = y2;
57
+
58
+ if (options.hitStrokeWidth !== undefined) {
59
+ this.hitStrokeWidth = options.hitStrokeWidth;
60
+ }
61
+
62
+ this.hasBorders = false;
63
+ this.hasControls = true;
64
+ this.selectable = true;
65
+ this.hoverCursor = 'move';
66
+ this.perPixelTargetFind = false;
67
+ this.strokeLineCap = 'butt';
68
+
85
69
  this._setWidthHeight();
86
70
  const { left, top } = options;
87
71
  typeof left === 'number' && this.set(LEFT, left);
88
72
  typeof top === 'number' && this.set(TOP, top);
73
+ this._setupLineControls();
74
+ }
75
+
76
+ _setupLineControls() {
77
+ this.controls = {
78
+ p1: new Control({
79
+ x: 0,
80
+ y: 0,
81
+ cursorStyle: 'move',
82
+ actionHandler: this._endpointActionHandler.bind(this),
83
+ positionHandler: this._p1PositionHandler.bind(this),
84
+ render: this._renderEndpointControl.bind(this),
85
+ sizeX: 12,
86
+ sizeY: 12,
87
+ }),
88
+ p2: new Control({
89
+ x: 0,
90
+ y: 0,
91
+ cursorStyle: 'move',
92
+ actionHandler: this._endpointActionHandler.bind(this),
93
+ positionHandler: this._p2PositionHandler.bind(this),
94
+ render: this._renderEndpointControl.bind(this),
95
+ sizeX: 12,
96
+ sizeY: 12,
97
+ }),
98
+ };
99
+ }
100
+
101
+ _p1PositionHandler() {
102
+ const vpt = this.canvas?.viewportTransform || [1, 0, 0, 1, 0, 0];
103
+ return new Point(this.x1, this.y1).transform(vpt);
104
+ }
105
+
106
+ _p2PositionHandler() {
107
+ const vpt = this.canvas?.viewportTransform || [1, 0, 0, 1, 0, 0];
108
+ return new Point(this.x2, this.y2).transform(vpt);
109
+ }
110
+
111
+ _renderEndpointControl(
112
+ ctx: CanvasRenderingContext2D,
113
+ left: number,
114
+ top: number
115
+ ) {
116
+ const size = 12;
117
+ ctx.save();
118
+ ctx.fillStyle = '#007bff';
119
+ ctx.strokeStyle = '#ffffff';
120
+ ctx.lineWidth = 2;
121
+ ctx.beginPath();
122
+ ctx.arc(left, top, size / 2, 0, 2 * Math.PI);
123
+ ctx.fill();
124
+ ctx.stroke();
125
+ ctx.restore();
126
+ }
127
+
128
+ drawBorders(ctx: CanvasRenderingContext2D, styleOverride: any = {}) {
129
+ if (this._useEndpointCoords) {
130
+ this._drawLineBorders(ctx, styleOverride);
131
+ return this;
132
+ }
133
+ return super.drawBorders(ctx, styleOverride, {});
134
+ }
135
+
136
+ _drawLineBorders(ctx: CanvasRenderingContext2D, styleOverride: any = {}) {
137
+ const vpt = this.canvas?.viewportTransform || [1, 0, 0, 1, 0, 0];
138
+ ctx.save();
139
+ ctx.setTransform(vpt[0], vpt[1], vpt[2], vpt[3], vpt[4], vpt[5]);
140
+ ctx.strokeStyle =
141
+ styleOverride.borderColor || this.borderColor || 'rgba(100, 200, 200, 0.5)';
142
+ ctx.lineWidth = (this.strokeWidth || 1) + 5;
143
+ ctx.lineCap = this.strokeLineCap || 'butt';
144
+ ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
145
+ ctx.beginPath();
146
+ ctx.moveTo(this.x1, this.y1);
147
+ ctx.lineTo(this.x2, this.y2);
148
+ ctx.stroke();
149
+ ctx.restore();
150
+ }
151
+
152
+ _renderControls(ctx: CanvasRenderingContext2D, styleOverride: any = {}) {
153
+ ctx.save();
154
+ ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
155
+ this.drawControls(ctx, styleOverride);
156
+ ctx.restore();
157
+ }
158
+
159
+ getBoundingRect() {
160
+ if (this._useEndpointCoords) {
161
+ const { x1, y1, x2, y2 } = this;
162
+ const effectiveStrokeWidth =
163
+ this.hitStrokeWidth === 'auto'
164
+ ? this.strokeWidth
165
+ : this.hitStrokeWidth;
166
+ const padding = Math.max(effectiveStrokeWidth / 2 + 5, 10);
167
+ return {
168
+ left: Math.min(x1, x2) - padding,
169
+ top: Math.min(y1, y2) - padding,
170
+ width: Math.abs(x2 - x1) + padding * 2 || padding * 2,
171
+ height: Math.abs(y2 - y1) + padding * 2 || padding * 2,
172
+ };
173
+ }
174
+ return super.getBoundingRect();
175
+ }
176
+
177
+ setCoords() {
178
+ if (this._useEndpointCoords) {
179
+ const minX = Math.min(this.x1, this.x2);
180
+ const maxX = Math.max(this.x1, this.x2);
181
+ const minY = Math.min(this.y1, this.y2);
182
+ const maxY = Math.max(this.y1, this.y2);
183
+ const effectiveStrokeWidth =
184
+ this.hitStrokeWidth === 'auto'
185
+ ? this.strokeWidth
186
+ : this.hitStrokeWidth;
187
+ const hitPadding = Math.max(effectiveStrokeWidth / 2 + 5, 10);
188
+ this.left = minX - hitPadding + (maxX - minX + hitPadding * 2) / 2;
189
+ this.top = minY - hitPadding + (maxY - minY + hitPadding * 2) / 2;
190
+ this.width = Math.abs(this.x2 - this.x1) + hitPadding * 2;
191
+ this.height = Math.abs(this.y2 - this.y1) + hitPadding * 2;
192
+ }
193
+ super.setCoords();
194
+ }
195
+
196
+ getCoords(): [Point, Point, Point, Point] {
197
+ if (this._useEndpointCoords) {
198
+ const deltaX = this.x2 - this.x1;
199
+ const deltaY = this.y2 - this.y1;
200
+ const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
201
+
202
+ if (length === 0) {
203
+ return super.getCoords() as [Point, Point, Point, Point];
204
+ }
205
+
206
+ const effectiveStrokeWidth = this.hitStrokeWidth === 'auto'
207
+ ? this.strokeWidth
208
+ : this.hitStrokeWidth;
209
+ const halfWidth = Math.max(effectiveStrokeWidth / 2 + 2, 5);
210
+
211
+ // Unit vector perpendicular to line
212
+ const perpX = -deltaY / length;
213
+ const perpY = deltaX / length;
214
+
215
+ // Four corners of oriented rectangle
216
+ return [
217
+ new Point(this.x1 + perpX * halfWidth, this.y1 + perpY * halfWidth),
218
+ new Point(this.x2 + perpX * halfWidth, this.y2 + perpY * halfWidth),
219
+ new Point(this.x2 - perpX * halfWidth, this.y2 - perpY * halfWidth),
220
+ new Point(this.x1 - perpX * halfWidth, this.y1 - perpY * halfWidth),
221
+ ];
222
+ }
223
+ return super.getCoords() as [Point, Point, Point, Point];
224
+ }
225
+
226
+ containsPoint(point: Point): boolean {
227
+ if (this._useEndpointCoords) {
228
+ if (this.canvas?.getActiveObject() === this) {
229
+ return super.containsPoint(point);
230
+ }
231
+ const distance = this._distanceToLineSegment(point.x, point.y);
232
+ const effectiveStrokeWidth = this.hitStrokeWidth === 'auto'
233
+ ? this.strokeWidth
234
+ : this.hitStrokeWidth || 1;
235
+
236
+ const tolerance = Math.max(effectiveStrokeWidth / 2 + 2, 5);
237
+ return distance <= tolerance;
238
+ }
239
+ return super.containsPoint(point);
240
+ }
241
+
242
+ _distanceToLineSegment(px: number, py: number): number {
243
+ const x1 = this.x1, y1 = this.y1, x2 = this.x2, y2 = this.y2;
244
+
245
+ const pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
246
+ if (pd2 === 0) {
247
+ return Math.sqrt((px - x1) * (px - x1) + (py - y1) * (py - y1));
248
+ }
249
+
250
+ const u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2;
251
+
252
+ let closestX: number, closestY: number;
253
+ if (u < 0) {
254
+ closestX = x1;
255
+ closestY = y1;
256
+ } else if (u > 1) {
257
+ closestX = x2;
258
+ closestY = y2;
259
+ } else {
260
+ closestX = x1 + u * (x2 - x1);
261
+ closestY = y1 + u * (y2 - y1);
262
+ }
263
+
264
+ return Math.sqrt((px - closestX) * (px - closestX) + (py - closestY) * (py - closestY));
265
+ }
266
+
267
+ _endpointActionHandler(
268
+ eventData: TPointerEvent,
269
+ transformData: Transform,
270
+ x: number,
271
+ y: number
272
+ ) {
273
+ const controlKey = transformData.corner;
274
+ const pointer = new Point(x, y);
275
+ let newX = pointer.x;
276
+ let newY = pointer.y;
277
+
278
+ if (eventData.shiftKey) {
279
+ const otherControl = controlKey === 'p1' ? 'p2' : 'p1';
280
+ const otherX = this[otherControl === 'p1' ? 'x1' : 'x2'];
281
+ const otherY = this[otherControl === 'p1' ? 'y1' : 'y2'];
282
+ const snapped = this._snapToAngle(otherX, otherY, newX, newY);
283
+ newX = snapped.x;
284
+ newY = snapped.y;
285
+ }
286
+
287
+ if (this._useEndpointCoords) {
288
+ if (controlKey === 'p1') {
289
+ this.x1 = newX;
290
+ this.y1 = newY;
291
+ } else if (controlKey === 'p2') {
292
+ this.x2 = newX;
293
+ this.y2 = newY;
294
+ }
295
+ this.dirty = true;
296
+ this.setCoords();
297
+ this.canvas?.requestRenderAll();
298
+ return true;
299
+ }
300
+
301
+ // Fallback for old system
302
+ this._updatingEndpoints = true;
303
+ if (controlKey === 'p1') {
304
+ this.x1 = newX;
305
+ this.y1 = newY;
306
+ } else if (controlKey === 'p2') {
307
+ this.x2 = newX;
308
+ this.y2 = newY;
309
+ }
310
+ this._setWidthHeight();
311
+ this.dirty = true;
312
+ this._updatingEndpoints = false;
313
+ this.canvas?.requestRenderAll();
314
+ this.fire('modified', { transform: transformData, target: this, e: eventData });
315
+ return true;
89
316
  }
90
317
 
91
- /**
92
- * @private
93
- * @param {Object} [options] Options
94
- */
95
- _setWidthHeight() {
96
- const { x1, y1, x2, y2 } = this;
97
- this.width = Math.abs(x2 - x1);
98
- this.height = Math.abs(y2 - y1);
99
- const { left, top, width, height } = makeBoundingBoxFromPoints([
100
- { x: x1, y: y1 },
101
- { x: x2, y: y2 },
102
- ]);
103
- const position = new Point(left + width / 2, top + height / 2);
104
- this.setPositionByOrigin(position, CENTER, CENTER);
318
+ _snapToAngle(
319
+ fromX: number,
320
+ fromY: number,
321
+ toX: number,
322
+ toY: number
323
+ ): { x: number; y: number } {
324
+ const deltaX = toX - fromX;
325
+ const deltaY = toY - fromY;
326
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
327
+ if (distance === 0) return { x: toX, y: toY };
328
+ let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
329
+ const snapIncrement = 15;
330
+ const snappedAngle = Math.round(angle / snapIncrement) * snapIncrement;
331
+ const snappedRadians = snappedAngle * (Math.PI / 180);
332
+ return {
333
+ x: fromX + Math.cos(snappedRadians) * distance,
334
+ y: fromY + Math.sin(snappedRadians) * distance,
335
+ };
336
+ }
337
+
338
+ _setWidthHeight(skipReposition = false) {
339
+ this.width = Math.abs(this.x2 - this.x1) || 1;
340
+ this.height = Math.abs(this.y2 - this.y1) || 1;
341
+ if (!skipReposition && !this._updatingEndpoints) {
342
+ const { left, top, width, height } = makeBoundingBoxFromPoints([
343
+ { x: this.x1, y: this.y1 },
344
+ { x: this.x2, y: this.y2 },
345
+ ]);
346
+ this.setPositionByOrigin(
347
+ new Point(left + width / 2, top + height / 2),
348
+ CENTER,
349
+ CENTER
350
+ );
351
+ }
105
352
  }
106
353
 
107
- /**
108
- * @private
109
- * @param {String} key
110
- * @param {*} value
111
- */
112
354
  _set(key: string, value: any) {
355
+ const oldLeft = this.left;
356
+ const oldTop = this.top;
113
357
  super._set(key, value);
114
- if (coordProps.includes(key as keyof UniqueLineProps)) {
115
- // this doesn't make sense very much, since setting x1 when top or left
116
- // are already set, is just going to show a strange result since the
117
- // line will move way more than the developer expect.
118
- // in fabric5 it worked only when the line didn't have extra transformations,
119
- // in fabric6 too. With extra transform they behave bad in different ways.
120
- // This needs probably a good rework or a tutorial if you have to create a dynamic line
358
+ if (coordProps.includes(key as keyof UniqueLineCoords)) {
121
359
  this._setWidthHeight();
360
+ this.dirty = true;
361
+ }
362
+ if ((key === 'left' || key === 'top') && this.canvas && !this._updatingEndpoints) {
363
+ const deltaX = this.left - oldLeft;
364
+ const deltaY = this.top - oldTop;
365
+ if (deltaX !== 0 || deltaY !== 0) {
366
+ this._updatingEndpoints = true;
367
+ this.x1 += deltaX;
368
+ this.y1 += deltaY;
369
+ this.x2 += deltaX;
370
+ this.y2 += deltaY;
371
+ this._updatingEndpoints = false;
372
+ }
122
373
  }
123
374
  return this;
124
375
  }
125
376
 
126
- /**
127
- * @private
128
- * @param {CanvasRenderingContext2D} ctx Context to render on
129
- */
130
- _render(ctx: CanvasRenderingContext2D) {
377
+ render(ctx: CanvasRenderingContext2D) {
378
+ if (this._useEndpointCoords) {
379
+ this._renderDirectly(ctx);
380
+ return;
381
+ }
382
+ super.render(ctx);
383
+ }
384
+
385
+ _renderDirectly(ctx: CanvasRenderingContext2D) {
386
+ if (!this.visible) return;
387
+ ctx.save();
388
+ const vpt = this.canvas?.viewportTransform || [1, 0, 0, 1, 0, 0];
389
+ ctx.transform(vpt[0], vpt[1], vpt[2], vpt[3], vpt[4], vpt[5]);
390
+ ctx.globalAlpha = this.opacity;
391
+ ctx.strokeStyle = this.stroke?.toString() || '#000';
392
+ ctx.lineWidth = this.strokeWidth;
393
+ ctx.lineCap = this.strokeLineCap || 'butt';
131
394
  ctx.beginPath();
395
+ ctx.moveTo(this.x1, this.y1);
396
+ ctx.lineTo(this.x2, this.y2);
397
+ ctx.stroke();
398
+ ctx.restore();
399
+ }
132
400
 
401
+ _render(ctx: CanvasRenderingContext2D) {
402
+ if (this._useEndpointCoords) return;
403
+ ctx.beginPath();
133
404
  const p = this.calcLinePoints();
134
405
  ctx.moveTo(p.x1, p.y1);
135
406
  ctx.lineTo(p.x2, p.y2);
136
-
137
407
  ctx.lineWidth = this.strokeWidth;
138
-
139
- // TODO: test this
140
- // make sure setting "fill" changes color of a line
141
- // (by copying fillStyle to strokeStyle, since line is stroked, not filled)
142
408
  const origStrokeStyle = ctx.strokeStyle;
143
409
  if (isFiller(this.stroke)) {
144
410
  ctx.strokeStyle = this.stroke.toLive(ctx)!;
145
- } else {
146
- ctx.strokeStyle = this.stroke ?? ctx.fillStyle;
147
411
  }
148
412
  this.stroke && this._renderStroke(ctx);
149
413
  ctx.strokeStyle = origStrokeStyle;
150
414
  }
151
415
 
152
- /**
153
- * This function is an helper for svg import. it returns the center of the object in the svg
154
- * untransformed coordinates
155
- * @private
156
- * @return {Point} center point from element coordinates
157
- */
158
416
  _findCenterFromElement(): Point {
159
417
  return new Point((this.x1 + this.x2) / 2, (this.y1 + this.y2) / 2);
160
418
  }
161
419
 
162
- /**
163
- * Returns object representation of an instance
164
- * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
165
- * @return {Object} object representation of an instance
166
- */
167
- toObject<
420
+ toObject<
168
421
  T extends Omit<Props & TClassProperties<this>, keyof SProps>,
169
- K extends keyof T = never,
422
+ K extends keyof T = never
170
423
  >(propertiesToInclude: K[] = []): Pick<T, K> & SProps {
171
424
  return {
172
425
  ...super.toObject(propertiesToInclude),
@@ -174,54 +427,37 @@ export class Line<
174
427
  };
175
428
  }
176
429
 
177
- /*
178
- * Calculate object dimensions from its properties
179
- * @private
180
- */
181
430
  _getNonTransformedDimensions(): Point {
182
431
  const dim = super._getNonTransformedDimensions();
183
- if (this.strokeLineCap === 'butt') {
184
- if (this.width === 0) {
185
- dim.y -= this.strokeWidth;
186
- }
187
- if (this.height === 0) {
188
- dim.x -= this.strokeWidth;
189
- }
432
+ if (this.strokeLineCap === 'round') {
433
+ dim.x += this.strokeWidth;
434
+ dim.y += this.strokeWidth;
190
435
  }
191
436
  return dim;
192
437
  }
193
438
 
194
- /**
195
- * Recalculates line points given width and height
196
- * Those points are simply placed around the center,
197
- * This is not useful outside internal render functions and svg output
198
- * Is not meant to be for the developer.
199
- * @private
200
- */
201
- calcLinePoints(): UniqueLineProps {
439
+ calcLinePoints(): UniqueLineCoords {
440
+ if (this._updatingEndpoints) {
441
+ const centerX = (this.x1 + this.x2) / 2;
442
+ const centerY = (this.y1 + this.y2) / 2;
443
+ return {
444
+ x1: this.x1 - centerX,
445
+ y1: this.y1 - centerY,
446
+ x2: this.x2 - centerX,
447
+ y2: this.y2 - centerY,
448
+ };
449
+ }
202
450
  const { x1: _x1, x2: _x2, y1: _y1, y2: _y2, width, height } = this;
203
- const xMult = _x1 <= _x2 ? -1 : 1,
204
- yMult = _y1 <= _y2 ? -1 : 1,
205
- x1 = (xMult * width) / 2,
206
- y1 = (yMult * height) / 2,
207
- x2 = (xMult * -width) / 2,
208
- y2 = (yMult * -height) / 2;
209
-
451
+ const xMult = _x1 <= _x2 ? -1 : 1;
452
+ const yMult = _y1 <= _y2 ? -1 : 1;
210
453
  return {
211
- x1,
212
- x2,
213
- y1,
214
- y2,
454
+ x1: (xMult * width) / 2,
455
+ y1: (yMult * height) / 2,
456
+ x2: (xMult * -width) / 2,
457
+ y2: (yMult * -height) / 2,
215
458
  };
216
459
  }
217
460
 
218
- /* _FROM_SVG_START_ */
219
-
220
- /**
221
- * Returns svg representation of an instance
222
- * @return {Array} an array of strings with the specific svg representation
223
- * of the instance
224
- */
225
461
  _toSVG() {
226
462
  const { x1, x2, y1, y2 } = this.calcLinePoints();
227
463
  return [
@@ -231,22 +467,12 @@ export class Line<
231
467
  ];
232
468
  }
233
469
 
234
- /**
235
- * List of attribute names to account for when parsing SVG element (used by {@link Line.fromElement})
236
- * @see http://www.w3.org/TR/SVG/shapes.html#LineElement
237
- */
238
470
  static ATTRIBUTE_NAMES = SHARED_ATTRIBUTES.concat(coordProps);
239
471
 
240
- /**
241
- * Returns Line instance from an SVG element
242
- * @param {HTMLElement} element Element to parse
243
- * @param {Object} [options] Options object
244
- * @param {Function} [callback] callback function invoked after parsing
245
- */
246
472
  static async fromElement(
247
473
  element: HTMLElement,
248
474
  options?: Abortable,
249
- cssRules?: CSSRules,
475
+ cssRules?: CSSRules
250
476
  ) {
251
477
  const {
252
478
  x1 = 0,
@@ -258,14 +484,7 @@ export class Line<
258
484
  return new this([x1, y1, x2, y2], parsedAttributes);
259
485
  }
260
486
 
261
- /* _FROM_SVG_END_ */
262
-
263
- /**
264
- * Returns Line instance from an object representation
265
- * @param {Object} object Object to create an instance from
266
- * @returns {Promise<Line>}
267
- */
268
- static fromObject<T extends TOptions<SerializedLineProps>>({
487
+ static fromObject<T extends TOptions<SerializedLineProps>>({
269
488
  x1,
270
489
  y1,
271
490
  x2,
@@ -273,16 +492,11 @@ export class Line<
273
492
  ...object
274
493
  }: T) {
275
494
  return this._fromObject<Line>(
276
- {
277
- ...object,
278
- points: [x1, y1, x2, y2],
279
- },
280
- {
281
- extraParam: 'points',
282
- },
495
+ { ...object, points: [x1, y1, x2, y2] },
496
+ { extraParam: 'points' }
283
497
  );
284
498
  }
285
499
  }
286
500
 
287
501
  classRegistry.setClass(Line);
288
- classRegistry.setSVGClass(Line);
502
+ classRegistry.setSVGClass(Line);