jellies-draw 0.2.4 → 0.3.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jellies-draw",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "A drawer for jellies",
5
5
  "private": false,
6
6
  "main": "./src/index.js",
@@ -79,7 +79,8 @@ export default {
79
79
  width: 100%;
80
80
  height: 100%;
81
81
  }
82
- .container-penetrable {
82
+ .container-penetrable,
83
+ .container-penetrable * {
83
84
  pointer-events: none;
84
85
  }
85
86
  .cursor-crosshair {
@@ -26,7 +26,7 @@ export default {
26
26
  default: 'selector'
27
27
  },
28
28
  shortCut: {
29
- type: Number,
29
+ type: [Number, String],
30
30
  default: null
31
31
  },
32
32
  isActive: {
@@ -141,4 +141,14 @@ button {
141
141
  .tool-clear:before {
142
142
  content: "\e7a9";
143
143
  }
144
+ .tool-laser:before {
145
+ content: "";
146
+ display: inline-block;
147
+ width: 8px;
148
+ height: 8px;
149
+ border-radius: 50%;
150
+ background: #f33b29;
151
+ box-shadow: 0 0 4px #f33b29, 0 0 8px rgba(243, 59, 41, 0.6);
152
+ vertical-align: middle;
153
+ }
144
154
  </style>
@@ -42,6 +42,7 @@ export default {
42
42
  data() {
43
43
  return {
44
44
  tools: [
45
+ 'laser',
45
46
  'selector',
46
47
  'rectangle',
47
48
  'ellipse',
@@ -63,7 +64,8 @@ export default {
63
64
  toolsCanBeUsedContinuously: [
64
65
  'pen',
65
66
  'text',
66
- 'eraser'
67
+ 'eraser',
68
+ 'laser'
67
69
  ]
68
70
  }
69
71
  },
@@ -92,13 +94,8 @@ export default {
92
94
  Properties.latestTool = newTool;
93
95
  }
94
96
  },
95
- isCanvasPenetrable: {
96
- get() {
97
- return Properties.isCanvasPenetrable;
98
- },
99
- set(isCanvasPenetrable) {
100
- Properties.isCanvasPenetrable = isCanvasPenetrable;
101
- }
97
+ isCanvasPenetrable() {
98
+ return Properties.isCanvasPenetrable;
102
99
  }
103
100
  },
104
101
  watch: {
@@ -108,7 +105,10 @@ export default {
108
105
  },
109
106
  methods: {
110
107
  toolShortCut(index) {
111
- return Properties.isUsingText || Properties.isCanvasPenetrable ? null : (index + 1) % 10
108
+ if (Properties.isUsingText) return null
109
+ if (this.tools[index] === 'laser') return null
110
+ if (index === 10) return 0
111
+ return index
112
112
  },
113
113
  applyAction(action) {
114
114
  if (action === 'image') {
@@ -117,10 +117,11 @@ export default {
117
117
  if (action === 'clear') {
118
118
  Tools.clear.clear()
119
119
  }
120
- if (this.currentTool !== action &&
120
+ if (this.currentTool !== action &&
121
121
  (
122
122
  (this.toolsCanBeLocked.includes(this.currentTool) && this.isCurrentToolLocked) ||
123
- this.toolsCanBeUsedContinuously.includes(this.currentTool)
123
+ this.toolsCanBeUsedContinuously.includes(this.currentTool) ||
124
+ this.currentTool === 'selector'
124
125
  )
125
126
  ) {
126
127
  this.latestTool = {
@@ -130,7 +131,6 @@ export default {
130
131
  }
131
132
  this.isCurrentToolLocked = this.toolsCanBeLocked.includes(action) && this.currentTool === action
132
133
  this.currentTool = action
133
- this.isCanvasPenetrable = this.currentTool === 'selector' && this.isCurrentToolLocked
134
134
  this.releaseInstantTool()
135
135
  },
136
136
  releaseInstantTool() {
@@ -147,12 +147,26 @@ export default {
147
147
  event.target.value = ''
148
148
  },
149
149
  handleShortCutKey(event) {
150
- if (this.hasShortCuts && this.currentTool !== 'text' && !this.isCanvasPenetrable) {
151
- const key = event.key
152
- const index = parseInt(key, 10)
153
- if (index >= 0 && index < this.tools.length) {
154
- this.applyAction(this.tools[(index + 9) % 10])
150
+ if (!this.hasShortCuts) return
151
+ if (this.currentTool === 'text') return
152
+ const tag = document.activeElement && document.activeElement.tagName
153
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || (document.activeElement && document.activeElement.isContentEditable)) return
154
+ if (event.key === '`') {
155
+ if (this.currentTool === 'laser') {
156
+ const fallback = this.latestTool.tool === 'laser' ? 'pen' : this.latestTool.tool
157
+ this.applyAction(fallback)
158
+ } else {
159
+ this.applyAction('laser')
155
160
  }
161
+ return
162
+ }
163
+ if (event.altKey || event.ctrlKey || event.metaKey) return
164
+ const key = event.key
165
+ const index = parseInt(key, 10)
166
+ if (isNaN(index)) return
167
+ const targetIdx = index === 0 ? 10 : index
168
+ if (targetIdx < this.tools.length) {
169
+ this.applyAction(this.tools[targetIdx])
156
170
  }
157
171
  }
158
172
  }
@@ -4,6 +4,9 @@ import Clipboard from './clipboard';
4
4
  import Properties from './properties';
5
5
  import Tools from './tools';
6
6
  import Histories from './histories';
7
+ import Laser from './laser';
8
+ import AttachedText from './tools/helper/attachedText';
9
+ import Connection from './tools/helper/connection';
7
10
  export default {
8
11
  layer: null,
9
12
  stage: null,
@@ -29,6 +32,16 @@ export default {
29
32
  });
30
33
  this.layer = new Konva.Layer();
31
34
  this.stage.add(this.layer);
35
+ Laser.initialize(this.stage, this.container);
36
+ this.unwatchLaser = Properties.$watch(
37
+ () => Properties.tool === 'laser',
38
+ (isLaserMode) => {
39
+ if (isLaserMode) Laser.activate();
40
+ else Laser.deactivate();
41
+ },
42
+ { immediate: true }
43
+ );
44
+ AttachedText.attachLayerListeners(this.layer);
32
45
  document.addEventListener('keydown', this.keydownHandler);
33
46
  document.addEventListener('keyup', this.keyupHandler);
34
47
  Transformer.initialize();
@@ -41,6 +54,10 @@ export default {
41
54
  window.addEventListener('pointerup', this.handleGlobalPointerUp, { once: true });
42
55
  });
43
56
  this.stage.on('mousemove', this._handleMouseMove.bind(this));
57
+ this.containerLeaveHandler = () => {
58
+ if (!this.isDrawing) Connection.hideSnapVisuals();
59
+ };
60
+ this.stage.container().addEventListener('mouseleave', this.containerLeaveHandler);
44
61
  },
45
62
  generateEventHandlers() {
46
63
  this.resizeHandler = this._handleResize.bind(this);
@@ -72,6 +89,15 @@ export default {
72
89
  this.stage.off('click tap', this.clickHandler);
73
90
  document.removeEventListener('keydown', this.keydownHandler);
74
91
  document.removeEventListener('keyup', this.keyupHandler);
92
+ if (this.containerLeaveHandler) {
93
+ this.stage.container().removeEventListener('mouseleave', this.containerLeaveHandler);
94
+ }
95
+ if (this.unwatchLaser) {
96
+ this.unwatchLaser();
97
+ this.unwatchLaser = null;
98
+ }
99
+ Laser.destroy();
100
+ Connection.hideSnapVisuals();
75
101
  this.stage.clear()
76
102
  this.stage = null;
77
103
  window.removeEventListener('resize', this.resizeHandler);
@@ -100,12 +126,12 @@ export default {
100
126
  this._handlePropertiesShortCuts(event);
101
127
  }
102
128
  } else if (event.altKey) {
103
- Properties.isCanvasPenetrable = true;
129
+ Properties.isAltHeld = true;
104
130
  }
105
131
  },
106
132
  _handleKeyup(event) {
107
133
  if (event.key === 'Alt') {
108
- Properties.isCanvasPenetrable = false;
134
+ Properties.isAltHeld = false;
109
135
  if (Properties.tool === 'selector') {
110
136
  Properties.isToolLocked = false;
111
137
  Properties.latestTool = {
@@ -195,11 +221,14 @@ export default {
195
221
  },
196
222
  _handleMouseMove(e) {
197
223
  if (Properties.isUsingDrawingTool || Properties.isUsingText) {
224
+ if (Properties.tool === 'line' || Properties.tool === 'arrow') {
225
+ this._handleSnapHoverPreview();
226
+ }
198
227
  return;
199
228
  }
200
229
 
201
230
  const target = e.target;
202
-
231
+
203
232
  if (target === this.stage) {
204
233
  this.stage.container().style.cursor = 'default';
205
234
  return;
@@ -208,9 +237,9 @@ export default {
208
237
  if (target.hasName('node')) {
209
238
  const mousePos = this.stage.getPointerPosition();
210
239
  const canInteract = this._canInteractWithNode(target, mousePos);
211
-
240
+
212
241
  this.stage.container().style.cursor = canInteract ? 'move' : 'default';
213
-
242
+
214
243
  if (!canInteract) {
215
244
  target.draggable(false);
216
245
  } else if (!Properties.isUsingDrawingTool) {
@@ -218,6 +247,17 @@ export default {
218
247
  }
219
248
  }
220
249
  },
250
+ _handleSnapHoverPreview() {
251
+ if (this.isDrawing) return;
252
+ const pos = this.stage.getPointerPosition();
253
+ if (!pos) return;
254
+ const target = Connection.findSnapTarget(pos);
255
+ if (target) {
256
+ Connection.showSnapVisuals(target);
257
+ } else {
258
+ Connection.hideSnapVisuals();
259
+ }
260
+ },
221
261
  _canInteractWithNode(node, mousePos) {
222
262
  const selectedNodes = Transformer._nodes();
223
263
  if (selectedNodes.includes(node)) {
@@ -2,6 +2,7 @@ import Canvas from './canvas';
2
2
  import Transformer from './transformer';
3
3
  import Tools from './tools';
4
4
  import Histories from './histories';
5
+ import Connection from './tools/helper/connection';
5
6
  export default {
6
7
  copiedNodes: [],
7
8
  copy() {
@@ -13,6 +14,21 @@ export default {
13
14
  },
14
15
  paste() {
15
16
  if (this.copiedNodes && this.copiedNodes.length > 0) {
17
+ const idMap = {};
18
+ this.copiedNodes.forEach((node) => {
19
+ if (node.attrs.nodeId) {
20
+ const oldId = node.attrs.nodeId;
21
+ node.setAttr('nodeId', undefined);
22
+ Connection.ensureNodeId(node);
23
+ idMap[oldId] = node.attrs.nodeId;
24
+ }
25
+ });
26
+ this.copiedNodes.forEach((node) => {
27
+ if (node.className === 'Line' || node.className === 'Arrow') {
28
+ this._remapLineAttachment(node, 'start', idMap);
29
+ this._remapLineAttachment(node, 'end', idMap);
30
+ }
31
+ });
16
32
  this.copiedNodes.forEach((node) => {
17
33
  node.x(node.x() + 10);
18
34
  node.y(node.y() + 10);
@@ -24,11 +40,31 @@ export default {
24
40
  Histories.record();
25
41
  }
26
42
  },
43
+ _remapLineAttachment(line, end, idMap) {
44
+ const oldId = line.attrs[`${end}AttachedTo`];
45
+ if (!oldId) return;
46
+ if (idMap[oldId]) {
47
+ line.setAttr(`${end}AttachedTo`, idMap[oldId]);
48
+ } else {
49
+ line.setAttr(`${end}AttachedTo`, undefined);
50
+ line.setAttr(`${end}AttachedLocalX`, undefined);
51
+ line.setAttr(`${end}AttachedLocalY`, undefined);
52
+ }
53
+ },
27
54
  _addEventsToCopiedNode(node) {
28
- if (node.getClassName() === 'Text') {
55
+ const className = node.getClassName();
56
+ if (className === 'Text') {
29
57
  Tools.text.bindEvents(node);
30
- } else if (node.getClassName() === 'Arrow') {
58
+ } else if (className === 'Arrow') {
31
59
  Tools.arrow.bindEvents(node);
60
+ } else if (className === 'Rect') {
61
+ Tools.rectangle.bindEvents(node);
62
+ } else if (className === 'Ellipse') {
63
+ Tools.ellipse.bindEvents(node);
64
+ } else if (className === 'Line') {
65
+ Tools.line.bindEvents(node);
66
+ } else if (className === 'Shape' && node.attrs.nodeType === 'pen') {
67
+ Tools.pen.bindEvents(node);
32
68
  }
33
69
  }
34
70
  }
@@ -0,0 +1,188 @@
1
+ import Konva from 'konva';
2
+
3
+ const FADE_DURATION = 700;
4
+ const LASER_WIDTH = 4;
5
+ const DRAG_THRESHOLD = 2;
6
+ const GLOW_COLOR = '243, 59, 41';
7
+ const CORE_COLOR = '255, 110, 90';
8
+
9
+ export default {
10
+ stage: null,
11
+ container: null,
12
+ layer: null,
13
+ shape: null,
14
+ segments: [],
15
+ isPressed: false,
16
+ hasDragged: false,
17
+ startX: 0,
18
+ startY: 0,
19
+ lastX: 0,
20
+ lastY: 0,
21
+ rafId: null,
22
+ active: false,
23
+ _onMouseDown: null,
24
+ _onMouseMove: null,
25
+ _onMouseUp: null,
26
+ _onTick: null,
27
+
28
+ initialize(stage, container) {
29
+ this.stage = stage;
30
+ this.container = container;
31
+ this._onMouseDown = this._handleMouseDown.bind(this);
32
+ this._onMouseMove = this._handleMouseMove.bind(this);
33
+ this._onMouseUp = this._handleMouseUp.bind(this);
34
+ this._onTick = this._tick.bind(this);
35
+ this.layer = new Konva.Layer({ listening: false });
36
+ this.shape = new Konva.Shape({
37
+ listening: false,
38
+ sceneFunc: (ctx) => this._renderSegments(ctx)
39
+ });
40
+ this.layer.add(this.shape);
41
+ this.layer.hide();
42
+ stage.add(this.layer);
43
+ },
44
+
45
+ destroy() {
46
+ this.deactivate();
47
+ if (this.layer) {
48
+ this.layer.destroy();
49
+ this.layer = null;
50
+ this.shape = null;
51
+ }
52
+ this.stage = null;
53
+ this.container = null;
54
+ },
55
+
56
+ activate() {
57
+ if (this.active || !this.layer) return;
58
+ this.active = true;
59
+ this.layer.show();
60
+ this.layer.moveToTop();
61
+ window.addEventListener('pointerdown', this._onMouseDown, true);
62
+ window.addEventListener('pointermove', this._onMouseMove, true);
63
+ window.addEventListener('pointerup', this._onMouseUp, true);
64
+ window.addEventListener('pointercancel', this._onMouseUp, true);
65
+ this.rafId = requestAnimationFrame(this._onTick);
66
+ },
67
+
68
+ deactivate() {
69
+ if (!this.active) return;
70
+ this.active = false;
71
+ window.removeEventListener('pointerdown', this._onMouseDown, true);
72
+ window.removeEventListener('pointermove', this._onMouseMove, true);
73
+ window.removeEventListener('pointerup', this._onMouseUp, true);
74
+ window.removeEventListener('pointercancel', this._onMouseUp, true);
75
+ if (this.rafId !== null) {
76
+ cancelAnimationFrame(this.rafId);
77
+ this.rafId = null;
78
+ }
79
+ this.segments = [];
80
+ this.isPressed = false;
81
+ this.hasDragged = false;
82
+ if (this.layer) {
83
+ this.layer.hide();
84
+ this.layer.batchDraw();
85
+ }
86
+ },
87
+
88
+ _isWithinContainer(clientX, clientY) {
89
+ if (!this.container) return false;
90
+ const rect = this.container.getBoundingClientRect();
91
+ if (rect.width === 0 || rect.height === 0) return false;
92
+ return clientX >= rect.left && clientX <= rect.right &&
93
+ clientY >= rect.top && clientY <= rect.bottom;
94
+ },
95
+
96
+ _toStageCoords(clientX, clientY) {
97
+ const rect = this.container.getBoundingClientRect();
98
+ return { x: clientX - rect.left, y: clientY - rect.top };
99
+ },
100
+
101
+ _handleMouseDown(e) {
102
+ if (e.button !== 0) return;
103
+ if (!this._isWithinContainer(e.clientX, e.clientY)) return;
104
+ const pt = this._toStageCoords(e.clientX, e.clientY);
105
+ this.isPressed = true;
106
+ this.hasDragged = false;
107
+ this.startX = pt.x;
108
+ this.startY = pt.y;
109
+ this.lastX = pt.x;
110
+ this.lastY = pt.y;
111
+ },
112
+
113
+ _handleMouseMove(e) {
114
+ if ((e.buttons & 1) === 0) {
115
+ this.isPressed = false;
116
+ this.hasDragged = false;
117
+ return;
118
+ }
119
+ if (!this.isPressed) return;
120
+ const pt = this._toStageCoords(e.clientX, e.clientY);
121
+ if (!this.hasDragged) {
122
+ const dx = pt.x - this.startX;
123
+ const dy = pt.y - this.startY;
124
+ if (dx * dx + dy * dy < DRAG_THRESHOLD * DRAG_THRESHOLD) return;
125
+ this.hasDragged = true;
126
+ this.segments.push({
127
+ x1: this.startX, y1: this.startY,
128
+ x2: pt.x, y2: pt.y,
129
+ time: performance.now()
130
+ });
131
+ } else {
132
+ this.segments.push({
133
+ x1: this.lastX, y1: this.lastY,
134
+ x2: pt.x, y2: pt.y,
135
+ time: performance.now()
136
+ });
137
+ }
138
+ this.lastX = pt.x;
139
+ this.lastY = pt.y;
140
+ },
141
+
142
+ _handleMouseUp() {
143
+ this.isPressed = false;
144
+ this.hasDragged = false;
145
+ },
146
+
147
+ _tick() {
148
+ if (!this.active) return;
149
+ const now = performance.now();
150
+ const initialLength = this.segments.length;
151
+ while (this.segments.length && now - this.segments[0].time > FADE_DURATION) {
152
+ this.segments.shift();
153
+ }
154
+ if (this.segments.length > 0 || initialLength > 0) {
155
+ this.layer.batchDraw();
156
+ }
157
+ this.rafId = requestAnimationFrame(this._onTick);
158
+ },
159
+
160
+ _renderSegments(ctx) {
161
+ const segs = this.segments;
162
+ if (segs.length === 0) return;
163
+ const now = performance.now();
164
+ const newestAge = (now - segs[segs.length - 1].time) / FADE_DURATION;
165
+ const opacity = Math.max(0, 1 - newestAge);
166
+ if (opacity <= 0) return;
167
+
168
+ ctx.save();
169
+ ctx.lineCap = 'round';
170
+ ctx.lineJoin = 'round';
171
+
172
+ ctx.beginPath();
173
+ ctx.moveTo(segs[0].x1, segs[0].y1);
174
+ for (let i = 0; i < segs.length; i++) {
175
+ ctx.lineTo(segs[i].x2, segs[i].y2);
176
+ }
177
+
178
+ ctx.strokeStyle = `rgba(${GLOW_COLOR}, ${opacity * 0.3})`;
179
+ ctx.lineWidth = LASER_WIDTH * 2.5;
180
+ ctx.stroke();
181
+
182
+ ctx.strokeStyle = `rgba(${CORE_COLOR}, ${opacity})`;
183
+ ctx.lineWidth = LASER_WIDTH;
184
+ ctx.stroke();
185
+
186
+ ctx.restore();
187
+ }
188
+ };
@@ -1,6 +1,7 @@
1
1
  import Vue from 'vue'
2
2
  import Canvas from './canvas'
3
3
  import Transformer from './transformer'
4
+ import Connection from './tools/helper/connection'
4
5
 
5
6
  export default new Vue({
6
7
  data() {
@@ -17,7 +18,7 @@ export default new Vue({
17
18
  isTextNodesSelected: false,
18
19
  isMassivelyAssigningProperties: false,
19
20
  isToolLocked: false,
20
- isCanvasPenetrable: false,
21
+ isAltHeld: false,
21
22
  fontSizeOptions: {
22
23
  15: 'S',
23
24
  20: 'M',
@@ -105,14 +106,19 @@ export default new Vue({
105
106
  return this.tool === 'text' || this.isTextNodesSelected;
106
107
  },
107
108
  isUsingDrawingTool() {
108
- return (this.tool !== 'selector');
109
+ return this.tool !== 'selector' && this.tool !== 'laser';
110
+ },
111
+ isCanvasPenetrable() {
112
+ return this.tool === 'laser' ||
113
+ this.isAltHeld ||
114
+ (this.tool === 'selector' && this.isToolLocked);
109
115
  }
110
116
  },
111
117
  watch: {
112
- toolSelected() {
118
+ toolSelected(newTool) {
113
119
  this.refreshNodesStatus()
114
- if (this.tool === 'selector' && this.isToolLocked) {
115
- this.isCanvasPenetrable = true;
120
+ if (newTool !== 'line' && newTool !== 'arrow') {
121
+ Connection.hideSnapVisuals()
116
122
  }
117
123
  }
118
124
  },
@@ -3,21 +3,30 @@ import Properties from '../properties'
3
3
  import Canvas from '../canvas'
4
4
  import Histories from '../histories'
5
5
  import AngleSnap from './helper/angleSnap'
6
+ import AttachedText from './helper/attachedText'
7
+ import Connection from './helper/connection'
6
8
 
7
9
  export default {
8
10
  temporalShape: null,
9
11
  startX: 0,
10
12
  startY: 0,
13
+ startSnap: null,
14
+ endSnap: null,
11
15
  show({ offsetX, offsetY }) {
12
16
  Canvas.isDrawing = true;
13
- this.startX = offsetX
14
- this.startY = offsetY
17
+ const startSnap = Connection.findSnapTarget({ x: offsetX, y: offsetY })
18
+ const startX = startSnap ? startSnap.worldX : offsetX
19
+ const startY = startSnap ? startSnap.worldY : offsetY
20
+ this.startX = startX
21
+ this.startY = startY
22
+ this.startSnap = startSnap
23
+ this.endSnap = null
15
24
  const strokeWidth = Properties.strokeWidth
16
25
  const pointerWidth = Math.pow(Properties.strokeWidth, 2) / 2
17
26
  const pointerLength = Math.pow(Properties.strokeWidth, 2) / 2
18
27
  const stroke = Properties.strokeColor
19
- const newArrow = this.generate({
20
- points: [offsetX, offsetY, offsetX, offsetY],
28
+ const newArrow = this.generate({
29
+ points: [startX, startY, startX, startY],
21
30
  stroke,
22
31
  strokeWidth,
23
32
  pointerWidth,
@@ -25,25 +34,47 @@ export default {
25
34
  })
26
35
  this.temporalShape = newArrow
27
36
  Canvas.layer.add(this.temporalShape)
37
+ Connection.hideSnapVisuals()
28
38
  },
29
39
  change({ offsetX, offsetY, shiftKey }) {
30
- if (Canvas.isDrawing) {
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]);
40
+ if (!Canvas.isDrawing) return
41
+ const endPoint = AngleSnap.getSnappedEndPoint(
42
+ this.startX,
43
+ this.startY,
44
+ offsetX,
45
+ offsetY,
46
+ shiftKey
47
+ );
48
+ let endX = endPoint.x
49
+ let endY = endPoint.y
50
+ const endSnap = Connection.findSnapTarget({ x: endX, y: endY }, this.temporalShape, 'end')
51
+ if (endSnap) {
52
+ endX = endSnap.worldX
53
+ endY = endSnap.worldY
54
+ Connection.showSnapVisuals(endSnap)
55
+ } else {
56
+ Connection.hideSnapVisuals()
39
57
  }
58
+ this.endSnap = endSnap
59
+ this.temporalShape.points([this.startX, this.startY, endX, endY]);
40
60
  },
41
61
  finish() {
62
+ Connection.hideSnapVisuals()
63
+ if (this.temporalShape) {
64
+ if (this.startSnap) {
65
+ Connection.attachLineEndpoint(this.temporalShape, 'start', this.startSnap)
66
+ }
67
+ if (this.endSnap) {
68
+ Connection.attachLineEndpoint(this.temporalShape, 'end', this.endSnap)
69
+ }
70
+ }
42
71
  if (!Properties.isToolLocked) {
43
72
  Properties.tool = Properties.latestTool.tool
44
73
  Properties.isToolLocked = Properties.latestTool.isToolLocked
45
74
  }
46
75
  this.temporalShape = null
76
+ this.startSnap = null
77
+ this.endSnap = null
47
78
  },
48
79
  bindEvents(newArrow) {
49
80
  newArrow.off('transform')
@@ -58,8 +89,10 @@ export default {
58
89
  })
59
90
  newArrow.on('transformend', Histories.record)
60
91
  newArrow.on('dragend', Histories.record)
92
+ AttachedText.bindShapeEvents(newArrow)
93
+ Connection.bindLineListeners(newArrow)
61
94
  },
62
- generate({ x, y, scaleX, scaleY, skewX, rotation, points, stroke, strokeWidth, pointerWidth, pointerLength }) {
95
+ generate({ x, y, scaleX, scaleY, skewX, rotation, points, stroke, strokeWidth, pointerWidth, pointerLength, attachedText, attachedFontSize, attachedFill, startAttachedTo, startAttachedLocalX, startAttachedLocalY, endAttachedTo, endAttachedLocalX, endAttachedLocalY }) {
63
96
  const newArrow = new Konva.Arrow({
64
97
  x,
65
98
  y,
@@ -72,6 +105,15 @@ export default {
72
105
  strokeWidth,
73
106
  pointerWidth,
74
107
  pointerLength,
108
+ attachedText,
109
+ attachedFontSize,
110
+ attachedFill,
111
+ startAttachedTo,
112
+ startAttachedLocalX,
113
+ startAttachedLocalY,
114
+ endAttachedTo,
115
+ endAttachedLocalX,
116
+ endAttachedLocalY,
75
117
  name: 'node',
76
118
  nodeType: 'arrow',
77
119
  lineCap: 'round',