@logicflow/core 2.1.4 → 2.2.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.
Files changed (74) hide show
  1. package/.turbo/turbo-build$colon$dev.log +2 -2
  2. package/.turbo/turbo-build.log +6 -6
  3. package/CHANGELOG.md +6 -0
  4. package/dist/index.min.js +1 -1
  5. package/dist/index.min.js.map +1 -1
  6. package/es/model/EditConfigModel.d.ts +3 -1
  7. package/es/model/EditConfigModel.js +5 -0
  8. package/es/model/GraphModel.js +1 -0
  9. package/es/tool/MultipleSelectTool.d.ts +1 -1
  10. package/es/tool/MultipleSelectTool.js +1 -1
  11. package/es/util/drag.d.ts +4 -4
  12. package/es/util/drag.js +12 -6
  13. package/es/view/Anchor.d.ts +3 -3
  14. package/es/view/Anchor.js +30 -4
  15. package/es/view/Control.js +1 -1
  16. package/es/view/Rotate.js +1 -1
  17. package/es/view/behavior/dnd.d.ts +4 -8
  18. package/es/view/behavior/dnd.js +50 -14
  19. package/es/view/edge/AdjustPoint.d.ts +1 -1
  20. package/es/view/edge/AdjustPoint.js +1 -1
  21. package/es/view/edge/BaseEdge.d.ts +2 -1
  22. package/es/view/edge/BaseEdge.js +18 -1
  23. package/es/view/edge/PolylineEdge.d.ts +1 -1
  24. package/es/view/edge/PolylineEdge.js +1 -1
  25. package/es/view/node/BaseNode.d.ts +2 -1
  26. package/es/view/node/BaseNode.js +17 -1
  27. package/es/view/overlay/BezierAdjustOverlay.js +1 -1
  28. package/es/view/overlay/CanvasOverlay.d.ts +12 -1
  29. package/es/view/overlay/CanvasOverlay.js +94 -15
  30. package/es/view/text/BaseText.d.ts +1 -1
  31. package/es/view/text/BaseText.js +1 -1
  32. package/lib/model/EditConfigModel.d.ts +3 -1
  33. package/lib/model/EditConfigModel.js +5 -0
  34. package/lib/model/GraphModel.js +1 -0
  35. package/lib/tool/MultipleSelectTool.d.ts +1 -1
  36. package/lib/tool/MultipleSelectTool.js +1 -1
  37. package/lib/util/drag.d.ts +4 -4
  38. package/lib/util/drag.js +12 -6
  39. package/lib/view/Anchor.d.ts +3 -3
  40. package/lib/view/Anchor.js +30 -4
  41. package/lib/view/Control.js +1 -1
  42. package/lib/view/Rotate.js +1 -1
  43. package/lib/view/behavior/dnd.d.ts +4 -8
  44. package/lib/view/behavior/dnd.js +50 -14
  45. package/lib/view/edge/AdjustPoint.d.ts +1 -1
  46. package/lib/view/edge/AdjustPoint.js +1 -1
  47. package/lib/view/edge/BaseEdge.d.ts +2 -1
  48. package/lib/view/edge/BaseEdge.js +18 -1
  49. package/lib/view/edge/PolylineEdge.d.ts +1 -1
  50. package/lib/view/edge/PolylineEdge.js +1 -1
  51. package/lib/view/node/BaseNode.d.ts +2 -1
  52. package/lib/view/node/BaseNode.js +17 -1
  53. package/lib/view/overlay/BezierAdjustOverlay.js +1 -1
  54. package/lib/view/overlay/CanvasOverlay.d.ts +12 -1
  55. package/lib/view/overlay/CanvasOverlay.js +94 -15
  56. package/lib/view/text/BaseText.d.ts +1 -1
  57. package/lib/view/text/BaseText.js +1 -1
  58. package/package.json +1 -1
  59. package/src/model/EditConfigModel.ts +3 -0
  60. package/src/model/GraphModel.ts +1 -0
  61. package/src/tool/MultipleSelectTool.tsx +2 -2
  62. package/src/util/drag.ts +16 -12
  63. package/src/view/Anchor.tsx +32 -6
  64. package/src/view/Control.tsx +1 -1
  65. package/src/view/Rotate.tsx +1 -1
  66. package/src/view/behavior/dnd.ts +55 -16
  67. package/src/view/edge/AdjustPoint.tsx +2 -2
  68. package/src/view/edge/BaseEdge.tsx +23 -3
  69. package/src/view/edge/PolylineEdge.tsx +2 -2
  70. package/src/view/node/BaseNode.tsx +21 -5
  71. package/src/view/overlay/BezierAdjustOverlay.tsx +1 -1
  72. package/src/view/overlay/CanvasOverlay.tsx +110 -4
  73. package/src/view/text/BaseText.tsx +5 -2
  74. package/stats.html +1 -1
