@logicflow/core 2.0.16 → 2.1.1

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 (42) hide show
  1. package/.turbo/turbo-build$colon$dev.log +2 -2
  2. package/.turbo/turbo-build.log +5 -5
  3. package/CHANGELOG.md +22 -0
  4. package/dist/index.min.js +1 -1
  5. package/dist/index.min.js.map +1 -1
  6. package/es/LogicFlow.js +4 -1
  7. package/es/constant/index.d.ts +3 -1
  8. package/es/constant/index.js +2 -0
  9. package/es/keyboard/shortcut.js +7 -0
  10. package/es/model/GraphModel.js +2 -0
  11. package/es/model/SnaplineModel.d.ts +2 -1
  12. package/es/model/SnaplineModel.js +26 -11
  13. package/es/options.d.ts +1 -0
  14. package/es/util/resize.d.ts +12 -10
  15. package/es/util/resize.js +160 -81
  16. package/es/view/Control.d.ts +5 -1
  17. package/es/view/Control.js +23 -2
  18. package/es/view/behavior/dnd.js +3 -0
  19. package/lib/LogicFlow.js +4 -1
  20. package/lib/constant/index.d.ts +3 -1
  21. package/lib/constant/index.js +2 -0
  22. package/lib/keyboard/shortcut.js +7 -0
  23. package/lib/model/GraphModel.js +2 -0
  24. package/lib/model/SnaplineModel.d.ts +2 -1
  25. package/lib/model/SnaplineModel.js +26 -11
  26. package/lib/options.d.ts +1 -0
  27. package/lib/util/resize.d.ts +12 -10
  28. package/lib/util/resize.js +162 -83
  29. package/lib/view/Control.d.ts +5 -1
  30. package/lib/view/Control.js +22 -1
  31. package/lib/view/behavior/dnd.js +3 -0
  32. package/package.json +1 -1
  33. package/src/LogicFlow.tsx +7 -2
  34. package/src/constant/index.ts +2 -0
  35. package/src/keyboard/shortcut.ts +6 -0
  36. package/src/model/GraphModel.ts +2 -0
  37. package/src/model/SnaplineModel.ts +29 -11
  38. package/src/options.ts +1 -0
  39. package/src/util/resize.ts +200 -112
  40. package/src/view/Control.tsx +29 -5
  41. package/src/view/behavior/dnd.ts +3 -0
  42. package/stats.html +1 -1
