fix(vue-flow,nodes): use computed pos to calculate relative node pos

This commit is contained in:
bcakmakoglu
2022-06-01 16:16:58 +02:00
committed by Braks
parent 30efd62082
commit 29439f005b
6 changed files with 57 additions and 60 deletions

View File

@@ -6,7 +6,7 @@ import type { MaybeRef } from '@vueuse/core'
import useVueFlow from './useVueFlow'
import { handleNodeClick, pointToRendererPoint } from '~/utils'
import type { NodeDragEvent, NodeDragItem, XYPosition } from '~/types'
import { getDragItems, getEventHandlerParams, getParentNodePosition, hasSelector, updatePosition } from '~/utils/drag'
import { getDragItems, getEventHandlerParams, hasSelector, updatePosition } from '~/utils/drag'
export type UseDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>
@@ -41,24 +41,21 @@ function useDrag(params: UseDragParams) {
const dragging = ref(false)
let dragItems = $ref<NodeDragItem[]>()
let lastPos = $ref<Partial<XYPosition>>({ x: undefined, y: undefined })
let parentPos = $ref<XYPosition>({ x: 0, y: 0 })
let dragHandler = $ref<any>()
const getMousePosition = (event: UseDragEvent) => {
const mousePos = pointToRendererPoint(
const x = event.sourceEvent.touches ? event.sourceEvent.touches[0].clientX : event.sourceEvent.clientX
const y = event.sourceEvent.touches ? event.sourceEvent.touches[0].clientY : event.sourceEvent.clientY
return pointToRendererPoint(
{
x: event.sourceEvent.clientX,
y: event.sourceEvent.clientY,
x,
y,
},
viewport,
snapToGrid,
snapGrid,
)
mousePos.x -= parentPos.x
mousePos.y -= parentPos.y
return mousePos
}
watch(
@@ -73,8 +70,6 @@ function useDrag(params: UseDragParams) {
} else {
dragHandler = drag()
.on('start', (event: UseDragEvent) => {
parentPos = getParentNodePosition(node && node.parentNode ? getNode(node!.parentNode!) : undefined)
if (!selectNodesOnDrag && !multiSelectionActive && id) {
if (!node?.selected) {
removeSelectedElements()
@@ -92,14 +87,12 @@ function useDrag(params: UseDragParams) {
const [currentNode, nodes] = getEventHandlerParams({
id,
dragItems,
node: node!,
getNode: $$(getNode),
})
onStart(event.sourceEvent, currentNode, nodes)
}
})
.on('drag', (event: UseDragEvent) => {
parentPos = getParentNodePosition(node && node.parentNode ? getNode(node!.parentNode!) : undefined)
const mousePos = getMousePosition(event)
// skip events without movement
@@ -109,14 +102,14 @@ function useDrag(params: UseDragParams) {
updatePosition(n, mousePos, n.parentNode ? getNode(n.parentNode) : undefined, nodeExtent),
)
updateNodePositions(dragItems)
updateNodePositions(dragItems, true, true)
dragging.value = true
if (onDrag) {
const [currentNode, nodes] = getEventHandlerParams({
id,
dragItems,
node: node!,
getNode: $$(getNode),
})
onDrag(event.sourceEvent, currentNode, nodes)
}
@@ -125,10 +118,12 @@ function useDrag(params: UseDragParams) {
event.on('end', (event) => {
dragging.value = false
if (onStop && dragItems) {
updateNodePositions(dragItems, false, false)
const [currentNode, nodes] = getEventHandlerParams({
id,
dragItems,
node: node!,
getNode: $$(getNode),
})
onStop(event.sourceEvent, currentNode, nodes)
}

View File

@@ -30,16 +30,31 @@ import {
} from '~/utils'
export default (state: State, getters: ComputedGetters): Actions => {
const updateNodePositions: Actions['updateNodePositions'] = (dragItems) => {
const updateNodePositions: Actions['updateNodePositions'] = (dragItems, changed, dragging) => {
const changes: NodePositionChange[] = []
dragItems.forEach((node) => {
const change: NodePositionChange = {
const change: Partial<NodePositionChange> = {
id: node.id,
type: 'position',
position: node.position,
dragging,
}
changes.push(change)
if (changed) {
change.computedPosition = node.position
change.position = node.position
if (node.parentNode) {
const parentNode = getters.getNode.value(node.parentNode)
change.position = {
x: change.position.x - (parentNode?.computedPosition?.x ?? 0),
y: change.position.y - (parentNode?.computedPosition?.y ?? 0),
}
}
}
changes.push(change as NodePositionChange)
})
if (changes?.length) {

View File

@@ -8,8 +8,6 @@ export interface NodeDragItem {
position: XYPosition
// distance from the mouse cursor to the node when start dragging
distance: XYPosition
// delta to previous position
delta: XYPosition
dimensions: Dimensions
extent?: Node['extent']
parentNode?: string
@@ -25,7 +23,9 @@ export interface NodeDimensionChange {
export interface NodePositionChange {
id: string
type: 'position'
position?: XYPosition
position: XYPosition
computedPosition: XYPosition
dragging: boolean
}
export interface NodeSelectionChange {

View File

@@ -113,7 +113,7 @@ export type SetState = (
| Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>
| ((state: State) => Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>),
) => void
export type UpdateNodePosition = (dragItems: NodeDragItem[]) => void
export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void
export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void
export interface Actions {

View File

@@ -97,6 +97,11 @@ export const applyChanges = <
case 'position':
if (isGraphNode(el)) {
if (typeof change.position !== 'undefined') el.position = change.position
if (typeof change.computedPosition !== 'undefined') {
el.computedPosition = { ...el.computedPosition, ...change.computedPosition }
}
if (el.expandParent && el.parentNode) {
const parent = elements.find((parent) => parent.id === el.parentNode)

View File

@@ -1,13 +1,6 @@
import type { Ref } from 'vue'
import { clampPosition, isParentSelected } from './graph'
import type { CoordinateExtent, Getters, GraphNode, NodeDragItem, XYPosition } from '~/types'
export function getParentNodePosition(parent?: GraphNode): XYPosition {
return {
x: parent?.computedPosition?.x || 0,
y: parent?.computedPosition?.y || 0,
}
}
import type { ComputedGetters, CoordinateExtent, Getters, GraphNode, NodeDragItem, XYPosition } from '~/types'
export function hasSelector(target: Element, selector: string, node: Ref<Element>): boolean {
let current = target
@@ -32,14 +25,10 @@ export function getDragItems(
.filter((n) => (n.selected || n.id === nodeId) && (!n.parentNode || !isParentSelected(n, getNode)))
.map((n) => ({
id: n.id,
position: n.position,
position: n.computedPosition || { x: 0, y: 0, z: 0 },
distance: {
x: mousePos.x - n.position.x,
y: mousePos.y - n.position.y,
},
delta: {
x: 0,
y: 0,
x: mousePos.x - n.computedPosition?.x || 0,
y: mousePos.y - n.computedPosition?.y || 0,
},
extent: n.extent,
parentNode: n.parentNode,
@@ -50,21 +39,17 @@ export function getDragItems(
export function getEventHandlerParams({
id,
dragItems,
node,
getNode,
}: {
id?: string
dragItems: NodeDragItem[]
node: GraphNode
getNode: ComputedGetters['getNode']
}): [GraphNode, GraphNode[]] {
const extendedDragItems: GraphNode[] = dragItems.map((n) => {
const node = getNode.value(n.id)!
return {
...node,
position: n.position,
computedPosition: {
x: (node.computedPosition?.x || 0) + n.delta.x,
y: (node.computedPosition?.y || 0) + n.delta.y,
z: node.computedPosition.z,
},
}
})
@@ -78,15 +63,18 @@ export function updatePosition(
nodeExtent?: CoordinateExtent,
): NodeDragItem {
let currentExtent = dragItem.extent || nodeExtent
let nextPosition = { x: mousePos.x - dragItem.distance.x, y: mousePos.y - dragItem.distance.y }
const nextPosition = { x: mousePos.x - dragItem.distance.x, y: mousePos.y - dragItem.distance.y }
if (dragItem.extent === 'parent' && parent) {
if (dragItem.parentNode && dragItem.dimensions.width && dragItem.dimensions.height) {
currentExtent =
parent.dimensions.width && parent.dimensions.height
parent.computedPosition && parent.dimensions.width && parent.dimensions.height
? [
[0, 0],
[parent.dimensions.width - dragItem.dimensions.width, parent.dimensions.height - dragItem.dimensions.height],
[parent.computedPosition.x, parent.computedPosition.y],
[
parent.computedPosition.x + parent.dimensions.width - dragItem.dimensions.width,
parent.computedPosition.y + parent.dimensions.height - dragItem.dimensions.height,
],
]
: currentExtent
}
@@ -94,13 +82,7 @@ export function updatePosition(
currentExtent = nodeExtent
}
nextPosition = currentExtent ? clampPosition(nextPosition, currentExtent as CoordinateExtent) : nextPosition
dragItem.delta = {
x: nextPosition.x - dragItem.position.x,
y: nextPosition.y - dragItem.position.y,
}
dragItem.position = nextPosition
dragItem.position = currentExtent ? clampPosition(nextPosition, currentExtent as CoordinateExtent) : nextPosition
return dragItem
}