@@ -34,7 +34,7 @@ export declare class PolylineEdge extends BaseEdge<IPolylineEdgeProps> {
34
34
  /**
35
35
  * 不支持重写
36
36
  */
37
- beforeDragStart: (e: any, appendInfo: any) => void;
37
+ beforeDragStart: (e: PointerEvent, appendInfo: any) => void;
38
38
  /**
39
39
  * @overridable 支持重写, 此方法为获取边的形状,如果需要自定义边的形状,请重写此方法。
40
40
  * @example https://docs.logic-flow.cn/docs/#/zh/guide/basic/edge?id=%e5%9f%ba%e4%ba%8e-react-%e7%bb%84%e4%bb%b6%e8%87%aa%e5%ae%9a%e4%b9%89%e8%be%b9
@@ -263,7 +263,7 @@ var PolylineEdge = /** @class */ (function (_super) {
263
263
  }
264
264
  appendInfo.direction = constant_1.SegmentDirection.HORIZONTAL;
265
265
  }
266
- append = ((0, jsx_runtime_1.jsx)("g", { className: this_1.isDragging ? 'lf-dragging' : 'lf-drag-able', onMouseDown: function (e) { return _this.beforeDragStart(e, appendInfo); }, children: (0, jsx_runtime_1.jsx)("g", { className: className, children: this_1.getAppendShape(appendInfo) }) }));
266
+ append = ((0, jsx_runtime_1.jsx)("g", { className: this_1.isDragging ? 'lf-dragging' : 'lf-drag-able', onPointerDown: function (e) { return _this.beforeDragStart(e, appendInfo); }, children: (0, jsx_runtime_1.jsx)("g", { className: className, children: this_1.getAppendShape(appendInfo) }) }));
267
267
  }
268
268
  LineAppendList.push(append);
269
269
  };
@@ -19,6 +19,7 @@ export declare abstract class BaseNode<P extends IProps = IProps> extends Compon
19
19
  mouseUpDrag?: boolean;
20
20
  startTime?: number;
21
21
  modelDisposer: IReactionDisposer;
22
+ longPressTimer?: number;
22
23
  constructor(props: IProps);
23
24
  componentWillUnmount(): void;
24
25
  componentDidMount(): void;
@@ -37,7 +38,7 @@ export declare abstract class BaseNode<P extends IProps = IProps> extends Compon
37
38
  handleMouseUp: () => void;
38
39
  handleClick: (e: MouseEvent) => void;
39
40
  handleContextMenu: (ev: MouseEvent) => void;
40
- handleMouseDown: (ev: MouseEvent) => void;
41
+ handleMouseDown: (ev: PointerEvent) => void;
41
42
  handleFocus: () => void;
42
43
  handleBlur: () => void;
43
44
  setHoverOn: (ev: MouseEvent) => void;
