mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
feat(minimap): support touch operation (#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
This commit is contained in:
parent
83ae052705
commit
fd423d9cb5
@ -1,6 +1,6 @@
|
|||||||
import { CSSProperties, type FC } from 'react';
|
import { CSSProperties, type FC } from 'react';
|
||||||
|
|
||||||
import { useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
|
import { MouseTouchEvent, useNodeRender, usePlayground } from '@flowgram.ai/free-layout-editor';
|
||||||
|
|
||||||
import type { CommentEditorModel } from '../model';
|
import type { CommentEditorModel } from '../model';
|
||||||
|
|
||||||
@ -26,23 +26,27 @@ export const ResizeArea: FC<IResizeArea> = (props) => {
|
|||||||
|
|
||||||
const { selectNode } = useNodeRender();
|
const { selectNode } = useNodeRender();
|
||||||
|
|
||||||
const handleMouseDown = (mouseDownEvent: React.MouseEvent) => {
|
const handleResizeStart = (
|
||||||
mouseDownEvent.preventDefault();
|
startResizeEvent: React.MouseEvent | React.TouchEvent | MouseEvent
|
||||||
mouseDownEvent.stopPropagation();
|
) => {
|
||||||
|
MouseTouchEvent.preventDefault(startResizeEvent);
|
||||||
|
startResizeEvent.stopPropagation();
|
||||||
if (!onResize) {
|
if (!onResize) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { resizing, resizeEnd } = onResize();
|
const { resizing, resizeEnd } = onResize();
|
||||||
model.setFocus(false);
|
model.setFocus(false);
|
||||||
selectNode(mouseDownEvent);
|
selectNode(startResizeEvent as React.MouseEvent);
|
||||||
playground.node.focus(); // 防止节点无法被删除
|
playground.node.focus(); // 防止节点无法被删除
|
||||||
|
|
||||||
const startX = mouseDownEvent.clientX;
|
const { clientX: startX, clientY: startY } = MouseTouchEvent.getEventCoord(
|
||||||
const startY = mouseDownEvent.clientY;
|
startResizeEvent as MouseEvent
|
||||||
|
);
|
||||||
|
|
||||||
const handleMouseMove = (mouseMoveEvent: MouseEvent) => {
|
const handleResizing = (mouseMoveEvent: MouseEvent | TouchEvent) => {
|
||||||
const deltaX = mouseMoveEvent.clientX - startX;
|
const { clientX: moveX, clientY: moveY } = MouseTouchEvent.getEventCoord(mouseMoveEvent);
|
||||||
const deltaY = mouseMoveEvent.clientY - startY;
|
const deltaX = moveX - startX;
|
||||||
|
const deltaY = moveY - startY;
|
||||||
const delta = getDelta?.({ x: deltaX, y: deltaY });
|
const delta = getDelta?.({ x: deltaX, y: deltaY });
|
||||||
if (!delta || !resizing) {
|
if (!delta || !resizing) {
|
||||||
return;
|
return;
|
||||||
@ -50,16 +54,22 @@ export const ResizeArea: FC<IResizeArea> = (props) => {
|
|||||||
resizing(delta);
|
resizing(delta);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
const handleResizeEnd = () => {
|
||||||
resizeEnd();
|
resizeEnd();
|
||||||
document.removeEventListener('mousemove', handleMouseMove);
|
document.removeEventListener('mousemove', handleResizing);
|
||||||
document.removeEventListener('mouseup', handleMouseUp);
|
document.removeEventListener('mouseup', handleResizeEnd);
|
||||||
document.removeEventListener('click', handleMouseUp);
|
document.removeEventListener('click', handleResizeEnd);
|
||||||
|
document.removeEventListener('touchmove', handleResizing);
|
||||||
|
document.removeEventListener('touchend', handleResizeEnd);
|
||||||
|
document.removeEventListener('touchcancel', handleResizeEnd);
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('mousemove', handleMouseMove);
|
document.addEventListener('mousemove', handleResizing);
|
||||||
document.addEventListener('mouseup', handleMouseUp);
|
document.addEventListener('mouseup', handleResizeEnd);
|
||||||
document.addEventListener('click', handleMouseUp);
|
document.addEventListener('click', handleResizeEnd);
|
||||||
|
document.addEventListener('touchmove', handleResizing, { passive: false });
|
||||||
|
document.addEventListener('touchend', handleResizeEnd);
|
||||||
|
document.addEventListener('touchcancel', handleResizeEnd);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -67,7 +77,8 @@ export const ResizeArea: FC<IResizeArea> = (props) => {
|
|||||||
className="workflow-comment-resize-area"
|
className="workflow-comment-resize-area"
|
||||||
style={style}
|
style={style}
|
||||||
data-flow-editor-selectable="false"
|
data-flow-editor-selectable="false"
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleResizeStart}
|
||||||
|
onTouchStart={handleResizeStart}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -36,14 +36,17 @@ export const Comment = () => {
|
|||||||
async (mouseEvent: React.MouseEvent<HTMLButtonElement>) => {
|
async (mouseEvent: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
setTooltipVisible(false);
|
setTooltipVisible(false);
|
||||||
const canvasPosition = calcNodePosition(mouseEvent);
|
const canvasPosition = calcNodePosition(mouseEvent);
|
||||||
// 创建节点
|
// create comment node - 创建节点
|
||||||
const node = document.createWorkflowNodeByType(WorkflowNodeType.Comment, canvasPosition);
|
const node = document.createWorkflowNodeByType(WorkflowNodeType.Comment, canvasPosition);
|
||||||
// 等待节点渲染
|
// wait comment node render - 等待节点渲染
|
||||||
await delay(16);
|
await delay(16);
|
||||||
// 选中节点
|
// select comment node - 选中节点
|
||||||
selectService.selectNode(node);
|
selectService.selectNode(node);
|
||||||
// 开始拖拽
|
// maybe touch event - 可能是触摸事件
|
||||||
dragService.startDragSelectedNodes(mouseEvent);
|
if (mouseEvent.detail !== 0) {
|
||||||
|
// start drag -开始拖拽
|
||||||
|
dragService.startDragSelectedNodes(mouseEvent);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[selectService, calcNodePosition, document, dragService]
|
[selectService, calcNodePosition, document, dragService]
|
||||||
);
|
);
|
||||||
|
|||||||
@ -145,6 +145,12 @@ export class PlaygroundLayer extends Layer<PlaygroundLayerOptions> {
|
|||||||
// 这里必须监听 NORMAL_LAYER,该图层最先触发
|
// 这里必须监听 NORMAL_LAYER,该图层最先触发
|
||||||
PipelineLayerPriority.NORMAL_LAYER
|
PipelineLayerPriority.NORMAL_LAYER
|
||||||
),
|
),
|
||||||
|
this.listenPlaygroundEvent('touchend', (e: TouchEvent) => {
|
||||||
|
this.options.hoverService?.clearHovered();
|
||||||
|
}),
|
||||||
|
this.listenPlaygroundEvent('touchcancel', (e: TouchEvent) => {
|
||||||
|
this.options.hoverService?.clearHovered();
|
||||||
|
}),
|
||||||
this.listenPlaygroundEvent(
|
this.listenPlaygroundEvent(
|
||||||
'mousedown',
|
'mousedown',
|
||||||
(e: MouseEvent) => {
|
(e: MouseEvent) => {
|
||||||
|
|||||||
@ -84,6 +84,12 @@ export const MinimapRender: React.FC<MinimapProps> = (props) => {
|
|||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
service.setActivate(false);
|
service.setActivate(false);
|
||||||
}}
|
}}
|
||||||
|
onTouchStartCapture={() => {
|
||||||
|
service.setActivate(true);
|
||||||
|
}}
|
||||||
|
onTouchEndCapture={() => {
|
||||||
|
service.setActivate(false);
|
||||||
|
}}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Disposable, DisposableCollection, IPoint, Rectangle } from '@flowgram.a
|
|||||||
import { FlowNodeEntity, FlowNodeTransformData } from '@flowgram.ai/document';
|
import { FlowNodeEntity, FlowNodeTransformData } from '@flowgram.ai/document';
|
||||||
import { FlowNodeBaseType } from '@flowgram.ai/document';
|
import { FlowNodeBaseType } from '@flowgram.ai/document';
|
||||||
import { FlowDocument } from '@flowgram.ai/document';
|
import { FlowDocument } from '@flowgram.ai/document';
|
||||||
import { EntityManager, PlaygroundConfigEntity } from '@flowgram.ai/core';
|
import { EntityManager, MouseTouchEvent, PlaygroundConfigEntity } from '@flowgram.ai/core';
|
||||||
|
|
||||||
import type { MinimapRenderContext, MinimapServiceOptions, MinimapCanvasStyle } from './type';
|
import type { MinimapRenderContext, MinimapServiceOptions, MinimapCanvasStyle } from './type';
|
||||||
import { MinimapDraw } from './draw';
|
import { MinimapDraw } from './draw';
|
||||||
@ -312,26 +312,29 @@ export class FlowMinimapService {
|
|||||||
private addEventListeners(): void {
|
private addEventListeners(): void {
|
||||||
this.canvas.addEventListener('wheel', this.handleWheel);
|
this.canvas.addEventListener('wheel', this.handleWheel);
|
||||||
this.canvas.addEventListener('mousedown', this.handleStartDrag);
|
this.canvas.addEventListener('mousedown', this.handleStartDrag);
|
||||||
|
this.canvas.addEventListener('touchstart', this.handleStartDrag, { passive: false });
|
||||||
this.canvas.addEventListener('mousemove', this.handleCursor);
|
this.canvas.addEventListener('mousemove', this.handleCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeEventListeners(): void {
|
private removeEventListeners(): void {
|
||||||
this.canvas.removeEventListener('wheel', this.handleWheel);
|
this.canvas.removeEventListener('wheel', this.handleWheel);
|
||||||
this.canvas.removeEventListener('mousedown', this.handleStartDrag);
|
this.canvas.removeEventListener('mousedown', this.handleStartDrag);
|
||||||
|
this.canvas.removeEventListener('touchstart', this.handleStartDrag);
|
||||||
this.canvas.removeEventListener('mousemove', this.handleCursor);
|
this.canvas.removeEventListener('mousemove', this.handleCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleWheel = (event: WheelEvent): void => {};
|
private handleWheel = (event: WheelEvent): void => {};
|
||||||
|
|
||||||
private handleStartDrag = (event: MouseEvent): void => {
|
private handleStartDrag = (event: MouseEvent | TouchEvent): void => {
|
||||||
event.preventDefault();
|
MouseTouchEvent.preventDefault(event);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const renderContext = this.createRenderContext();
|
const renderContext = this.createRenderContext();
|
||||||
const { viewRect, scale, offset } = renderContext;
|
const { viewRect, scale, offset } = renderContext;
|
||||||
const canvasRect = this.canvas.getBoundingClientRect();
|
const canvasRect = this.canvas.getBoundingClientRect();
|
||||||
|
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
|
||||||
const mousePoint: IPoint = {
|
const mousePoint: IPoint = {
|
||||||
x: event.clientX - canvasRect.left,
|
x: clientX - canvasRect.left,
|
||||||
y: event.clientY - canvasRect.top,
|
y: clientY - canvasRect.top,
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewRectOnCanvas = this.rectOnCanvas({
|
const viewRectOnCanvas = this.rectOnCanvas({
|
||||||
@ -344,20 +347,26 @@ export class FlowMinimapService {
|
|||||||
}
|
}
|
||||||
this.isDragging = true;
|
this.isDragging = true;
|
||||||
this.dragStart = mousePoint;
|
this.dragStart = mousePoint;
|
||||||
|
// click
|
||||||
document.addEventListener('mousemove', this.handleDragging);
|
document.addEventListener('mousemove', this.handleDragging);
|
||||||
document.addEventListener('mouseup', this.handleEndDrag);
|
document.addEventListener('mouseup', this.handleEndDrag);
|
||||||
|
// touch
|
||||||
|
document.addEventListener('touchmove', this.handleDragging, { passive: false });
|
||||||
|
document.addEventListener('touchend', this.handleEndDrag);
|
||||||
|
document.addEventListener('touchcancel', this.handleEndDrag);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleDragging = (event: MouseEvent): void => {
|
private handleDragging = (event: MouseEvent | TouchEvent): void => {
|
||||||
if (!this.isDragging || !this.dragStart) return;
|
if (!this.isDragging || !this.dragStart) return;
|
||||||
event.preventDefault();
|
MouseTouchEvent.preventDefault(event);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const renderContext = this.createRenderContext();
|
const renderContext = this.createRenderContext();
|
||||||
const { scale } = renderContext;
|
const { scale } = renderContext;
|
||||||
const canvasRect = this.canvas.getBoundingClientRect();
|
const canvasRect = this.canvas.getBoundingClientRect();
|
||||||
const mouseX = event.clientX - canvasRect.left;
|
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
|
||||||
const mouseY = event.clientY - canvasRect.top;
|
const mouseX = clientX - canvasRect.left;
|
||||||
|
const mouseY = clientY - canvasRect.top;
|
||||||
|
|
||||||
const deltaX = (mouseX - this.dragStart.x) / scale;
|
const deltaX = (mouseX - this.dragStart.x) / scale;
|
||||||
const deltaY = (mouseY - this.dragStart.y) / scale;
|
const deltaY = (mouseY - this.dragStart.y) / scale;
|
||||||
@ -368,11 +377,16 @@ export class FlowMinimapService {
|
|||||||
this.render();
|
this.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleEndDrag = (event: MouseEvent): void => {
|
private handleEndDrag = (event: MouseEvent | TouchEvent): void => {
|
||||||
event.preventDefault();
|
MouseTouchEvent.preventDefault(event);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
// click
|
||||||
document.removeEventListener('mousemove', this.handleDragging);
|
document.removeEventListener('mousemove', this.handleDragging);
|
||||||
document.removeEventListener('mouseup', this.handleEndDrag);
|
document.removeEventListener('mouseup', this.handleEndDrag);
|
||||||
|
// touch
|
||||||
|
document.removeEventListener('touchmove', this.handleDragging);
|
||||||
|
document.removeEventListener('touchend', this.handleEndDrag);
|
||||||
|
document.removeEventListener('touchcancel', this.handleEndDrag);
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
this.dragStart = undefined;
|
this.dragStart = undefined;
|
||||||
this.setActivate(this.isMouseInCanvas(event));
|
this.setActivate(this.isMouseInCanvas(event));
|
||||||
@ -404,13 +418,14 @@ export class FlowMinimapService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private isMouseInCanvas(event: MouseEvent): boolean {
|
private isMouseInCanvas(event: MouseEvent | TouchEvent): boolean {
|
||||||
const canvasRect = this.canvas.getBoundingClientRect();
|
const canvasRect = this.canvas.getBoundingClientRect();
|
||||||
|
const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
|
||||||
return (
|
return (
|
||||||
event.clientX >= canvasRect.left &&
|
clientX >= canvasRect.left &&
|
||||||
event.clientX <= canvasRect.right &&
|
clientX <= canvasRect.right &&
|
||||||
event.clientY >= canvasRect.top &&
|
clientY >= canvasRect.top &&
|
||||||
event.clientY <= canvasRect.bottom
|
clientY <= canvasRect.bottom
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user