@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.
- package/0 +0 -0
- package/dist/index.js +323 -155
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +323 -155
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +323 -155
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +323 -155
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.json.min.mjs +1 -1
- package/dist/package.json.mjs +1 -1
- package/dist/src/shapes/Line.d.ts +31 -86
- package/dist/src/shapes/Line.d.ts.map +1 -1
- package/dist/src/shapes/Line.min.mjs +1 -1
- package/dist/src/shapes/Line.min.mjs.map +1 -1
- package/dist/src/shapes/Line.mjs +323 -154
- package/dist/src/shapes/Line.mjs.map +1 -1
- package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
- package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
- package/dist-extensions/src/shapes/Line.d.ts +31 -86
- package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
- package/fabric-test-editor.html +157 -8
- package/fabric-test2.html +513 -0
- package/fabric.ts +182 -182
- package/package.json +1 -1
- package/src/shapes/Line.ts +372 -158
- package/debug/konva/CHANGELOG.md +0 -1474
- package/debug/konva/LICENSE +0 -22
- package/debug/konva/README.md +0 -205
- package/debug/konva/gulpfile.mjs +0 -110
- package/debug/konva/package.json +0 -139
- package/debug/konva/release.sh +0 -65
- package/debug/konva/resources/doc-includes/ContainerParams.txt +0 -6
- package/debug/konva/resources/doc-includes/NodeParams.txt +0 -20
- package/debug/konva/resources/doc-includes/ShapeParams.txt +0 -53
- package/debug/konva/resources/jsdoc.conf.json +0 -28
- package/debug/konva/rollup.config.mjs +0 -32
- package/debug/konva/src/Animation.ts +0 -237
- package/debug/konva/src/BezierFunctions.ts +0 -826
- package/debug/konva/src/Canvas.ts +0 -193
- package/debug/konva/src/Container.ts +0 -649
- package/debug/konva/src/Context.ts +0 -1017
- package/debug/konva/src/Core.ts +0 -5
- package/debug/konva/src/DragAndDrop.ts +0 -173
- package/debug/konva/src/Factory.ts +0 -246
- package/debug/konva/src/FastLayer.ts +0 -29
- package/debug/konva/src/Global.ts +0 -210
- package/debug/konva/src/Group.ts +0 -31
- package/debug/konva/src/Layer.ts +0 -546
- package/debug/konva/src/Node.ts +0 -3477
- package/debug/konva/src/PointerEvents.ts +0 -67
- package/debug/konva/src/Shape.ts +0 -2081
- package/debug/konva/src/Stage.ts +0 -1000
- package/debug/konva/src/Tween.ts +0 -811
- package/debug/konva/src/Util.ts +0 -1123
- package/debug/konva/src/Validators.ts +0 -210
- package/debug/konva/src/_CoreInternals.ts +0 -85
- package/debug/konva/src/_FullInternals.ts +0 -171
- package/debug/konva/src/canvas-backend.ts +0 -36
- package/debug/konva/src/filters/Blur.ts +0 -388
- package/debug/konva/src/filters/Brighten.ts +0 -48
- package/debug/konva/src/filters/Brightness.ts +0 -30
- package/debug/konva/src/filters/Contrast.ts +0 -75
- package/debug/konva/src/filters/Emboss.ts +0 -207
- package/debug/konva/src/filters/Enhance.ts +0 -154
- package/debug/konva/src/filters/Grayscale.ts +0 -25
- package/debug/konva/src/filters/HSL.ts +0 -108
- package/debug/konva/src/filters/HSV.ts +0 -106
- package/debug/konva/src/filters/Invert.ts +0 -23
- package/debug/konva/src/filters/Kaleidoscope.ts +0 -274
- package/debug/konva/src/filters/Mask.ts +0 -220
- package/debug/konva/src/filters/Noise.ts +0 -44
- package/debug/konva/src/filters/Pixelate.ts +0 -107
- package/debug/konva/src/filters/Posterize.ts +0 -46
- package/debug/konva/src/filters/RGB.ts +0 -82
- package/debug/konva/src/filters/RGBA.ts +0 -103
- package/debug/konva/src/filters/Sepia.ts +0 -27
- package/debug/konva/src/filters/Solarize.ts +0 -29
- package/debug/konva/src/filters/Threshold.ts +0 -44
- package/debug/konva/src/index.ts +0 -3
- package/debug/konva/src/shapes/Arc.ts +0 -176
- package/debug/konva/src/shapes/Arrow.ts +0 -231
- package/debug/konva/src/shapes/Circle.ts +0 -76
- package/debug/konva/src/shapes/Ellipse.ts +0 -121
- package/debug/konva/src/shapes/Image.ts +0 -319
- package/debug/konva/src/shapes/Label.ts +0 -386
- package/debug/konva/src/shapes/Line.ts +0 -364
- package/debug/konva/src/shapes/Path.ts +0 -1013
- package/debug/konva/src/shapes/Rect.ts +0 -79
- package/debug/konva/src/shapes/RegularPolygon.ts +0 -167
- package/debug/konva/src/shapes/Ring.ts +0 -94
- package/debug/konva/src/shapes/Sprite.ts +0 -370
- package/debug/konva/src/shapes/Star.ts +0 -125
- package/debug/konva/src/shapes/Text.ts +0 -1065
- package/debug/konva/src/shapes/TextPath.ts +0 -583
- package/debug/konva/src/shapes/Transformer.ts +0 -1889
- package/debug/konva/src/shapes/Wedge.ts +0 -129
- package/debug/konva/src/skia-backend.ts +0 -35
- package/debug/konva/src/types.ts +0 -84
- package/debug/konva/tsconfig.json +0 -31
- package/debug/konva/tsconfig.test.json +0 -7
package/src/shapes/Line.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
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
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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 === '
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|