jellies-draw 0.2.1 → 0.2.2
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/package.json +1 -1
- package/src/components/functions/canvas.js +15 -14
- package/src/components/functions/tools/arrow.js +11 -2
- package/src/components/functions/tools/ellipse.js +12 -3
- package/src/components/functions/tools/eraser.js +0 -1
- package/src/components/functions/tools/helper/angleSnap.js +29 -0
- package/src/components/functions/tools/helper/shapeConstraint.js +19 -0
- package/src/components/functions/tools/line.js +11 -2
- package/src/components/functions/tools/rectangle.js +12 -3
- package/src/components/functions/transformer.js +64 -22
package/package.json
CHANGED
|
@@ -202,34 +202,35 @@ export default {
|
|
|
202
202
|
|
|
203
203
|
const target = e.target;
|
|
204
204
|
|
|
205
|
-
// Reset cursor to default when over stage
|
|
206
205
|
if (target === this.stage) {
|
|
207
206
|
this.stage.container().style.cursor = 'default';
|
|
208
207
|
return;
|
|
209
208
|
}
|
|
210
209
|
|
|
211
|
-
// Handle cursor for nodes
|
|
212
210
|
if (target.hasName('node')) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
211
|
+
const mousePos = this.stage.getPointerPosition();
|
|
212
|
+
const canInteract = this._canInteractWithNode(target, mousePos);
|
|
213
|
+
|
|
214
|
+
this.stage.container().style.cursor = canInteract ? 'move' : 'default';
|
|
215
|
+
|
|
216
|
+
if (!canInteract) {
|
|
217
|
+
target.draggable(false);
|
|
218
|
+
} else if (!Properties.isUsingDrawingTool) {
|
|
219
|
+
target.draggable(true);
|
|
217
220
|
}
|
|
218
221
|
}
|
|
219
222
|
},
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
+
_canInteractWithNode(node, mousePos) {
|
|
224
|
+
const selectedNodes = Transformer._nodes();
|
|
225
|
+
if (selectedNodes.includes(node)) {
|
|
223
226
|
return true;
|
|
224
227
|
}
|
|
225
228
|
|
|
226
|
-
|
|
227
|
-
|
|
229
|
+
if (!['Rect', 'Ellipse'].includes(node.className) ||
|
|
230
|
+
(node.attrs.fill && node.attrs.fill !== 'transparent')) {
|
|
228
231
|
return true;
|
|
229
232
|
}
|
|
230
|
-
|
|
231
|
-
// For unfilled shapes, only show move cursor near border
|
|
232
|
-
const mousePos = this.stage.getPointerPosition();
|
|
233
|
+
|
|
233
234
|
return Transformer._isNearBorder(node, mousePos);
|
|
234
235
|
}
|
|
235
236
|
}
|
|
@@ -2,6 +2,8 @@ import Konva from 'konva'
|
|
|
2
2
|
import Properties from '../properties'
|
|
3
3
|
import Canvas from '../canvas'
|
|
4
4
|
import Histories from '../histories'
|
|
5
|
+
import AngleSnap from './helper/angleSnap'
|
|
6
|
+
|
|
5
7
|
export default {
|
|
6
8
|
temporalShape: null,
|
|
7
9
|
startX: 0,
|
|
@@ -24,9 +26,16 @@ export default {
|
|
|
24
26
|
this.temporalShape = newArrow
|
|
25
27
|
Canvas.layer.add(this.temporalShape)
|
|
26
28
|
},
|
|
27
|
-
change({ offsetX, offsetY }) {
|
|
29
|
+
change({ offsetX, offsetY, shiftKey }) {
|
|
28
30
|
if (Canvas.isDrawing) {
|
|
29
|
-
|
|
31
|
+
const endPoint = AngleSnap.getSnappedEndPoint(
|
|
32
|
+
this.startX,
|
|
33
|
+
this.startY,
|
|
34
|
+
offsetX,
|
|
35
|
+
offsetY,
|
|
36
|
+
shiftKey
|
|
37
|
+
);
|
|
38
|
+
this.temporalShape.points([this.startX, this.startY, endPoint.x, endPoint.y]);
|
|
30
39
|
}
|
|
31
40
|
},
|
|
32
41
|
finish() {
|
|
@@ -2,6 +2,8 @@ import Konva from 'konva'
|
|
|
2
2
|
import Properties from '../properties'
|
|
3
3
|
import Canvas from '../canvas'
|
|
4
4
|
import Histories from '../histories'
|
|
5
|
+
import ShapeConstraint from './helper/shapeConstraint'
|
|
6
|
+
|
|
5
7
|
export default {
|
|
6
8
|
temporalShape: null,
|
|
7
9
|
startX: 0,
|
|
@@ -26,12 +28,19 @@ export default {
|
|
|
26
28
|
this.temporalShape = newEllipse
|
|
27
29
|
Canvas.layer.add(this.temporalShape)
|
|
28
30
|
},
|
|
29
|
-
change({ offsetX, offsetY }) {
|
|
31
|
+
change({ offsetX, offsetY, shiftKey }) {
|
|
30
32
|
if (Canvas.isDrawing) {
|
|
33
|
+
const { width, height } = ShapeConstraint.getConstrainedSize(
|
|
34
|
+
this.startX,
|
|
35
|
+
this.startY,
|
|
36
|
+
offsetX,
|
|
37
|
+
offsetY,
|
|
38
|
+
shiftKey
|
|
39
|
+
);
|
|
31
40
|
this.temporalShape.x((offsetX + this.startX) / 2);
|
|
32
41
|
this.temporalShape.y((offsetY + this.startY) / 2);
|
|
33
|
-
this.temporalShape.radiusX(Math.abs(
|
|
34
|
-
this.temporalShape.radiusY(Math.abs(
|
|
42
|
+
this.temporalShape.radiusX(Math.abs(width) / 2);
|
|
43
|
+
this.temporalShape.radiusY(Math.abs(height) / 2);
|
|
35
44
|
}
|
|
36
45
|
},
|
|
37
46
|
finish() {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
getAngle(startX, startY, endX, endY) {
|
|
3
|
+
return Math.atan2(endY - startY, endX - startX);
|
|
4
|
+
},
|
|
5
|
+
|
|
6
|
+
snapAngle(angle) {
|
|
7
|
+
let degrees = (angle * 180) / Math.PI;
|
|
8
|
+
degrees = (degrees + 360) % 360;
|
|
9
|
+
const snappedDegrees = Math.round(degrees / 15) * 15;
|
|
10
|
+
return (snappedDegrees * Math.PI) / 180;
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
getSnappedEndPoint(startX, startY, endX, endY, isShiftPressed) {
|
|
14
|
+
if (!isShiftPressed) {
|
|
15
|
+
return { x: endX, y: endY };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const angle = this.getAngle(startX, startY, endX, endY);
|
|
19
|
+
const snappedAngle = this.snapAngle(angle);
|
|
20
|
+
const distance = Math.sqrt(
|
|
21
|
+
Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
x: startX + distance * Math.cos(snappedAngle),
|
|
26
|
+
y: startY + distance * Math.sin(snappedAngle)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
getConstrainedSize(startX, startY, currentX, currentY, isShiftPressed) {
|
|
3
|
+
if (!isShiftPressed) {
|
|
4
|
+
return {
|
|
5
|
+
width: currentX - startX,
|
|
6
|
+
height: currentY - startY
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const width = currentX - startX;
|
|
11
|
+
const height = currentY - startY;
|
|
12
|
+
const size = Math.max(Math.abs(width), Math.abs(height));
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
width: size * Math.sign(width),
|
|
16
|
+
height: size * Math.sign(height)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -2,6 +2,8 @@ import Konva from 'konva'
|
|
|
2
2
|
import Properties from '../properties'
|
|
3
3
|
import Canvas from '../canvas'
|
|
4
4
|
import Histories from '../histories'
|
|
5
|
+
import AngleSnap from './helper/angleSnap'
|
|
6
|
+
|
|
5
7
|
export default {
|
|
6
8
|
temporalShape: null,
|
|
7
9
|
startX: 0,
|
|
@@ -20,9 +22,16 @@ export default {
|
|
|
20
22
|
this.temporalShape = newLine
|
|
21
23
|
Canvas.layer.add(this.temporalShape)
|
|
22
24
|
},
|
|
23
|
-
change({ offsetX, offsetY }) {
|
|
25
|
+
change({ offsetX, offsetY, shiftKey }) {
|
|
24
26
|
if (Canvas.isDrawing) {
|
|
25
|
-
|
|
27
|
+
const endPoint = AngleSnap.getSnappedEndPoint(
|
|
28
|
+
this.startX,
|
|
29
|
+
this.startY,
|
|
30
|
+
offsetX,
|
|
31
|
+
offsetY,
|
|
32
|
+
shiftKey
|
|
33
|
+
);
|
|
34
|
+
this.temporalShape.points([this.startX, this.startY, endPoint.x, endPoint.y]);
|
|
26
35
|
}
|
|
27
36
|
},
|
|
28
37
|
finish() {
|
|
@@ -2,6 +2,8 @@ import Konva from 'konva'
|
|
|
2
2
|
import Properties from '../properties'
|
|
3
3
|
import Canvas from '../canvas'
|
|
4
4
|
import Histories from '../histories'
|
|
5
|
+
import ShapeConstraint from './helper/shapeConstraint'
|
|
6
|
+
|
|
5
7
|
export default {
|
|
6
8
|
temporalShape: null,
|
|
7
9
|
startX: 0,
|
|
@@ -26,10 +28,17 @@ export default {
|
|
|
26
28
|
this.temporalShape = newRectangle
|
|
27
29
|
Canvas.layer.add(this.temporalShape)
|
|
28
30
|
},
|
|
29
|
-
change({ offsetX, offsetY }) {
|
|
31
|
+
change({ offsetX, offsetY, shiftKey }) {
|
|
30
32
|
if (Canvas.isDrawing) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const { width, height } = ShapeConstraint.getConstrainedSize(
|
|
34
|
+
this.startX,
|
|
35
|
+
this.startY,
|
|
36
|
+
offsetX,
|
|
37
|
+
offsetY,
|
|
38
|
+
shiftKey
|
|
39
|
+
);
|
|
40
|
+
this.temporalShape.width(width);
|
|
41
|
+
this.temporalShape.height(height);
|
|
33
42
|
}
|
|
34
43
|
},
|
|
35
44
|
finish() {
|
|
@@ -11,6 +11,15 @@ export default {
|
|
|
11
11
|
},
|
|
12
12
|
initialize() {
|
|
13
13
|
this.transformer = new Konva.Transformer();
|
|
14
|
+
|
|
15
|
+
// 创建一个透明的拖动区域
|
|
16
|
+
this.dragArea = new Konva.Rect({
|
|
17
|
+
fill: 'transparent',
|
|
18
|
+
draggable: true,
|
|
19
|
+
listening: true
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
Canvas.layer.add(this.dragArea);
|
|
14
23
|
Canvas.layer.add(this.transformer);
|
|
15
24
|
},
|
|
16
25
|
copySelectedNodes() {
|
|
@@ -68,20 +77,22 @@ export default {
|
|
|
68
77
|
this._selectLinearNode(targetNodes[0]);
|
|
69
78
|
} else {
|
|
70
79
|
this._nodes(targetNodes);
|
|
71
|
-
if (targetNodes.length
|
|
72
|
-
this.
|
|
80
|
+
if (targetNodes.length > 0) {
|
|
81
|
+
this._updateDragArea(targetNodes);
|
|
82
|
+
this._updateTransformerAnchors(targetNodes);
|
|
73
83
|
} else {
|
|
74
|
-
this.
|
|
84
|
+
this.dragArea.visible(false);
|
|
75
85
|
}
|
|
76
|
-
Properties.isTextNodesSelected = this._hasSelectedOnlyTextNodes()
|
|
86
|
+
Properties.isTextNodesSelected = this._hasSelectedOnlyTextNodes();
|
|
77
87
|
}
|
|
78
88
|
Properties.setProperties(this._getCommonPropertiesOfSelectedNodes());
|
|
79
89
|
},
|
|
80
90
|
deselectAllNodes() {
|
|
91
|
+
this.dragArea.visible(false);
|
|
81
92
|
this.selectNodes([]);
|
|
82
93
|
if (this.linearTransformer.node) {
|
|
83
94
|
this.linearTransformer.node.destroy();
|
|
84
|
-
this._removeLinearTransformer()
|
|
95
|
+
this._removeLinearTransformer();
|
|
85
96
|
}
|
|
86
97
|
},
|
|
87
98
|
selectAllNodes() {
|
|
@@ -217,7 +228,7 @@ export default {
|
|
|
217
228
|
return false;
|
|
218
229
|
},
|
|
219
230
|
_shouldSelectNode(node) {
|
|
220
|
-
if ((node.className === 'Rect' || node.className === '
|
|
231
|
+
if ((node.className === 'Rect' || node.className === 'Ellipse') &&
|
|
221
232
|
(!node.attrs.fill || node.attrs.fill === 'transparent')) {
|
|
222
233
|
const stage = Canvas.stage;
|
|
223
234
|
const mousePos = stage.getPointerPosition();
|
|
@@ -226,33 +237,64 @@ export default {
|
|
|
226
237
|
return true;
|
|
227
238
|
},
|
|
228
239
|
_isNearBorder(node, point) {
|
|
229
|
-
const
|
|
230
|
-
const
|
|
240
|
+
const transform = node.getAbsoluteTransform().copy();
|
|
241
|
+
const localPoint = transform.invert().point(point);
|
|
242
|
+
|
|
243
|
+
const threshold = node.attrs.strokeWidth * 2;
|
|
231
244
|
|
|
232
245
|
if (node.className === 'Rect') {
|
|
233
246
|
const width = node.width();
|
|
234
247
|
const height = node.height();
|
|
235
248
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
localPoint.y >= -threshold &&
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
249
|
+
const nearHorizontalEdge = (localPoint.y >= -threshold && localPoint.y <= threshold) ||
|
|
250
|
+
(localPoint.y >= height - threshold && localPoint.y <= height + threshold);
|
|
251
|
+
const nearVerticalEdge = (localPoint.x >= -threshold && localPoint.x <= threshold) ||
|
|
252
|
+
(localPoint.x >= width - threshold && localPoint.x <= width + threshold);
|
|
253
|
+
|
|
254
|
+
return (nearHorizontalEdge && localPoint.x >= -threshold && localPoint.x <= width + threshold) ||
|
|
255
|
+
(nearVerticalEdge && localPoint.y >= -threshold && localPoint.y <= height + threshold);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (node.className === 'Ellipse') {
|
|
259
|
+
const radiusX = node.radiusX();
|
|
260
|
+
const radiusY = node.radiusY();
|
|
247
261
|
|
|
248
262
|
const normalizedX = localPoint.x / radiusX;
|
|
249
263
|
const normalizedY = localPoint.y / radiusY;
|
|
264
|
+
const distanceFromCenter = Math.sqrt(normalizedX * normalizedX + normalizedY * normalizedY);
|
|
250
265
|
|
|
251
|
-
const distanceFromCenter = Math.sqrt(
|
|
252
|
-
normalizedX * normalizedX + normalizedY * normalizedY
|
|
253
|
-
);
|
|
254
266
|
return Math.abs(distanceFromCenter - 1) < threshold / Math.min(radiusX, radiusY);
|
|
255
267
|
}
|
|
268
|
+
|
|
256
269
|
return true;
|
|
270
|
+
},
|
|
271
|
+
_updateDragArea(targetNodes) {
|
|
272
|
+
const box = this.transformer.getClientRect();
|
|
273
|
+
this.dragArea.setAttrs({
|
|
274
|
+
x: box.x,
|
|
275
|
+
y: box.y,
|
|
276
|
+
width: box.width,
|
|
277
|
+
height: box.height,
|
|
278
|
+
visible: true
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
this.dragArea.off('dragmove');
|
|
282
|
+
this.dragArea.on('dragmove', () => {
|
|
283
|
+
const dx = this.dragArea.x() - box.x;
|
|
284
|
+
const dy = this.dragArea.y() - box.y;
|
|
285
|
+
targetNodes.forEach(node => {
|
|
286
|
+
node.x(node.x() + dx);
|
|
287
|
+
node.y(node.y() + dy);
|
|
288
|
+
});
|
|
289
|
+
box.x = this.dragArea.x();
|
|
290
|
+
box.y = this.dragArea.y();
|
|
291
|
+
});
|
|
292
|
+
},
|
|
293
|
+
_updateTransformerAnchors(targetNodes) {
|
|
294
|
+
if (targetNodes.length === 1) {
|
|
295
|
+
this.transformer.enabledAnchors(['top-left', 'top-center', 'top-right', 'middle-right', 'middle-left', 'bottom-left', 'bottom-center', 'bottom-right']);
|
|
296
|
+
} else {
|
|
297
|
+
this.transformer.enabledAnchors(['top-left', 'top-right', 'bottom-left', 'bottom-right']);
|
|
298
|
+
}
|
|
257
299
|
}
|
|
258
300
|
}
|