diff --git a/src/renderers/webgl/index.ts b/src/renderers/webgl/index.ts index 41a7b335..2b9b918d 100644 --- a/src/renderers/webgl/index.ts +++ b/src/renderers/webgl/index.ts @@ -150,6 +150,7 @@ export class Renderer { animateNodeRadius: number | false = defaultOptions.animateNodeRadius circle: CircleTexture arrow: ArrowTexture + draggedNode?: NodeRenderer private doubleClick = false private doubleClickTimeout?: number @@ -242,7 +243,15 @@ export class Renderer { this.root.addEventListener('pointerdown', this.pointerDown) this.root.addEventListener('pointermove', this.pointerMove) this.root.addEventListener('pointerup', this.pointerUp) - this.root.addEventListener('pointerupoutside', this.pointerUp) + this.root.addEventListener('pointerupoutside', (event) => { + if (this.draggedNode) { + const draggedNode = this.draggedNode + draggedNode.pointerUp(event) + draggedNode.pointerLeave(event) + } else { + this.pointerUp(event) + } + }) this.root.addEventListener('pointercancel', this.pointerUp) this.root.addEventListener('pointerleave', this.pointerLeave) view.addEventListener!('wheel', this.zoomInteraction.wheel, { passive: false }) @@ -590,10 +599,6 @@ export class Renderer { }) } - // TODO - pointerupoutside doesn't work for nodes but does for viewport - // if node is dragging, fire onNodePointerUp on pointer up outside - // TODO - don't fire pointer up if it's handled by a node/edge pointerUp handler - // but still complete drag/decelarate event private pointerUp = (event: FederatedPointerEvent) => { if (!this.pointerIsDown) { return diff --git a/src/renderers/webgl/interaction/edgeHitArea.ts b/src/renderers/webgl/interaction/edgeHitArea.ts index f998ca25..5bb2f189 100644 --- a/src/renderers/webgl/interaction/edgeHitArea.ts +++ b/src/renderers/webgl/interaction/edgeHitArea.ts @@ -7,9 +7,9 @@ const MIN_LINE_HOVER_RADIUS = 2 export class EdgeHitArea { mounted = false + private hitArea = new Container() private container: Container private edgeRenderer: EdgeRenderer - private hitArea = new Container() constructor(container: Container, edgeRenderer: EdgeRenderer) { this.container = container @@ -19,7 +19,6 @@ export class EdgeHitArea { this.hitArea.addEventListener('pointerenter', this.edgeRenderer.pointerEnter) this.hitArea.addEventListener('pointerdown', this.edgeRenderer.pointerDown) this.hitArea.addEventListener('pointerup', this.edgeRenderer.pointerUp) - this.hitArea.addEventListener('pointerupoutside', this.edgeRenderer.pointerUp) this.hitArea.addEventListener('pointercancel', this.edgeRenderer.pointerUp) this.hitArea.addEventListener('pointerleave', this.edgeRenderer.pointerLeave) } diff --git a/src/renderers/webgl/interaction/nodeHitArea.ts b/src/renderers/webgl/interaction/nodeHitArea.ts index 89f88673..b3632373 100644 --- a/src/renderers/webgl/interaction/nodeHitArea.ts +++ b/src/renderers/webgl/interaction/nodeHitArea.ts @@ -4,9 +4,9 @@ import { NodeRenderer } from '../node' export class NodeHitArea { mounted = false + private hitArea = new Container() private container: Container private nodeRenderer: NodeRenderer - private hitArea = new Container() constructor(container: Container, nodeRenderer: NodeRenderer) { this.container = container @@ -16,7 +16,6 @@ export class NodeHitArea { this.hitArea.addEventListener('pointerenter', this.nodeRenderer.pointerEnter) this.hitArea.addEventListener('pointerdown', this.nodeRenderer.pointerDown) this.hitArea.addEventListener('pointerup', this.nodeRenderer.pointerUp) - this.hitArea.addEventListener('pointerupoutside', this.nodeRenderer.pointerUp) this.hitArea.addEventListener('pointercancel', this.nodeRenderer.pointerUp) this.hitArea.addEventListener('pointerleave', this.nodeRenderer.pointerLeave) } diff --git a/src/renderers/webgl/node.ts b/src/renderers/webgl/node.ts index b838cae2..52f703f0 100644 --- a/src/renderers/webgl/node.ts +++ b/src/renderers/webgl/node.ts @@ -243,6 +243,7 @@ export class NodeRenderer { if (this.renderer.onNodeDrag) { event.stopPropagation() + this.renderer.draggedNode = this this.renderer.container.style.cursor = 'move' this.nodeMoveXOffset = local.x - (this.node!.x ?? 0) this.nodeMoveYOffset = local.y - (this.node!.y ?? 0) @@ -299,6 +300,7 @@ export class NodeRenderer { const local = this.renderer.root.toLocal(event.global) if (this.renderer.onNodeDrag) { + this.renderer.draggedNode = undefined this.renderer.container.style.cursor = 'auto' this.renderer.root.removeEventListener('pointermove', this.pointerMove) this.renderer.zoomInteraction.resume() diff --git a/tests/webgl/index.ts b/tests/webgl/index.ts index ab4deebf..44219ad2 100644 --- a/tests/webgl/index.ts +++ b/tests/webgl/index.ts @@ -110,45 +110,45 @@ const options: Renderer.Options = { // onViewportDragEnd: (event: Renderer.ViewportDragEvent | Renderer.ViewportDragDecelerateEvent) => { // console.log('viewport drag end', `x: ${event.dx}, y: ${event.dy}`) // }, - // onViewportPointerUp: (event: Renderer.ViewportPointerEvent) => { - // console.log('viewport pointer up', `x: ${event.x}, y: ${event.y}`) - // }, - // onViewportClick: (event: Renderer.ViewportPointerEvent) => { - // console.log('viewport click', `x: ${event.x}, y: ${event.y}`) - // // if (options.x === 0 && options.y === 0 && options.zoom === 1) { - // // options.x = 1000 - // // options.y = -1000 - // // options.zoom = 0.2 - // // } else { - // // options.x = 0 - // // options.y = 0 - // // options.zoom = 1 - // // } - // // renderer.update({ nodes, edges, options }) - - // // if (options.width === 1400 && options.height === 1000) { - // // options.width = 700 - // // options.height = 500 - // // } else { - // // options.width = 1400 - // // options.height = 1000 - // // } - // // renderer.update({ nodes, edges, options }) - - // // force({ nodes, edges }).then((graph) => { - // // nodes = graph.nodes - - // // const { x, y, zoom } = Graph.boundsToViewport( - // // Graph.getSelectionBounds(nodes, 40), - // // { width: options.width, height: options.height } - // // ) - // // options.x = x - // // options.y = y - // // options.zoom = zoom - - // // renderer.update({ nodes, edges, options: options }) - // // }) - // }, + onViewportPointerUp: (event: Renderer.ViewportPointerEvent) => { + console.log('viewport pointer up', `x: ${event.x}, y: ${event.y}`) + }, + onViewportClick: (event: Renderer.ViewportPointerEvent) => { + console.log('viewport click', `x: ${event.x}, y: ${event.y}`) + // if (options.x === 0 && options.y === 0 && options.zoom === 1) { + // options.x = 1000 + // options.y = -1000 + // options.zoom = 0.2 + // } else { + // options.x = 0 + // options.y = 0 + // options.zoom = 1 + // } + // renderer.update({ nodes, edges, options }) + + // if (options.width === 1400 && options.height === 1000) { + // options.width = 700 + // options.height = 500 + // } else { + // options.width = 1400 + // options.height = 1000 + // } + // renderer.update({ nodes, edges, options }) + + // force({ nodes, edges }).then((graph) => { + // nodes = graph.nodes + + // const { x, y, zoom } = Graph.boundsToViewport( + // Graph.getSelectionBounds(nodes, 40), + // { width: options.width, height: options.height } + // ) + // options.x = x + // options.y = y + // options.zoom = zoom + + // renderer.update({ nodes, edges, options: options }) + // }) + }, // onViewportDoubleClick: (event: Renderer.ViewportPointerEvent) => { // console.log('viewport double click', `x: ${event.x}, y: ${event.y}`) // }, @@ -162,7 +162,7 @@ const options: Renderer.Options = { // console.log('viewport pointer leave', `x: ${event.x}, y: ${event.y}`) // }, onNodePointerEnter: (event: Renderer.NodePointerEvent) => { - console.log('node pointer enter', `x: ${event.x}, y: ${event.y}, id: ${event.target.id}`) + // console.log('node pointer enter', `x: ${event.x}, y: ${event.y}, id: ${event.target.id}`) nodes = nodes.map((node) => (node.id === event.target.id ? { ...node, label: node.label + ' 北京', style: NODE_HOVER_STYLE } : node)) edges = edges.map((edge) => edge.source === event.target.id || edge.target === event.target.id ? { ...edge, style: EDGE_HOVER_STYLE } : edge @@ -175,31 +175,31 @@ const options: Renderer.Options = { // onNodeDragStart: (event: Renderer.NodeDragEvent) => { // console.log('node drag start', `x: ${event.x}, y: ${event.y}`) // }, - // onNodeDrag: (event: Renderer.NodeDragEvent) => { - // console.log('node drag', `x: ${event.x}, y: ${event.y}`) - // nodes = nodes.map((node) => - // node.id === event.target.id ? { ...node, x: (node.x ?? 0) + event.dx, y: (node.y ?? 0) + event.dy } : node - // ) - // renderer.update({ nodes, edges, options }) - // }, + onNodeDrag: (event: Renderer.NodeDragEvent) => { + console.log('node drag', `x: ${event.x}, y: ${event.y}`) + nodes = nodes.map((node) => + node.id === event.target.id ? { ...node, x: (node.x ?? 0) + event.dx, y: (node.y ?? 0) + event.dy } : node + ) + renderer.update({ nodes, edges, options }) + }, // onNodeDragEnd: (event: Renderer.NodeDragEvent) => { // console.log('node drag end', `x: ${event.x}, y: ${event.y}`) // }, - // onNodePointerUp: (event: Renderer.NodePointerEvent) => { - // console.log('node pointer up', `x: ${event.x}, y: ${event.y}`) - // }, - // onNodeClick: (event: Renderer.NodePointerEvent) => { - // console.log('node pointer click', `x: ${event.x}, y: ${event.y}`) - // // const graph = hierarchy(event.target.id, { nodes, edges, options: { separation: (a, b) => 1, nodeSize: [30, 100] } }) - // // nodes = graph.nodes.map((node) => ({ ...node, x: node.y, y: node.x })) - // // edges = graph.edges - // // renderer.update({ nodes, edges, options }) - // }, - // onNodeDoubleClick: (event: Renderer.NodePointerEvent) => { - // console.log('node pointer double click', `x: ${event.x}, y: ${event.y}`) - // }, + onNodePointerUp: (event: Renderer.NodePointerEvent) => { + console.log('node pointer up', `x: ${event.x}, y: ${event.y}`) + }, + onNodeClick: (event: Renderer.NodePointerEvent) => { + console.log('node pointer click', `x: ${event.x}, y: ${event.y}`) + // const graph = hierarchy(event.target.id, { nodes, edges, options: { separation: (a, b) => 1, nodeSize: [30, 100] } }) + // nodes = graph.nodes.map((node) => ({ ...node, x: node.y, y: node.x })) + // edges = graph.edges + // renderer.update({ nodes, edges, options }) + }, + onNodeDoubleClick: (event: Renderer.NodePointerEvent) => { + console.log('node pointer double click', `x: ${event.x}, y: ${event.y}`) + }, onNodePointerLeave: (event: Renderer.NodePointerEvent) => { - console.log('node pointer leave', `x: ${event.x}, y: ${event.y}`) + // console.log('node pointer leave', `x: ${event.x}, y: ${event.y}`) nodes = nodes.map((node) => node.id === event.target.id ? { ...node, label: node.label?.slice(0, node.label.length - 3), style: NODE_STYLE } : node ) @@ -209,24 +209,24 @@ const options: Renderer.Options = { renderer.update({ nodes, edges, options }) }, onEdgePointerEnter: (event: Renderer.EdgePointerEvent) => { - console.log('edge pointer enter', `x: ${event.x}, y: ${event.y}`) + // console.log('edge pointer enter', `x: ${event.x}, y: ${event.y}`) edges = edges.map((edge) => (edge.id === event.target.id ? { ...edge, style: EDGE_HOVER_STYLE } : edge)) renderer.update({ nodes, edges, options }) }, // onEdgePointerDown: (event: Renderer.EdgePointerEvent) => { // console.log('edge pointer down', `x: ${event.x}, y: ${event.y}`) // }, - // onEdgePointerUp: (event: Renderer.EdgePointerEvent) => { - // console.log('edge pointer up', `x: ${event.x}, y: ${event.y}`) - // }, - // onEdgeClick: (event: Renderer.EdgePointerEvent) => { - // console.log('edge pointer click', `x: ${event.x}, y: ${event.y}`) - // }, + onEdgePointerUp: (event: Renderer.EdgePointerEvent) => { + console.log('edge pointer up', `x: ${event.x}, y: ${event.y}`) + }, + onEdgeClick: (event: Renderer.EdgePointerEvent) => { + console.log('edge pointer click', `x: ${event.x}, y: ${event.y}`) + }, // onEdgeDoubleClick: (event: Renderer.EdgePointerEvent) => { // console.log('edge pointer double click', `x: ${event.x}, y: ${event.y}`) // }, onEdgePointerLeave: (event: Renderer.EdgePointerEvent) => { - console.log('edge pointer leave', `x: ${event.x}, y: ${event.y}`) + // console.log('edge pointer leave', `x: ${event.x}, y: ${event.y}`) edges = edges.map((edge) => ({ ...edge, style: EDGE_STYLE })) renderer.update({ nodes, edges, options }) }