@@ -172,6 +172,10 @@ var BaseNode = /** @class */ (function (_super) {
172
172
  _this.handleMouseUp = function () {
173
173
  var model = _this.props.model;
174
174
  _this.mouseUpDrag = model.isDragging;
175
+ if (_this.longPressTimer) {
176
+ clearTimeout(_this.longPressTimer);
177
+ _this.longPressTimer = undefined;
178
+ }
175
179
  };
176
180
  _this.handleClick = function (e) {
177
181
  // 节点拖拽进画布之后,不触发click事件相关emit
@@ -272,6 +276,16 @@ var BaseNode = /** @class */ (function (_super) {
272
276
  if (editConfigModel.adjustNodePosition && model.draggable) {
273
277
  _this.stepDrag && _this.stepDrag.handleMouseDown(ev);
274
278
  }
279
+ if (_this.longPressTimer) {
280
+ clearTimeout(_this.longPressTimer);
281
+ }
282
+ if (ev.pointerType === 'touch') {
283
+ _this.longPressTimer = window.setTimeout(function () {
284
+ if (!_this.props.model.isDragging) {
285
+ _this.handleContextMenu(ev);
286
+ }
287
+ }, 500);
288
+ }
275
289
  };
276
290
  _this.handleFocus = function () {
277
291
  var _a = _this.props, model = _a.model, graphModel = _a.graphModel;
@@ -453,7 +467,9 @@ var BaseNode = /** @class */ (function (_super) {
453
467
  if (adjustNodePosition && draggable) {
454
468
  this.stepDrag.setStep(gridSize * SCALE_X);
455
469
  }
456
- nodeShape = ((0, jsx_runtime_1.jsx)("g", __assign({ className: "".concat(this.getStateClassName(), " ").concat(className), onMouseDown: this.handleMouseDown, onMouseUp: this.handleMouseUp, onClick: this.handleClick, onMouseEnter: this.setHoverOn, onMouseOver: this.setHoverOn, onMouseLeave: this.setHoverOff, onMouseOut: this.onMouseOut, onContextMenu: this.handleContextMenu, onFocus: this.handleFocus, onBlur: this.handleBlur }, restAttributes, { children: nodeShapeInner })));
470
+ nodeShape = ((0, jsx_runtime_1.jsx)("g", __assign({ className: "".concat(this.getStateClassName(), " ").concat(className), onPointerDown: this.handleMouseDown, onPointerUp: this.handleMouseUp, onClick: this.handleClick,
471
+ //因为移动端点击操作完成会按顺序触发enter、leave、click事件,所以会造成节点的闪烁,所以在这里没有统一状态为Pointer
472
+ onMouseEnter: this.setHoverOn, onMouseOver: this.setHoverOn, onMouseLeave: this.setHoverOff, onMouseOut: this.onMouseOut, onContextMenu: this.handleContextMenu, onFocus: this.handleFocus, onBlur: this.handleBlur }, restAttributes, { children: nodeShapeInner })));
457
473
  }
458
474
  return nodeShape;
459
475
  };
@@ -91,7 +91,7 @@ var BezierAdjustAnchor = /** @class */ (function (_super) {
91
91
  var x = position.x, y = position.y;
92
92
  var bezierModel = this.props.bezierModel;
93
93
  var adjustAnchor = bezierModel.getEdgeStyle().adjustAnchor;
94
- return ((0, jsx_runtime_1.jsx)(shape_1.Circle, __assign({ className: "lf-bezier-adjust-anchor", x: x, y: y }, adjustAnchor, { onMouseDown: function (ev) {
94
+ return ((0, jsx_runtime_1.jsx)(shape_1.Circle, __assign({ className: "lf-bezier-adjust-anchor", x: x, y: y }, adjustAnchor, { onPointerDown: function (ev) {
95
95
  // if (edgeAddable !== false) {
96
96
  _this.dragHandler.handleMouseDown(ev);
97
97
  // }
@@ -13,13 +13,24 @@ export declare class CanvasOverlay extends Component<IProps, IState> {
13
13
  stepDrag: StepDrag;
14
14
  stepScrollX: number;
15
15
  stepScrollY: number;
16
+ pointers: Map<number, {
17
+ x: number;
18
+ y: number;
19
+ }>;
20
+ pinchStartDistance?: number;
21
+ pinchStartScale?: number;
22
+ pinchLastCenterX?: number;
23
+ pinchLastCenterY?: number;
24
+ longPressTimer?: number;
16
25
  constructor(props: IProps);
17
26
  onDragging: ({ deltaX, deltaY }: IDragParams) => void;
18
27
  onDragEnd: () => void;
19
28
  zoomHandler: (ev: WheelEvent) => void;
20
29
  clickHandler: (ev: MouseEvent) => void;
21
30
  handleContextMenu: (ev: MouseEvent) => void;
22
- mouseDownHandler: (ev: MouseEvent) => void;
31
+ pointerDownHandler: (ev: PointerEvent) => void;
32
+ pointerMoveHandler: (ev: PointerEvent) => void;
33
+ pointerUpHandler: (ev: PointerEvent) => void;
23
34
  render(): import("preact/compat").JSX.Element;
24
35
  }
25
36
  export default CanvasOverlay;
@@ -14,17 +14,6 @@ var __extends = (this && this.__extends) || (function () {
14
14
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
15
  };
16
16
  })();
17
- var __assign = (this && this.__assign) || function () {
18
- __assign = Object.assign || function(t) {
19
- for (var s, i = 1, n = arguments.length; i < n; i++) {
20
- s = arguments[i];
21
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
- t[p] = s[p];
23
- }
24
- return t;
25
- };
26
- return __assign.apply(this, arguments);
27
- };
28
17
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
29
18
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
30
19
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -44,11 +33,16 @@ var CanvasOverlay = /** @class */ (function (_super) {
44
33
  var _this = _super.call(this) || this;
45
34
  _this.stepScrollX = 0;
46
35
  _this.stepScrollY = 0;
36
+ _this.pointers = new Map();
47
37
  // get InjectedProps() {
48
38
  // return this.props as InjectedProps;
49
39
  // }
50
40
  _this.onDragging = function (_a) {
51
41
  var deltaX = _a.deltaX, deltaY = _a.deltaY;
42
+ if (_this.longPressTimer) {
43
+ clearTimeout(_this.longPressTimer);
44
+ _this.longPressTimer = undefined;
45
+ }
52
46
  _this.setState({
53
47
  isDragging: true,
54
48
  });
@@ -126,8 +120,36 @@ var CanvasOverlay = /** @class */ (function (_super) {
126
120
  }
127
121
  };
128
122
  // 鼠标、触摸板 按下
129
- _this.mouseDownHandler = function (ev) {
123
+ _this.pointerDownHandler = function (ev) {
130
124
  var _a = _this.props.graphModel, eventCenter = _a.eventCenter, editConfigModel = _a.editConfigModel, SCALE_X = _a.transformModel.SCALE_X, gridSize = _a.gridSize;
125
+ _this.pointers.set(ev.pointerId, { x: ev.clientX, y: ev.clientY });
126
+ if (_this.longPressTimer) {
127
+ clearTimeout(_this.longPressTimer);
128
+ }
129
+ if (ev.pointerType === 'touch') {
130
+ _this.longPressTimer = window.setTimeout(function () {
131
+ _this.handleContextMenu(ev);
132
+ }, 500);
133
+ }
134
+ // 检测双指触摸,初始化捏合缩放
135
+ if (_this.pointers.size === 2) {
136
+ var _b = _this.props.graphModel, transformModel = _b.transformModel, editConfigModel_1 = _b.editConfigModel;
137
+ // 记录两指当前位置用于计算初始距离
138
+ var pts = Array.from(_this.pointers.values());
139
+ var dx = pts[0].x - pts[1].x;
140
+ var dy = pts[0].y - pts[1].y;
141
+ var cx = (pts[0].x + pts[1].x) / 2;
142
+ var cy = (pts[0].y + pts[1].y) / 2;
143
+ // 记录捏合起始距离与当前缩放,后续按比例计算缩放
144
+ _this.pinchStartDistance = Math.hypot(dx, dy);
145
+ _this.pinchStartScale = transformModel.SCALE_X;
146
+ // 双指操作下取消画布拖拽,避免与捏合缩放冲突
147
+ _this.stepDrag.cancelDrag();
148
+ _this.pinchLastCenterX = cx;
149
+ _this.pinchLastCenterY = cy;
150
+ editConfigModel_1.updateEditConfig({ isPinching: true });
151
+ return;
152
+ }
131
153
  var adjustEdge = editConfigModel.adjustEdge, adjustNodePosition = editConfigModel.adjustNodePosition, stopMoveGraph = editConfigModel.stopMoveGraph;
132
154
  var target = ev.target;
133
155
  var isFrozenElement = !adjustEdge && !adjustNodePosition;
@@ -143,6 +165,63 @@ var CanvasOverlay = /** @class */ (function (_super) {
143
165
  _this.clickHandler(ev);
144
166
  }
145
167
  };
168
+ _this.pointerMoveHandler = function (ev) {
169
+ var _a;
170
+ // 记录当前指针位置(按 pointerId)
171
+ _this.pointers.set(ev.pointerId, { x: ev.clientX, y: ev.clientY });
172
+ // 当已记录初始捏合距离且存在两指时,执行捏合缩放
173
+ if (_this.pinchStartDistance && _this.pointers.size >= 2) {
174
+ var _b = _this.props, graphModel = _b.graphModel, _c = _b.graphModel, editConfigModel = _c.editConfigModel, transformModel = _c.transformModel;
175
+ if (editConfigModel.stopZoomGraph)
176
+ return;
177
+ // 取消触摸长按计时,避免捏合过程中误触发上下文菜单
178
+ if (_this.longPressTimer) {
179
+ clearTimeout(_this.longPressTimer);
180
+ }
181
+ // 计算两指间当前距离
182
+ var pts = Array.from(_this.pointers.values());
183
+ var dx = pts[0].x - pts[1].x;
184
+ var dy = pts[0].y - pts[1].y;
185
+ var dist = Math.hypot(dx, dy);
186
+ // 以初始缩放为基准,根据距离比例得到新的缩放比例
187
+ var scale = ((_a = _this.pinchStartScale) !== null && _a !== void 0 ? _a : transformModel.SCALE_X) *
188
+ (dist / _this.pinchStartDistance);
189
+ // 取两指中心作为缩放原点,并转换为画布坐标系
190
+ var cx = (pts[0].x + pts[1].x) / 2;
191
+ var cy = (pts[0].y + pts[1].y) / 2;
192
+ var pos = graphModel.getPointByClient({ x: cx, y: cy });
193
+ var _d = pos.canvasOverlayPosition, x = _d.x, y = _d.y;
194
+ transformModel.zoom(scale, [x, y]);
195
+ // 双指中心位移驱动画布平移,配合缩放实现捏合移动;
196
+ if (!editConfigModel.stopMoveGraph || editConfigModel.isPinching) {
197
+ var deltaX = _this.pinchLastCenterX === undefined ? 0 : cx - _this.pinchLastCenterX;
198
+ var deltaY = _this.pinchLastCenterY === undefined ? 0 : cy - _this.pinchLastCenterY;
199
+ transformModel.translate(deltaX, deltaY);
200
+ _this.pinchLastCenterX = cx;
201
+ _this.pinchLastCenterY = cy;
202
+ }
203
+ ev.preventDefault();
204
+ }
205
+ };
206
+ _this.pointerUpHandler = function (ev) {
207
+ _this.pointers.delete(ev.pointerId);
208
+ if (_this.longPressTimer) {
209
+ clearTimeout(_this.longPressTimer);
210
+ _this.longPressTimer = undefined;
211
+ }
212
+ // 双指松开或仅剩一指:结束捏合手势并清理临时状态
213
+ if (_this.pointers.size < 2) {
214
+ // 清空捏合距离与缩放起始值
215
+ _this.pinchStartDistance = undefined;
216
+ _this.pinchStartScale = undefined;
217
+ // 清空上一帧的双指中心
218
+ _this.pinchLastCenterX = undefined;
219
+ _this.pinchLastCenterY = undefined;
220
+ var editConfigModel = _this.props.graphModel.editConfigModel;
221
+ // 标记退出捏合,框选等交互可恢复
222
+ editConfigModel.updateEditConfig({ isPinching: false });
223
+ }
224
+ };
146
225
  var _a = props.graphModel, gridSize = _a.gridSize, eventCenter = _a.eventCenter;
147
226
  _this.stepDrag = new util_1.StepDrag({
148
227
  onDragging: _this.onDragging,
@@ -162,11 +241,11 @@ var CanvasOverlay = /** @class */ (function (_super) {
162
241
  CanvasOverlay.prototype.render = function () {
163
242
  var transformModel = this.props.graphModel.transformModel;
164
243
  var transform = transformModel.getTransformStyle().transform;
165
- var _a = this.props, children = _a.children, dnd = _a.dnd;
244
+ var children = this.props.children;
166
245
  var isDragging = this.state.isDragging;
167
- return ((0, jsx_runtime_1.jsx)("svg", __assign({ xmlns: "http://www.w3.org/2000/svg", width: "100%", height: "100%", name: "canvas-overlay", onWheel: this.zoomHandler, onMouseDown: this.mouseDownHandler, onContextMenu: this.handleContextMenu, className: isDragging
246
+ return ((0, jsx_runtime_1.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "100%", height: "100%", name: "canvas-overlay", onWheel: this.zoomHandler, onPointerDown: this.pointerDownHandler, onPointerMove: this.pointerMoveHandler, onPointerUp: this.pointerUpHandler, onPointerCancel: this.pointerUpHandler, onContextMenu: this.handleContextMenu, style: { touchAction: 'none', WebkitUserSelect: 'none' }, className: isDragging
168
247
  ? 'lf-canvas-overlay lf-dragging'
169
- : 'lf-canvas-overlay lf-drag-able' }, dnd.eventMap(), { children: (0, jsx_runtime_1.jsx)("g", { transform: transform, children: children }) })));
248
+ : 'lf-canvas-overlay lf-drag-able', children: (0, jsx_runtime_1.jsx)("g", { transform: transform, children: children }) }));
170
249
  };
171
250
  CanvasOverlay = __decorate([
172
251
  __1.observer
@@ -14,7 +14,7 @@ export declare class BaseText<P extends IBaseTextProps, S extends IBaseTextState
14
14
  stepperDrag: StepDrag;
15
15
  constructor(props: P);
16
16
  getShape(): h.JSX.Element | null;
17
- mouseDownHandler: (e: MouseEvent) => void;
17
+ mouseDownHandler: (e: PointerEvent) => void;
18
18
  onDragging: ({ deltaX, deltaY }: IDragParams) => void;
19
19
  dbClickHandler: () => void;
20
20
  render(): h.JSX.Element | undefined;
@@ -125,7 +125,7 @@ var BaseText = /** @class */ (function (_super) {
125
125
  BaseText.prototype.render = function () {
126
126
  var text = this.props.model.text;
127
127
  if (text) {
128
- return ((0, jsx_runtime_1.jsx)("g", { onMouseDown: this.mouseDownHandler, onDblClick: this.dbClickHandler, children: this.getShape() }));
128
+ return ((0, jsx_runtime_1.jsx)("g", { onPointerDown: this.mouseDownHandler, onDblClick: this.dbClickHandler, children: this.getShape() }));
129
129
  }
130
130
  };
131
131
  return BaseText;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logicflow/core",
3
- "version": "2.1.4",
3
+ "version": "2.2.0",
4
4
  "description": "LogicFlow, help you quickly create flowcharts",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -125,6 +125,7 @@ export interface IEditConfigType {
125
125
  edgeTextMode: TextMode
126
126
  // 开启网格对齐
127
127
  snapGrid: boolean
128
+ isPinching: boolean
128
129
  }
129
130
 
130
131
  export type IConfigKeys = keyof IEditConfigType
@@ -186,6 +187,7 @@ const allKeys = [
186
187
  'edgeTextMultiple', // 是否支持多个边文本
187
188
  'nodeTextVertical', // 节点文本是否纵向显示
188
189
  'edgeTextVertical', // 边文本是否纵向显示
190
+ 'isPinching', //是否是双指捏合态
189
191
  ] as const
190
192
 
191
193
  /**
@@ -202,6 +204,7 @@ export class EditConfigModel {
202
204
  @observable stopMoveGraph = false
203
205
  @observable stopScrollGraph = false
204
206
  @observable snapGrid = false
207
+ @observable isPinching = false
205
208
  /*********************************************************
206
209
  * 文本相关配置(全局)
207
210
  ********************************************************/
@@ -1248,6 +1248,7 @@ export class GraphModel {
1248
1248
  clearSelectElements() {
1249
1249
  this.selectElements.forEach((element) => {
1250
1250
  element?.setSelected(false)
1251
+ element?.setHovered(false)
1251
1252
  })
1252
1253
  this.selectElements.clear()
1253
1254
  /**
@@ -26,7 +26,7 @@ export default class MultipleSelect extends Component<IToolProps> {
26
26
  })
27
27
  }
28
28
 
29
- handleMouseDown = (ev: MouseEvent) => {
29
+ handleMouseDown = (ev: PointerEvent) => {
30
30
  this.stepDrag.handleMouseDown(ev)
31
31
  }
32
32
  // 使多选区域的滚轮事件可以触发画布的滚轮事件
@@ -123,7 +123,7 @@ export default class MultipleSelect extends Component<IToolProps> {
123
123
  <div
124
124
  className="lf-multiple-select"
125
125
  style={style}
126
- onMouseDown={this.handleMouseDown}
126
+ onPointerDown={this.handleMouseDown}
127
127
  onContextMenu={this.handleContextMenu}
128
128
  onWheel={this.handleWheelEvent}
129
129
  />
package/src/util/drag.ts CHANGED
@@ -10,7 +10,7 @@ const LEFT_MOUSE_BUTTON_CODE = 0
10
10
  export type IDragParams = {
11
11
  deltaX: number
12
12
  deltaY: number
13
- event?: MouseEvent
13
+ event?: PointerEvent
14
14
  [key: string]: unknown
15
15
  }
16
16
 
@@ -99,16 +99,16 @@ export class StepDrag {
99
99
  this.model = model
100
100
  }
101
101
 
102
- handleMouseDown = (e: MouseEvent) => {
102
+ handleMouseDown = (e: PointerEvent) => {
103
103
  const DOC: any = window?.document
104
104
  if (e.button !== LEFT_MOUSE_BUTTON_CODE) return
105
105
  if (this.isStopPropagation) e.stopPropagation()
106
+ e.preventDefault()
106
107
  this.isStartDragging = true
107
108
  this.startX = e.clientX
108
109
  this.startY = e.clientY
109
-
110
- DOC.addEventListener('mousemove', this.handleMouseMove, false)
111
- DOC.addEventListener('mouseup', this.handleMouseUp, false)
110
+ DOC.addEventListener('pointermove', this.handleMouseMove, false)
111
+ DOC.addEventListener('pointerup', this.handleMouseUp, false)
112
112
  const elementData = this.model?.getData()
113
113
  this.eventCenter?.emit(EventType[`${this.eventType}_MOUSEDOWN`], {
114
114
  e,
@@ -117,8 +117,9 @@ export class StepDrag {
117
117
  this.startTime = new Date().getTime()
118
118
  }
119
119
 
120
- handleMouseMove = (e: MouseEvent) => {
120
+ handleMouseMove = (e: PointerEvent) => {
121
121
  if (this.isStopPropagation) e.stopPropagation()
122
+ e.preventDefault()
122
123
  if (!this.isStartDragging) return
123
124
  this.sumDeltaX += e.clientX - this.startX
124
125
  this.sumDeltaY += e.clientY - this.startY
@@ -170,15 +171,19 @@ export class StepDrag {
170
171
  }
171
172
  }
172
173
 
173
- handleMouseUp = (e: MouseEvent) => {
174
+ handleMouseUp = (e: PointerEvent) => {
174
175
  const DOC = window.document
175
176
 
176
177
  this.isStartDragging = false
177
178
  if (this.isStopPropagation) e.stopPropagation()
179
+ const target = e.target as any
180
+ if (target && typeof target.releasePointerCapture === 'function') {
181
+ target.releasePointerCapture(e.pointerId)
182
+ }
178
183
  // fix #568: 如果onDragging在下一个事件循环中触发,而drop在当前事件循环,会出现问题。
179
184
  Promise.resolve().then(() => {
180
- DOC.removeEventListener('mousemove', this.handleMouseMove, false)
181
- DOC.removeEventListener('mouseup', this.handleMouseUp, false)
185
+ DOC.removeEventListener('pointermove', this.handleMouseMove, false)
186
+ DOC.removeEventListener('pointerup', this.handleMouseUp, false)
182
187
  const elementData = this.model?.getData()
183
188
  this.eventCenter?.emit(EventType[`${this.eventType}_MOUSEUP`], {
184
189
  e,
@@ -195,9 +200,8 @@ export class StepDrag {
195
200
  }
196
201
  cancelDrag = () => {
197
202
  const DOC: any = window?.document
198
-
199
- DOC.removeEventListener('mousemove', this.handleMouseMove, false)
200
- DOC.removeEventListener('mouseup', this.handleMouseUp, false)
203
+ DOC.removeEventListener('pointermove', this.handleMouseMove, false)
204
+ DOC.removeEventListener('pointerup', this.handleMouseUp, false)
201
205
  this.onDragEnd({ event: undefined })
202
206
  this.isDragging = false
203
207
  }
@@ -29,7 +29,7 @@ interface IProps {
29
29
  anchorIndex: number
30
30
  graphModel: GraphModel
31
31
  nodeModel: BaseNodeModel
32
- setHoverOff: (e: MouseEvent) => void
32
+ setHoverOff: (e: PointerEvent) => void
33
33
  }
34
34
 
35
35
  interface IState {
@@ -154,7 +154,7 @@ class Anchor extends Component<IProps, IState> {
154
154
  endY: y1,
155
155
  dragging: true,
156
156
  })
157
- this.moveAnchorEnd(x1, y1)
157
+ this.moveAnchorEnd(x1, y1, event)
158
158
  if (nearBoundary.length > 0 && !stopMoveGraph && autoExpand) {
159
159
  this.t = createRaf(() => {
160
160
  const [translateX, translateY] = nearBoundary
@@ -164,7 +164,7 @@ class Anchor extends Component<IProps, IState> {
164
164
  endX: endX - translateX,
165
165
  endY: endY - translateY,
166
166
  })
167
- this.moveAnchorEnd(endX - translateX, endY - translateY)
167
+ this.moveAnchorEnd(endX - translateX, endY - translateY, event)
168
168
  })
169
169
  }
170
170
  eventCenter.emit(EventType.ANCHOR_DRAG, {
@@ -189,6 +189,11 @@ class Anchor extends Component<IProps, IState> {
189
189
  this.sourceRuleResults.clear()
190
190
  this.targetRuleResults.clear()
191
191
  const { graphModel, nodeModel, anchorData } = this.props
192
+ // 拖拽结束清理:取消悬浮态
193
+ if (this.preTargetNode) {
194
+ this.preTargetNode.setHovered(false)
195
+ this.preTargetNode = undefined
196
+ }
192
197
 
193
198
  graphModel.eventCenter.emit(EventType.ANCHOR_DRAGEND, {
194
199
  data: anchorData,
@@ -216,7 +221,7 @@ class Anchor extends Component<IProps, IState> {
216
221
  }
217
222
  }
218
223
 
219
- checkEnd = (event: MouseEvent | null | undefined) => {
224
+ checkEnd = (event: PointerEvent | null | undefined) => {
220
225
  const {
221
226
  graphModel,
222
227
  nodeModel,
@@ -289,7 +294,7 @@ class Anchor extends Component<IProps, IState> {
289
294
  }
290
295
  }
291
296
 
292
- moveAnchorEnd(endX: number, endY: number) {
297
+ moveAnchorEnd(endX: number, endY: number, event?: PointerEvent) {
293
298
  const { graphModel, nodeModel, anchorData } = this.props
294
299
  const info = targetNodeInfo(
295
300
  {
@@ -344,12 +349,33 @@ class Anchor extends Component<IProps, IState> {
344
349
  } else {
345
350
  targetNode.setElementState(ElementState.NOT_ALLOW_CONNECT)
346
351
  }
352
+ // 人工触发进入目标节点事件,同步设置 hovered 以驱动锚点显隐和样式
353
+ if (!targetNode.isHovered) {
354
+ const nodeData = targetNode.getData()
355
+ if (event) {
356
+ graphModel.eventCenter.emit(EventType.NODE_MOUSEENTER, {
357
+ data: nodeData,
358
+ e: event,
359
+ })
360
+ }
361
+ targetNode.setHovered(true)
362
+ }
347
363
  } else if (
348
364
  this.preTargetNode &&
349
365
  this.preTargetNode.state !== ElementState.DEFAULT
350
366
  ) {
351
367
  // 为了保证鼠标离开的时候,将上一个节点状态重置为正常状态。
352
368
  this.preTargetNode.setElementState(ElementState.DEFAULT)
369
+ // 未命中任何节点:人工派发离开事件并取消悬浮,避免状态残留
370
+ const prevData = this.preTargetNode.getData()
371
+ if (event) {
372
+ graphModel.eventCenter.emit(EventType.NODE_MOUSELEAVE, {
373
+ data: prevData,
374
+ e: event,
375
+ })
376
+ }
377
+ this.preTargetNode.setHovered(false)
378
+ this.preTargetNode = undefined
353
379
  }
354
380
  }
355
381
 
@@ -375,7 +401,7 @@ class Anchor extends Component<IProps, IState> {
375
401
  nodeModel,
376
402
  })
377
403
  }}
378
- onMouseDown={(ev) => {
404
+ onPointerDown={(ev) => {
379
405
  graphModel.eventCenter.emit(EventType.ANCHOR_MOUSEDOWN, {
380
406
  data: anchorData,
381
407
  e: ev!,
@@ -393,7 +393,7 @@ export class ResizeControl extends Component<
393
393
  height={25}
394
394
  fill="transparent"
395
395
  stroke="transparent"
396
- onMouseDown={this.dragHandler.handleMouseDown}
396
+ onPointerDown={this.dragHandler.handleMouseDown}
397
397
  />
398
398
  </g>
399
399
  )
@@ -99,7 +99,7 @@ class RotateControlPoint extends Component<IRotateControlProps> {
99
99
  return (
100
100
  <g className="lf-rotate-control">
101
101
  <g
102
- onMouseDown={(ev) => {
102
+ onPointerDown={(ev) => {
103
103
  this.stepperDrag.handleMouseDown(ev)
104
104
  }}
105
105
  >
@@ -11,6 +11,8 @@ export class Dnd {
11
11
  nodeConfig: OnDragNodeConfig | null = null
12
12
  lf: LogicFlow
13
13
  fakeNode: BaseNodeModel | null = null
14
+ docPointerMove?: (e: PointerEvent) => void
15
+ docPointerUp?: (e: PointerEvent) => void
14
16
 
15
17
  constructor(params: { lf: LogicFlow }) {
16
18
  const { lf } = params
@@ -36,19 +38,67 @@ export class Dnd {
36
38
  }
37
39
  }
38
40
 
41
+ isInsideCanvas(e: PointerEvent): boolean {
42
+ const overlay = this.lf.graphModel.rootEl.querySelector(
43
+ '[name="canvas-overlay"]',
44
+ ) as HTMLElement | null
45
+ const topEl = window.document.elementFromPoint(
46
+ e.clientX,
47
+ e.clientY,
48
+ ) as HTMLElement | null
49
+ return (
50
+ topEl === overlay ||
51
+ (topEl !== null && !!overlay && overlay.contains(topEl))
52
+ )
53
+ }
39
54
  startDrag(nodeConfig: OnDragNodeConfig) {
40
55
  const { editConfigModel } = this.lf.graphModel
41
- if (!editConfigModel?.isSilentMode) {
42
- this.nodeConfig = nodeConfig
43
- window.document.addEventListener('mouseup', this.stopDrag)
56
+ if (editConfigModel?.isSilentMode) return
57
+ this.nodeConfig = nodeConfig
58
+ // 指针移动:根据命中结果判断是否在画布覆盖层上,驱动假节点创建/移动或清理
59
+ this.docPointerMove = (e: PointerEvent) => {
60
+ if (!this.nodeConfig) return
61
+ // 离开画布:清理吸附线与假节点
62
+ if (!this.isInsideCanvas(e)) {
63
+ this.onDragLeave()
64
+ return
65
+ }
66
+ // 首次进入画布:创建假节点并初始化位置
67
+ if (!this.fakeNode) {
68
+ this.dragEnter(e)
69
+ return
70
+ }
71
+ // 在画布内移动:更新假节点位置与吸附线
72
+ this.onDragOver(e)
73
+ }
74
+ // 指针抬起:在画布内落点生成节点,否则清理假节点
75
+ this.docPointerUp = (e: PointerEvent) => {
76
+ if (!this.nodeConfig) return
77
+ if (this.isInsideCanvas(e)) {
78
+ this.onDrop(e)
79
+ } else {
80
+ this.onDragLeave()
81
+ }
82
+ // 阻止默认行为与冒泡,避免滚动/点击穿透
83
+ e.preventDefault()
84
+ e.stopPropagation()
85
+ // 结束拖拽并移除监听
86
+ this.stopDrag()
44
87
  }
88
+ window.document.addEventListener('pointermove', this.docPointerMove)
89
+ window.document.addEventListener('pointerup', this.docPointerUp)
45
90
  }
46
91
 
47
92
  stopDrag = () => {
48
93
  this.nodeConfig = null
49
- window.document.removeEventListener('mouseup', this.stopDrag)
94
+ if (this.docPointerMove) {
95
+ window.document.removeEventListener('pointermove', this.docPointerMove)
96
+ }
97
+ if (this.docPointerUp) {
98
+ window.document.removeEventListener('pointerup', this.docPointerUp)
99
+ }
50
100
  }
51
- dragEnter = (e: MouseEvent) => {
101
+ dragEnter = (e: PointerEvent) => {
52
102
  if (!this.nodeConfig || this.fakeNode) return
53
103
  this.fakeNode = this.lf.createFakeNode({
54
104
  ...this.nodeConfig,
@@ -107,17 +157,6 @@ export class Dnd {
107
157
  this.lf.graphModel.removeFakeNode()
108
158
  this.fakeNode = null
109
159
  }
110
-
111
- eventMap() {
112
- return {
113
- onMouseEnter: this.dragEnter,
114
- onMouseOver: this.dragEnter, // IE11
115
- onMouseMove: this.onDragOver,
116
- onMouseLeave: this.onDragLeave,
117
- // onMouseOut: this.onDragLeave, // IE11
118
- onMouseUp: this.onDrop,
119
- }
120
- }
121
160
  }
122
161
 
123
162
  export default Dnd
@@ -78,7 +78,7 @@ export class AdjustPoint extends Component<IProps, IState> {
78
78
  })
79
79
  }
80
80
 
81
- handleMouseDown = (ev: MouseEvent) => {
81
+ handleMouseDown = (ev: PointerEvent) => {
82
82
  if (this.stepDrag) {
83
83
  this.stepDrag.handleMouseDown(ev)
84
84
  }
@@ -414,7 +414,7 @@ export class AdjustPoint extends Component<IProps, IState> {
414
414
  return (
415
415
  <g
416
416
  pointerEvents={dragging ? 'none' : ''}
417
- onMouseDown={this.handleMouseDown}
417
+ onPointerDown={this.handleMouseDown}
418
418
  >
419
419
  {!dragging ? getAdjustPointShape(x, y, edgeModel) : ''}
420
420
  </g>