Skip to content

Commit fd423d9

Browse files
authored
feat(minimap): support touch operation (bytedance#381)
* fix(core): touch end clear hover * feat(minimap): support touch operation * fix(demo): touch create comment should not trigger drag * feat(demo): comment node support touch resize
1 parent 83ae052 commit fd423d9

File tree

5 files changed

+80
-39
lines changed

5 files changed

+80
-39
lines changed
Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CSSProperties, type FC } from 'react';
22

3-
import { useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
3+
import { MouseTouchEvent, useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
44

55
import type { CommentEditorModel } from '../model';
66

@@ -26,48 +26,59 @@ export const ResizeArea: FC<IResizeArea> = (props) => {
2626

2727
const { selectNode } = useNodeRender();
2828

29-
const handleMouseDown = (mouseDownEvent: React.MouseEvent) => {
30-
mouseDownEvent.preventDefault();
31-
mouseDownEvent.stopPropagation();
29+
const handleResizeStart = (
30+
startResizeEvent: React.MouseEvent | React.TouchEvent | MouseEvent
31+
) => {
32+
MouseTouchEvent.preventDefault(startResizeEvent);
33+
startResizeEvent.stopPropagation();
3234
if (!onResize) {
3335
return;
3436
}
3537
const { resizing, resizeEnd } = onResize();
3638
model.setFocus(false);
37-
selectNode(mouseDownEvent);
39+
selectNode(startResizeEvent as React.MouseEvent);
3840
playground.node.focus(); // 防止节点无法被删除
3941

40-
const startX = mouseDownEvent.clientX;
41-
const startY = mouseDownEvent.clientY;
42+
const { clientX: startX, clientY: startY } = MouseTouchEvent.getEventCoord(
43+
startResizeEvent as MouseEvent
44+
);
4245

43-
const handleMouseMove = (mouseMoveEvent: MouseEvent) => {
44-
const deltaX = mouseMoveEvent.clientX - startX;
45-
const deltaY = mouseMoveEvent.clientY - startY;
46+
const handleResizing = (mouseMoveEvent: MouseEvent | TouchEvent) => {
47+
const { clientX: moveX, clientY: moveY } = MouseTouchEvent.getEventCoord(mouseMoveEvent);
48+
const deltaX = moveX - startX;
49+
const deltaY = moveY - startY;
4650
const delta = getDelta?.({ x: deltaX, y: deltaY });
4751
if (!delta || !resizing) {
4852
return;
4953
}
5054
resizing(delta);
5155
};
5256

53-
const handleMouseUp = () => {
57+
const handleResizeEnd = () => {
5458
resizeEnd();
55-
document.removeEventListener('mousemove', handleMouseMove);
56-
document.removeEventListener('mouseup', handleMouseUp);
57-
document.removeEventListener('click', handleMouseUp);
59+
document.removeEventListener('mousemove', handleResizing);
60+
document.removeEventListener('mouseup', handleResizeEnd);
61+
document.removeEventListener('click', handleResizeEnd);
62+
document.removeEventListener('touchmove', handleResizing);
63+
document.removeEventListener('touchend', handleResizeEnd);
64+
document.removeEventListener('touchcancel', handleResizeEnd);
5865
};
5966

60-
document.addEventListener('mousemove', handleMouseMove);
61-
document.addEventListener('mouseup', handleMouseUp);
62-
document.addEventListener('click', handleMouseUp);
67+
document.addEventListener('mousemove', handleResizing);
68+
document.addEventListener('mouseup', handleResizeEnd);
69+
document.addEventListener('click', handleResizeEnd);
70+
document.addEventListener('touchmove', handleResizing, { passive: false });
71+
document.addEventListener('touchend', handleResizeEnd);
72+
document.addEventListener('touchcancel', handleResizeEnd);
6373
};
6474

6575
return (
6676
<div
6777
className="workflow-comment-resize-area"
6878
style={style}
6979
data-flow-editor-selectable="false"
70-
onMouseDown={handleMouseDown}
80+
onMouseDown={handleResizeStart}
81+
onTouchStart={handleResizeStart}
7182
/>
7283
);
7384
};

apps/demo-free-layout/src/components/tools/comment.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,17 @@ export const Comment = () => {
3636
async (mouseEvent: React.MouseEvent<HTMLButtonElement>) => {
3737
setTooltipVisible(false);
3838
const canvasPosition = calcNodePosition(mouseEvent);
39-
// 创建节点
39+
// create comment node - 创建节点
4040
const node = document.createWorkflowNodeByType(WorkflowNodeType.Comment, canvasPosition);
41-
// 等待节点渲染
41+
// wait comment node render - 等待节点渲染
4242
await delay(16);
43-
// 选中节点
43+
// select comment node - 选中节点
4444
selectService.selectNode(node);
45-
// 开始拖拽
46-
dragService.startDragSelectedNodes(mouseEvent);
45+
// maybe touch event - 可能是触摸事件
46+
if (mouseEvent.detail !== 0) {
47+
// start drag -开始拖拽
48+
dragService.startDragSelectedNodes(mouseEvent);
49+
}
4750
},
4851
[selectService, calcNodePosition, document, dragService]
4952
);

packages/canvas-engine/core/src/core/layer/playground-layer.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ export class PlaygroundLayer extends Layer<PlaygroundLayerOptions> {
145145
// 这里必须监听 NORMAL_LAYER,该图层最先触发
146146
PipelineLayerPriority.NORMAL_LAYER
147147
),
148+
this.listenPlaygroundEvent('touchend', (e: TouchEvent) => {
149+
this.options.hoverService?.clearHovered();
150+
}),
151+
this.listenPlaygroundEvent('touchcancel', (e: TouchEvent) => {
152+
this.options.hoverService?.clearHovered();
153+
}),
148154
this.listenPlaygroundEvent(
149155
'mousedown',
150156
(e: MouseEvent) => {

packages/plugins/minimap-plugin/src/component.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ export const MinimapRender: React.FC<MinimapProps> = (props) => {
8484
onMouseLeave={() => {
8585
service.setActivate(false);
8686
}}
87+
onTouchStartCapture={() => {
88+
service.setActivate(true);
89+
}}
90+
onTouchEndCapture={() => {
91+
service.setActivate(false);
92+
}}
8793
></div>
8894
</div>
8995
);

packages/plugins/minimap-plugin/src/service.ts

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Disposable, DisposableCollection, IPoint, Rectangle } from '@flowgram.a
44
import { FlowNodeEntity, FlowNodeTransformData } from '@flowgram.ai/document';
55
import { FlowNodeBaseType } from '@flowgram.ai/document';
66
import { FlowDocument } from '@flowgram.ai/document';
7-
import { EntityManager, PlaygroundConfigEntity } from '@flowgram.ai/core';
7+
import { EntityManager, MouseTouchEvent, PlaygroundConfigEntity } from '@flowgram.ai/core';
88

99
import type { MinimapRenderContext, MinimapServiceOptions, MinimapCanvasStyle } from './type';
1010
import { MinimapDraw } from './draw';
@@ -312,26 +312,29 @@ export class FlowMinimapService {
312312
private addEventListeners(): void {
313313
this.canvas.addEventListener('wheel', this.handleWheel);
314314
this.canvas.addEventListener('mousedown', this.handleStartDrag);
315+
this.canvas.addEventListener('touchstart', this.handleStartDrag, { passive: false });
315316
this.canvas.addEventListener('mousemove', this.handleCursor);
316317
}
317318

318319
private removeEventListeners(): void {
319320
this.canvas.removeEventListener('wheel', this.handleWheel);
320321
this.canvas.removeEventListener('mousedown', this.handleStartDrag);
322+
this.canvas.removeEventListener('touchstart', this.handleStartDrag);
321323
this.canvas.removeEventListener('mousemove', this.handleCursor);
322324
}
323325

324326
private handleWheel = (event: WheelEvent): void => {};
325327

326-
private handleStartDrag = (event: MouseEvent): void => {
327-
event.preventDefault();
328+
private handleStartDrag = (event: MouseEvent | TouchEvent): void => {
329+
MouseTouchEvent.preventDefault(event);
328330
event.stopPropagation();
329331
const renderContext = this.createRenderContext();
330332
const { viewRect, scale, offset } = renderContext;
331333
const canvasRect = this.canvas.getBoundingClientRect();
334+
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
332335
const mousePoint: IPoint = {
333-
x: event.clientX - canvasRect.left,
334-
y: event.clientY - canvasRect.top,
336+
x: clientX - canvasRect.left,
337+
y: clientY - canvasRect.top,
335338
};
336339

337340
const viewRectOnCanvas = this.rectOnCanvas({
@@ -344,20 +347,26 @@ export class FlowMinimapService {
344347
}
345348
this.isDragging = true;
346349
this.dragStart = mousePoint;
350+
// click
347351
document.addEventListener('mousemove', this.handleDragging);
348352
document.addEventListener('mouseup', this.handleEndDrag);
353+
// touch
354+
document.addEventListener('touchmove', this.handleDragging, { passive: false });
355+
document.addEventListener('touchend', this.handleEndDrag);
356+
document.addEventListener('touchcancel', this.handleEndDrag);
349357
};
350358

351-
private handleDragging = (event: MouseEvent): void => {
359+
private handleDragging = (event: MouseEvent | TouchEvent): void => {
352360
if (!this.isDragging || !this.dragStart) return;
353-
event.preventDefault();
361+
MouseTouchEvent.preventDefault(event);
354362
event.stopPropagation();
355363

356364
const renderContext = this.createRenderContext();
357365
const { scale } = renderContext;
358366
const canvasRect = this.canvas.getBoundingClientRect();
359-
const mouseX = event.clientX - canvasRect.left;
360-
const mouseY = event.clientY - canvasRect.top;
367+
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
368+
const mouseX = clientX - canvasRect.left;
369+
const mouseY = clientY - canvasRect.top;
361370

362371
const deltaX = (mouseX - this.dragStart.x) / scale;
363372
const deltaY = (mouseY - this.dragStart.y) / scale;
@@ -368,11 +377,16 @@ export class FlowMinimapService {
368377
this.render();
369378
};
370379

371-
private handleEndDrag = (event: MouseEvent): void => {
372-
event.preventDefault();
380+
private handleEndDrag = (event: MouseEvent | TouchEvent): void => {
381+
MouseTouchEvent.preventDefault(event);
373382
event.stopPropagation();
383+
// click
374384
document.removeEventListener('mousemove', this.handleDragging);
375385
document.removeEventListener('mouseup', this.handleEndDrag);
386+
// touch
387+
document.removeEventListener('touchmove', this.handleDragging);
388+
document.removeEventListener('touchend', this.handleEndDrag);
389+
document.removeEventListener('touchcancel', this.handleEndDrag);
376390
this.isDragging = false;
377391
this.dragStart = undefined;
378392
this.setActivate(this.isMouseInCanvas(event));
@@ -404,13 +418,14 @@ export class FlowMinimapService {
404418
}
405419
};
406420

407-
private isMouseInCanvas(event: MouseEvent): boolean {
421+
private isMouseInCanvas(event: MouseEvent | TouchEvent): boolean {
408422
const canvasRect = this.canvas.getBoundingClientRect();
423+
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
409424
return (
410-
event.clientX >= canvasRect.left &&
411-
event.clientX <= canvasRect.right &&
412-
event.clientY >= canvasRect.top &&
413-
event.clientY <= canvasRect.bottom
425+
clientX >= canvasRect.left &&
426+
clientX <= canvasRect.right &&
427+
clientY >= canvasRect.top &&
428+
clientY <= canvasRect.bottom
414429
);
415430
}
416431

0 commit comments

Comments
 (0)