@@ -19,8 +19,10 @@ export class SnaplineModel {
19
19
  @observable isShowVertical: boolean
20
20
  // 对齐线的中心位置,目前仅展示中心对齐的情况,后面可以考虑多种对齐策略
21
21
  @observable position: Position
22
+ // 对齐线显示的阈值距离
23
+ epsilon: number
22
24
 
23
- constructor(graphModel: GraphModel) {
25
+ constructor(graphModel: GraphModel, epsilon: number = 1) {
24
26
  this.isShowHorizontal = false
25
27
  this.isShowVertical = false
26
28
  this.position = {
@@ -28,6 +30,7 @@ export class SnaplineModel {
28
30
  y: 0,
29
31
  }
30
32
  this.graphModel = graphModel
33
+ this.epsilon = epsilon
31
34
  }
32
35
 
33
36
  getStyle() {
@@ -48,10 +51,10 @@ export class SnaplineModel {
48
51
  const item = nodes[i]
49
52
  // 排除当前节点
50
53
  if (item.id !== draggingNode.id) {
51
- if (x === item.x) {
54
+ if (equal(x, item.x, this.epsilon)) {
52
55
  isShowVertical = true
53
56
  }
54
- if (y === item.y) {
57
+ if (equal(y, item.y, this.epsilon)) {
55
58
  isShowHorizontal = true
56
59
  }
57
60
  // 如果水平垂直都显示,则停止循环。减少不必要的遍历
@@ -95,10 +98,11 @@ export class SnaplineModel {
95
98
  // 排除当前节点
96
99
  if (item.id !== draggingNode.id) {
97
100
  const itemData = getNodeBBox(item)
101
+
98
102
  // 如果节点的最大最小Y轴坐标与节点的最大最小Y轴坐标相等,展示水平线
99
103
  if (
100
- itemData.minY === draggingData?.minY ||
101
- itemData.maxY === draggingData?.minY
104
+ equal(itemData.minY, draggingData?.minY, this.epsilon) ||
105
+ equal(itemData.maxY, draggingData?.minY, this.epsilon)
102
106
  ) {
103
107
  // 找到则停止循环。减少不必要的遍历
104
108
  isShowHorizontal = true
@@ -106,8 +110,8 @@ export class SnaplineModel {
106
110
  break
107
111
  }
108
112
  if (
109
- itemData.minY === draggingData?.maxY ||
110
- itemData.maxY === draggingData?.maxY
113
+ equal(itemData.minY, draggingData?.maxY, this.epsilon) ||
114
+ equal(itemData.maxY, draggingData?.maxY, this.epsilon)
111
115
  ) {
112
116
  isShowHorizontal = true
113
117
  horizontalY = draggingData.maxY
@@ -145,15 +149,21 @@ export class SnaplineModel {
145
149
  }
146
150
  }
147
151
  }
152
+
148
153
  for (let i = 0; i < nodes.length; i++) {
149
154
  const item = nodes[i]
150
155
  // 排除当前节点
151
156
  if (item.id !== draggingNode.id) {
152
157
  const itemData = getNodeBBox(item)
153
158
  // 如果节点的最大最小X轴坐标与节点的最大最小X轴坐标相等,展示垂直线
159
+ if (equal(itemData.minX, draggingData?.minX, this.epsilon)) {
160
+ isShowVertical = true
161
+ verticalX = draggingData.minX
162
+ break
163
+ }
154
164
  if (
155
- itemData.minX === draggingData?.minX ||
156
- itemData.maxX === draggingData?.minX
165
+ equal(itemData.minX, draggingData?.minX, this.epsilon) ||
166
+ equal(itemData.maxX, draggingData?.minX, this.epsilon)
157
167
  ) {
158
168
  // 找到则停止循环。减少不必要的遍历
159
169
  isShowVertical = true
@@ -161,8 +171,8 @@ export class SnaplineModel {
161
171
  break
162
172
  }
163
173
  if (
164
- itemData.minX === draggingData?.maxX ||
165
- itemData.maxX === draggingData?.maxX
174
+ equal(itemData.minX, draggingData?.maxX, this.epsilon) ||
175
+ equal(itemData.maxX, draggingData?.maxX, this.epsilon)
166
176
  ) {
167
177
  isShowVertical = true
168
178
  verticalX = draggingData.maxX
@@ -235,4 +245,12 @@ export class SnaplineModel {
235
245
  }
236
246
  }
237
247
 
248
+ function equal(num1: number, num2: number, epsilon: number) {
249
+ if (Math.abs(num1 - num2) <= epsilon) {
250
+ return true
251
+ } else {
252
+ return false
253
+ }
254
+ }
255
+
238
256
  export default SnaplineModel
package/src/options.ts CHANGED
@@ -88,6 +88,7 @@ export namespace Options {
88
88
  history?: boolean
89
89
  outline?: boolean
90
90
  snapline?: boolean
91
+ snaplineEpsilon?: number
91
92
  textEdit?: boolean
92
93
 
93
94
  guards?: GuardsConfig
@@ -12,6 +12,117 @@ import {
12
12
  } from '../algorithm/rotate'
13
13
  import type { SimplePoint } from '../algorithm/rotate'
14
14
 
15
+ export function calculateWidthAndHeight(
16
+ startRotatedTouchControlPoint: SimplePoint,
17
+ endRotatedTouchControlPoint: SimplePoint,
18
+ oldCenter: SimplePoint,
19
+ angle: number,
20
+ freezeWidth = false,
21
+ freezeHeight = false,
22
+ oldWidth: number,
23
+ oldHeight: number,
24
+ ) {
25
+ // 假设目前触摸的是右下角的control点
26
+ // 计算出来左上角的control坐标,resize过程左上角的control坐标保持不变
27
+ const freezePoint: SimplePoint = {
28
+ x: oldCenter.x - (startRotatedTouchControlPoint.x - oldCenter.x),
29
+ y: oldCenter.y - (startRotatedTouchControlPoint.y - oldCenter.y),
30
+ }
31
+ // 【touchEndPoint】右下角 + freezePoint左上角 计算出新的中心点
32
+ const newCenter = getNewCenter(freezePoint, endRotatedTouchControlPoint)
33
+
34
+ // 得到【touchEndPoint】右下角-没有transform的坐标
35
+ let endZeroTouchControlPoint: SimplePoint = calculatePointAfterRotateAngle(
36
+ endRotatedTouchControlPoint,
37
+ newCenter,
38
+ -angle,
39
+ )
40
+
41
+ // ---------- 使用transform之前的坐标计算出新的width和height ----------
42
+
43
+ // 得到左上角---没有transform的坐标
44
+ let zeroFreezePoint: SimplePoint = calculatePointAfterRotateAngle(
45
+ freezePoint,
46
+ newCenter,
47
+ -angle,
48
+ )
49
+
50
+ if (freezeWidth) {
51
+ // 如果固定width,那么不能单纯使用endZeroTouchControlPoint.x=startZeroTouchControlPoint.x
52
+ // 因为去掉transform的左上角不一定是重合的,我们要保证的是transform后的左上角重合
53
+ const newWidth = Math.abs(endZeroTouchControlPoint.x - zeroFreezePoint.x)
54
+ const widthDx = newWidth - oldWidth
55
+
56
+ // 点击的是左边锚点,是+widthDx/2,点击是右边锚点,是-widthDx/2
57
+ if (newCenter.x > endZeroTouchControlPoint.x) {
58
+ // 当前触摸的是左边锚点
59
+ newCenter.x = newCenter.x + widthDx / 2
60
+ } else {
61
+ // 当前触摸的是右边锚点
62
+ newCenter.x = newCenter.x - widthDx / 2
63
+ }
64
+ }
65
+ if (freezeHeight) {
66
+ const newHeight = Math.abs(endZeroTouchControlPoint.y - zeroFreezePoint.y)
67
+ const heightDy = newHeight - oldHeight
68
+ if (newCenter.y > endZeroTouchControlPoint.y) {
69
+ // 当前触摸的是上边锚点
70
+ newCenter.y = newCenter.y + heightDy / 2
71
+ } else {
72
+ newCenter.y = newCenter.y - heightDy / 2
73
+ }
74
+ }
75
+
76
+ if (freezeWidth || freezeHeight) {
77
+ // 如果调整过transform之前的坐标,那么transform后的坐标也会改变,那么算出来的newCenter也得调整
78
+ // 由于无论如何rotate,中心点都是不变的,因此我们可以使用transform之前的坐标算出新的中心点
79
+ const nowFreezePoint = calculatePointAfterRotateAngle(
80
+ zeroFreezePoint,
81
+ newCenter,
82
+ angle,
83
+ )
84
+
85
+ // 得到当前新rect的左上角与实际上transform后的左上角的偏移量
86
+ const dx = nowFreezePoint.x - freezePoint.x
87
+ const dy = nowFreezePoint.y - freezePoint.y
88
+
89
+ // 修正不使用transform的坐标: 左上角、右下角、center
90
+ newCenter.x = newCenter.x - dx
91
+ newCenter.y = newCenter.y - dy
92
+ zeroFreezePoint = calculatePointAfterRotateAngle(
93
+ freezePoint,
94
+ newCenter,
95
+ -angle,
96
+ )
97
+ endZeroTouchControlPoint = {
98
+ x: newCenter.x - (zeroFreezePoint.x - newCenter.x),
99
+ y: newCenter.y - (zeroFreezePoint.y - newCenter.y),
100
+ }
101
+ }
102
+
103
+ // transform之前的坐标的左上角+右下角计算出宽度和高度
104
+ let width = Math.abs(endZeroTouchControlPoint.x - zeroFreezePoint.x)
105
+ let height = Math.abs(endZeroTouchControlPoint.y - zeroFreezePoint.y)
106
+
107
+ // ---------- 使用transform之前的坐标计算出新的width和height ----------
108
+
109
+ if (freezeWidth) {
110
+ // 理论计算出来的width应该等于oldWidth
111
+ // 但是有误差,比如oldWidth = 100; newWidth=100.000000000001
112
+ // 会在handleResize()限制放大缩小的最大最小范围中被阻止滑动
113
+ width = oldWidth
114
+ }
115
+ if (freezeHeight) {
116
+ height = oldHeight
117
+ }
118
+
119
+ return {
120
+ width,
121
+ height,
122
+ center: newCenter,
123
+ }
124
+ }
125
+
15
126
  function recalcRotatedResizeInfo(
16
127
  pct: number,
17
128
  resizeInfo: ResizeInfo,
@@ -89,10 +200,12 @@ export const recalcResizeInfo = (
89
200
  controlY: number | undefined,
90
201
  oldCenterX: number,
91
202
  oldCenterY: number,
203
+ forceProportional = false,
92
204
  ): ResizeInfo => {
93
205
  const nextResizeInfo = cloneDeep(resizeInfo)
94
206
  let { deltaX, deltaY } = nextResizeInfo
95
207
  const { width, height, PCTResizeInfo } = nextResizeInfo
208
+
96
209
  if (PCTResizeInfo) {
97
210
  const sensitivity = 4 // 越低越灵敏
98
211
  let deltaScale = 0
@@ -190,7 +303,88 @@ export const recalcResizeInfo = (
190
303
  )
191
304
  }
192
305
 
193
- // 如果限制了宽/高不变,对应的 width/height 保持一致
306
+ //Shift键等比缩放逻辑
307
+ if (forceProportional) {
308
+ // 计算当前的宽高比
309
+ const aspectRatio = width / height
310
+
311
+ // 根据拖拽方向确定主要的缩放参考
312
+ let primaryDelta = 0
313
+ let newWidth = width
314
+ let newHeight = height
315
+
316
+ switch (index) {
317
+ case ResizeControlIndex.LEFT_TOP:
318
+ // 取绝对值较大的delta作为主要缩放参考
319
+ primaryDelta = Math.abs(deltaX) > Math.abs(deltaY) ? -deltaX : -deltaY
320
+ if (aspectRatio >= 1) {
321
+ // 宽度大于等于高度,以宽度为基准
322
+ newWidth = width + primaryDelta
323
+ newHeight = newWidth / aspectRatio
324
+ } else {
325
+ // 高度大于宽度,以高度为基准
326
+ newHeight = height + primaryDelta
327
+ newWidth = newHeight * aspectRatio
328
+ }
329
+ nextResizeInfo.width = newWidth
330
+ nextResizeInfo.height = newHeight
331
+ nextResizeInfo.deltaX = width - newWidth
332
+ nextResizeInfo.deltaY = height - newHeight
333
+ break
334
+
335
+ case ResizeControlIndex.RIGHT_TOP:
336
+ primaryDelta = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : -deltaY
337
+ if (aspectRatio >= 1) {
338
+ newWidth = width + primaryDelta
339
+ newHeight = newWidth / aspectRatio
340
+ } else {
341
+ newHeight = height - primaryDelta
342
+ newWidth = newHeight * aspectRatio
343
+ }
344
+ nextResizeInfo.width = newWidth
345
+ nextResizeInfo.height = newHeight
346
+ nextResizeInfo.deltaX = newWidth - width
347
+ nextResizeInfo.deltaY = height - newHeight
348
+ break
349
+
350
+ case ResizeControlIndex.RIGHT_BOTTOM:
351
+ primaryDelta = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY
352
+ if (aspectRatio >= 1) {
353
+ newWidth = width + primaryDelta
354
+ newHeight = newWidth / aspectRatio
355
+ } else {
356
+ newHeight = height + primaryDelta
357
+ newWidth = newHeight * aspectRatio
358
+ }
359
+ nextResizeInfo.width = newWidth
360
+ nextResizeInfo.height = newHeight
361
+ nextResizeInfo.deltaX = newWidth - width
362
+ nextResizeInfo.deltaY = newHeight - height
363
+ break
364
+
365
+ case ResizeControlIndex.LEFT_BOTTOM:
366
+ primaryDelta = Math.abs(deltaX) > Math.abs(deltaY) ? -deltaX : deltaY
367
+ if (aspectRatio >= 1) {
368
+ newWidth = width - primaryDelta
369
+ newHeight = newWidth / aspectRatio
370
+ } else {
371
+ newHeight = height + primaryDelta
372
+ newWidth = newHeight * aspectRatio
373
+ }
374
+ nextResizeInfo.width = newWidth
375
+ nextResizeInfo.height = newHeight
376
+ nextResizeInfo.deltaX = width - newWidth
377
+ nextResizeInfo.deltaY = newHeight - height
378
+ break
379
+
380
+ default:
381
+ break
382
+ }
383
+
384
+ return nextResizeInfo
385
+ }
386
+
387
+ // 原有的非等比缩放逻辑保持不变
194
388
  switch (index) {
195
389
  case ResizeControlIndex.LEFT_TOP:
196
390
  nextResizeInfo.width = freezeWidth ? width : width - deltaX * pct
@@ -292,6 +486,7 @@ export type IHandleResizeParams = {
292
486
  nodeModel: BaseNodeModel
293
487
  graphModel: GraphModel
294
488
  cancelCallback?: () => void
489
+ forceProportional?: boolean
295
490
  }
296
491
 
297
492
  /**
@@ -304,6 +499,7 @@ export type IHandleResizeParams = {
304
499
  * @param nodeModel
305
500
  * @param graphModel
306
501
  * @param cancelCallback
502
+ * @param forceProportional
307
503
  */
308
504
  export const handleResize = ({
309
505
  x,
@@ -314,6 +510,7 @@ export const handleResize = ({
314
510
  nodeModel,
315
511
  graphModel,
316
512
  cancelCallback,
513
+ forceProportional = false,
317
514
  }: IHandleResizeParams) => {
318
515
  const {
319
516
  r, // circle
@@ -331,6 +528,7 @@ export const handleResize = ({
331
528
  x: oldCenterX,
332
529
  y: oldCenterY,
333
530
  } = nodeModel
531
+
334
532
  const isFreezeWidth = minWidth === maxWidth
335
533
  const isFreezeHeight = minHeight === maxHeight
336
534
 
@@ -356,6 +554,7 @@ export const handleResize = ({
356
554
  controlY,
357
555
  oldCenterX,
358
556
  oldCenterY,
557
+ forceProportional,
359
558
  )
360
559
 
361
560
  // 限制放大缩小的最大最小范围
@@ -405,114 +604,3 @@ export const handleResize = ({
405
604
  graphModel,
406
605
  )
407
606
  }
408
-
409
- export function calculateWidthAndHeight(
410
- startRotatedTouchControlPoint: SimplePoint,
411
- endRotatedTouchControlPoint: SimplePoint,
412
- oldCenter: SimplePoint,
413
- angle: number,
414
- freezeWidth = false,
415
- freezeHeight = false,
416
- oldWidth: number,
417
- oldHeight: number,
418
- ) {
419
- // 假设目前触摸的是右下角的control点
420
- // 计算出来左上角的control坐标,resize过程左上角的control坐标保持不变
421
- const freezePoint: SimplePoint = {
422
- x: oldCenter.x - (startRotatedTouchControlPoint.x - oldCenter.x),
423
- y: oldCenter.y - (startRotatedTouchControlPoint.y - oldCenter.y),
424
- }
425
- // 【touchEndPoint】右下角 + freezePoint左上角 计算出新的中心点
426
- const newCenter = getNewCenter(freezePoint, endRotatedTouchControlPoint)
427
-
428
- // 得到【touchEndPoint】右下角-没有transform的坐标
429
- let endZeroTouchControlPoint: SimplePoint = calculatePointAfterRotateAngle(
430
- endRotatedTouchControlPoint,
431
- newCenter,
432
- -angle,
433
- )
434
-
435
- // ---------- 使用transform之前的坐标计算出新的width和height ----------
436
-
437
- // 得到左上角---没有transform的坐标
438
- let zeroFreezePoint: SimplePoint = calculatePointAfterRotateAngle(
439
- freezePoint,
440
- newCenter,
441
- -angle,
442
- )
443
-
444
- if (freezeWidth) {
445
- // 如果固定width,那么不能单纯使用endZeroTouchControlPoint.x=startZeroTouchControlPoint.x
446
- // 因为去掉transform的左上角不一定是重合的,我们要保证的是transform后的左上角重合
447
- const newWidth = Math.abs(endZeroTouchControlPoint.x - zeroFreezePoint.x)
448
- const widthDx = newWidth - oldWidth
449
-
450
- // 点击的是左边锚点,是+widthDx/2,点击是右边锚点,是-widthDx/2
451
- if (newCenter.x > endZeroTouchControlPoint.x) {
452
- // 当前触摸的是左边锚点
453
- newCenter.x = newCenter.x + widthDx / 2
454
- } else {
455
- // 当前触摸的是右边锚点
456
- newCenter.x = newCenter.x - widthDx / 2
457
- }
458
- }
459
- if (freezeHeight) {
460
- const newHeight = Math.abs(endZeroTouchControlPoint.y - zeroFreezePoint.y)
461
- const heightDy = newHeight - oldHeight
462
- if (newCenter.y > endZeroTouchControlPoint.y) {
463
- // 当前触摸的是上边锚点
464
- newCenter.y = newCenter.y + heightDy / 2
465
- } else {
466
- newCenter.y = newCenter.y - heightDy / 2
467
- }
468
- }
469
-
470
- if (freezeWidth || freezeHeight) {
471
- // 如果调整过transform之前的坐标,那么transform后的坐标也会改变,那么算出来的newCenter也得调整
472
- // 由于无论如何rotate,中心点都是不变的,因此我们可以使用transform之前的坐标算出新的中心点
473
- const nowFreezePoint = calculatePointAfterRotateAngle(
474
- zeroFreezePoint,
475
- newCenter,
476
- angle,
477
- )
478
-
479
- // 得到当前新rect的左上角与实际上transform后的左上角的偏移量
480
- const dx = nowFreezePoint.x - freezePoint.x
481
- const dy = nowFreezePoint.y - freezePoint.y
482
-
483
- // 修正不使用transform的坐标: 左上角、右下角、center
484
- newCenter.x = newCenter.x - dx
485
- newCenter.y = newCenter.y - dy
486
- zeroFreezePoint = calculatePointAfterRotateAngle(
487
- freezePoint,
488
- newCenter,
489
- -angle,
490
- )
491
- endZeroTouchControlPoint = {
492
- x: newCenter.x - (zeroFreezePoint.x - newCenter.x),
493
- y: newCenter.y - (zeroFreezePoint.y - newCenter.y),
494
- }
495
- }
496
-
497
- // transform之前的坐标的左上角+右下角计算出宽度和高度
498
- let width = Math.abs(endZeroTouchControlPoint.x - zeroFreezePoint.x)
499
- let height = Math.abs(endZeroTouchControlPoint.y - zeroFreezePoint.y)
500
-
501
- // ---------- 使用transform之前的坐标计算出新的width和height ----------
502
-
503
- if (freezeWidth) {
504
- // 理论计算出来的width应该等于oldWidth
505
- // 但是有误差,比如oldWidth = 100; newWidth=100.000000000001
506
- // 会在handleResize()限制放大缩小的最大最小范围中被阻止滑动
507
- width = oldWidth
508
- }
509
- if (freezeHeight) {
510
- height = oldHeight
511
- }
512
-
513
- return {
514
- width,
515
- height,
516
- center: newCenter,
517
- }
518
- }
@@ -1,5 +1,5 @@
1
1
  import { createElement as h, Component } from 'preact/compat'
2
- import { cloneDeep, find, forEach, map } from 'lodash-es'
2
+ import { find, forEach, map, cloneDeep } from 'lodash-es'
3
3
  import { Rect } from './shape'
4
4
  import LogicFlow from '../LogicFlow'
5
5
  import { getNodeBBox, IDragParams, StepDrag, handleResize } from '../util'
@@ -8,8 +8,8 @@ import { BaseNodeModel, GraphModel } from '../model'
8
8
  import NodeData = LogicFlow.NodeData
9
9
  import VectorData = LogicFlow.VectorData
10
10
  import { EventType } from '../constant'
11
- import ResizeInfo = ResizeControl.ResizeInfo
12
11
  import ResizeNodeData = ResizeControl.ResizeNodeData
12
+ import ResizeInfo = ResizeControl.ResizeInfo
13
13
  import ControlItemProps = ResizeControl.ControlItemProps
14
14
 
15
15
  export enum ResizeControlIndex {
@@ -41,6 +41,9 @@ export class ResizeControl extends Component<
41
41
  readonly graphModel: GraphModel
42
42
  readonly dragHandler: StepDrag
43
43
 
44
+ //判断Shift键状态
45
+ private isShiftPressed = false
46
+
44
47
  constructor(props: IResizeControlProps) {
45
48
  super()
46
49
  const { index, model, graphModel } = props
@@ -48,17 +51,39 @@ export class ResizeControl extends Component<
48
51
  this.nodeModel = model
49
52
  this.graphModel = graphModel
50
53
 
51
- // 初始化拖拽工具
52
54
  this.dragHandler = new StepDrag({
53
55
  onDragStart: this.onDragStart,
54
56
  onDragging: this.onDragging,
55
57
  onDragEnd: this.onDragEnd,
56
58
  step: graphModel.gridSize,
57
59
  })
60
+
61
+ this.bindKeyboardEvents()
62
+ }
63
+
64
+ //绑定键盘事件监听
65
+ bindKeyboardEvents = () => {
66
+ document.addEventListener('keydown', this.handleKeyDown)
67
+ document.addEventListener('keyup', this.handleKeyUp)
68
+ }
69
+
70
+ //处理键盘按下事件
71
+ handleKeyDown = (event: KeyboardEvent) => {
72
+ if (event.key === 'Shift') {
73
+ this.isShiftPressed = true
74
+ }
75
+ }
76
+
77
+ handleKeyUp = (event: KeyboardEvent) => {
78
+ if (event.key === 'Shift') {
79
+ this.isShiftPressed = false
80
+ }
58
81
  }
59
82
 
60
83
  componentWillUnmount() {
61
84
  this.dragHandler.destroy()
85
+ document.removeEventListener('keydown', this.handleKeyDown)
86
+ document.removeEventListener('keyup', this.handleKeyUp)
62
87
  }
63
88
 
64
89
  updateEdgePointByAnchors = () => {
@@ -247,7 +272,6 @@ export class ResizeControl extends Component<
247
272
  resizeNode = ({ deltaX, deltaY }: VectorData) => {
248
273
  const { index } = this
249
274
  const { model, graphModel, x, y } = this.props
250
-
251
275
  // DONE: 调用每个节点中更新缩放时的方法 updateNode 函数,用来各节点缩放的方法
252
276
  handleResize({
253
277
  x,
@@ -257,6 +281,7 @@ export class ResizeControl extends Component<
257
281
  index,
258
282
  nodeModel: model,
259
283
  graphModel,
284
+ forceProportional: this.isShiftPressed,
260
285
  cancelCallback: () => {
261
286
  this.dragHandler.cancelDrag()
262
287
  },
@@ -325,7 +350,6 @@ export class ResizeControl extends Component<
325
350
  onDragging = ({ deltaX, deltaY }: IDragParams) => {
326
351
  const { transformModel } = this.graphModel
327
352
  const [dx, dy] = transformModel.fixDeltaXY(deltaX, deltaY)
328
-
329
353
  this.resizeNode({
330
354
  deltaX: dx,
331
355
  deltaY: dy,
@@ -59,6 +59,9 @@ export class Dnd {
59
59
  })
60
60
  }
61
61
  onDragOver = (e: MouseEvent) => {
62
+ this.lf.graphModel.eventCenter.emit(EventType.BLANK_CANVAS_MOUSEMOVE, {
63
+ e,
64
+ })
62
65
  e.preventDefault()
63
66
  if (this.fakeNode) {
64
67
  const { x, y } = this.clientToLocalPoint({