fix: zoom pan helper not being initialized timely
This commit is contained in:
@@ -28,7 +28,10 @@ const rfInstance = ref<OnLoadParams | null>(null)
|
|||||||
const onElementsRemove = (elementsToRemove: Elements) =>
|
const onElementsRemove = (elementsToRemove: Elements) =>
|
||||||
(elements.value = removeElements(elementsToRemove, elements.value as Elements))
|
(elements.value = removeElements(elementsToRemove, elements.value as Elements))
|
||||||
const onConnect = (params: Edge | Connection) => (elements.value = addEdge(params, elements.value as Elements))
|
const onConnect = (params: Edge | Connection) => (elements.value = addEdge(params, elements.value as Elements))
|
||||||
const onLoad = (revueFlowInstance: OnLoadParams) => (rfInstance.value = revueFlowInstance)
|
const onLoad = (revueFlowInstance: OnLoadParams) => {
|
||||||
|
revueFlowInstance.fitView({ padding: 0.1 })
|
||||||
|
rfInstance.value = revueFlowInstance
|
||||||
|
}
|
||||||
|
|
||||||
const updatePos = () => {
|
const updatePos = () => {
|
||||||
elements.value = elements.value.map((el: FlowElement) => {
|
elements.value = elements.value.map((el: FlowElement) => {
|
||||||
|
|||||||
@@ -14,10 +14,6 @@ export interface ControlProps extends HTMLAttributes {
|
|||||||
showFitView?: boolean
|
showFitView?: boolean
|
||||||
showInteractive?: boolean
|
showInteractive?: boolean
|
||||||
fitViewParams?: FitViewParams
|
fitViewParams?: FitViewParams
|
||||||
onZoomIn?: () => void
|
|
||||||
onZoomOut?: () => void
|
|
||||||
onFitView?: () => void
|
|
||||||
onInteractiveChange?: (interactiveStatus: boolean) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ControlProps>(), {
|
const props = withDefaults(defineProps<ControlProps>(), {
|
||||||
@@ -25,6 +21,8 @@ const props = withDefaults(defineProps<ControlProps>(), {
|
|||||||
showFitView: true,
|
showFitView: true,
|
||||||
showInteractive: true,
|
showInteractive: true,
|
||||||
})
|
})
|
||||||
|
// todo define types for events
|
||||||
|
const emit = defineEmits(['zoom-in', 'zoom-out', 'fit-view', 'interaction-change'])
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const { zoomIn, zoomOut, fitView } = useZoomPanHelper()
|
const { zoomIn, zoomOut, fitView } = useZoomPanHelper()
|
||||||
@@ -34,22 +32,22 @@ const mapClasses = ['vue-flow__controls']
|
|||||||
|
|
||||||
const onZoomInHandler = () => {
|
const onZoomInHandler = () => {
|
||||||
zoomIn?.()
|
zoomIn?.()
|
||||||
props.onZoomIn?.()
|
emit('zoom-in')
|
||||||
}
|
}
|
||||||
|
|
||||||
const onZoomOutHandler = () => {
|
const onZoomOutHandler = () => {
|
||||||
zoomOut?.()
|
zoomOut?.()
|
||||||
props.onZoomOut?.()
|
emit('zoom-out')
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFitViewHandler = () => {
|
const onFitViewHandler = () => {
|
||||||
fitView?.(props.fitViewParams)
|
fitView?.(props.fitViewParams)
|
||||||
props.onFitView?.()
|
emit('fit-view')
|
||||||
}
|
}
|
||||||
|
|
||||||
const onInteractiveChangeHandler = () => {
|
const onInteractiveChangeHandler = () => {
|
||||||
store.setInteractive?.(!isInteractive.value)
|
store.setInteractive?.(!isInteractive.value)
|
||||||
props.onInteractiveChange?.(!isInteractive.value)
|
emit('interaction-change', !isInteractive.value)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
+10
-16
@@ -2,11 +2,9 @@ import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
|
|||||||
import { pointer, select } from 'd3-selection'
|
import { pointer, select } from 'd3-selection'
|
||||||
import { Ref } from 'vue'
|
import { Ref } from 'vue'
|
||||||
import { get } from '@vueuse/core'
|
import { get } from '@vueuse/core'
|
||||||
import { FlowTransform, PanOnScrollMode, Transform, UseZoom, UseZoomOptions } from '~/types'
|
import { FlowTransform, PanOnScrollMode, UseZoom, UseZoomOptions } from '~/types'
|
||||||
import { clamp } from '~/utils'
|
import { clamp } from '~/utils'
|
||||||
import useKeyPress from '~/composables/useKeyPress'
|
import { useKeyPress, useHooks, useStore } from '~/composables'
|
||||||
import useHooks from '~/composables/useHooks'
|
|
||||||
import useStore from '~/composables/useStore'
|
|
||||||
|
|
||||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
||||||
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k
|
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k
|
||||||
@@ -38,12 +36,9 @@ export default (el: Ref<HTMLDivElement>, options: UseZoomOptions): UseZoom => {
|
|||||||
const clampedX = clamp(defaultPosition[0], store.translateExtent[0][0], store.translateExtent[1][0])
|
const clampedX = clamp(defaultPosition[0], store.translateExtent[0][0], store.translateExtent[1][0])
|
||||||
const clampedY = clamp(defaultPosition[1], store.translateExtent[0][1], store.translateExtent[1][1])
|
const clampedY = clamp(defaultPosition[1], store.translateExtent[0][1], store.translateExtent[1][1])
|
||||||
const clampedZoom = clamp(defaultZoom, store.minZoom, store.maxZoom)
|
const clampedZoom = clamp(defaultZoom, store.minZoom, store.maxZoom)
|
||||||
const transform = controlledRef<Transform>([clampedX, clampedY, clampedZoom], {
|
const transform = ref({ x: clampedX, y: clampedY, zoom: clampedZoom })
|
||||||
onBeforeChange(val, oldVal) {
|
biSyncRef(transform, prevTransform)
|
||||||
if (val === oldVal) return false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
store.transform = transform.value
|
|
||||||
const d3Zoom = ref(
|
const d3Zoom = ref(
|
||||||
zoom<HTMLDivElement, any>().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(store.translateExtent),
|
zoom<HTMLDivElement, any>().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(store.translateExtent),
|
||||||
)
|
)
|
||||||
@@ -60,6 +55,7 @@ export default (el: Ref<HTMLDivElement>, options: UseZoomOptions): UseZoom => {
|
|||||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
||||||
d3z.transform(d3s, updatedTransform)
|
d3z.transform(d3s, updatedTransform)
|
||||||
store.initD3Zoom({ d3Zoom: d3z, d3Selection: d3s, d3ZoomHandler })
|
store.initD3Zoom({ d3Zoom: d3z, d3Selection: d3s, d3ZoomHandler })
|
||||||
|
store.transform = [updatedTransform.x, updatedTransform.y, updatedTransform.k]
|
||||||
|
|
||||||
const applyZoomHandlers = () => {
|
const applyZoomHandlers = () => {
|
||||||
d3z.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3z.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
@@ -85,12 +81,10 @@ export default (el: Ref<HTMLDivElement>, options: UseZoomOptions): UseZoom => {
|
|||||||
d3z.on('zoom', null)
|
d3z.on('zoom', null)
|
||||||
} else {
|
} else {
|
||||||
d3z.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3z.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
transform.value = [
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
clamp(event.transform.x, store.translateExtent[0][0], store.translateExtent[1][0]),
|
prevTransform.value = flowTransform
|
||||||
clamp(event.transform.y, store.translateExtent[0][1], store.translateExtent[1][1]),
|
|
||||||
clamp(event.transform.k, store.minZoom, store.maxZoom),
|
hooks.move.trigger(flowTransform)
|
||||||
]
|
|
||||||
hooks.move.trigger(eventToFlowTransform(event.transform))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import useStore from '~/composables/useStore'
|
|||||||
|
|
||||||
const DEFAULT_PADDING = 0.1
|
const DEFAULT_PADDING = 0.1
|
||||||
|
|
||||||
export default (): UseZoomPanHelper => {
|
export default (store = useStore()): UseZoomPanHelper => {
|
||||||
const store = useStore()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
zoomIn: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1.2),
|
zoomIn: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1.2),
|
||||||
zoomOut: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1 / 1.2),
|
zoomOut: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1 / 1.2),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
import { KeyCode, PanOnScrollMode } from '~/types'
|
import { KeyCode, PanOnScrollMode } from '~/types'
|
||||||
import { useHooks, useStore, useZoom, useZoomPanHelper } from '~/composables'
|
import { useHooks, useStore, useZoom, useZoomPanHelper } from '~/composables'
|
||||||
import { onLoadGetElements, onLoadProject, onLoadToObject } from '~/utils/graph'
|
import { onLoadGetElements, onLoadProject, onLoadToObject } from '~/utils/graph'
|
||||||
@@ -34,27 +35,38 @@ const store = useStore()
|
|||||||
const hooks = useHooks()
|
const hooks = useHooks()
|
||||||
|
|
||||||
const zoomPaneEl = templateRef<HTMLDivElement>('zoom-pane', null)
|
const zoomPaneEl = templateRef<HTMLDivElement>('zoom-pane', null)
|
||||||
const { transform } = useZoom(zoomPaneEl, props)
|
|
||||||
const { width, height } = useElementBounding(zoomPaneEl)
|
|
||||||
watch(width, (val) => (store.dimensions.width = val))
|
|
||||||
watch(height, (val) => (store.dimensions.height = val))
|
|
||||||
watch(transform, (val) => (store.transform = val))
|
|
||||||
|
|
||||||
store.transform = transform.value
|
const { width, height } = useElementBounding(zoomPaneEl)
|
||||||
store.dimensions = {
|
store.dimensions = {
|
||||||
width: width.value,
|
width: width.value,
|
||||||
height: height.value,
|
height: height.value,
|
||||||
}
|
}
|
||||||
const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper()
|
|
||||||
hooks.load.trigger({
|
const { transform } = useZoom(zoomPaneEl, props)
|
||||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
store.transform = [transform.value.x, transform.value.y, transform.value.zoom]
|
||||||
zoomIn,
|
|
||||||
zoomOut,
|
watch(width, (val) => (store.dimensions.width = val))
|
||||||
zoomTo,
|
watch(height, (val) => (store.dimensions.height = val))
|
||||||
setTransform,
|
watch(transform, (val) => (store.transform = [val.x, val.y, val.zoom]))
|
||||||
project: onLoadProject(store),
|
|
||||||
getElements: onLoadGetElements(store),
|
onMounted(() => {
|
||||||
toObject: onLoadToObject(store),
|
const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper(store)
|
||||||
|
|
||||||
|
watchOnce(
|
||||||
|
() => width.value && height.value,
|
||||||
|
() => {
|
||||||
|
hooks.load.trigger({
|
||||||
|
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||||
|
zoomIn,
|
||||||
|
zoomOut,
|
||||||
|
zoomTo,
|
||||||
|
setTransform,
|
||||||
|
project: onLoadProject(store),
|
||||||
|
getElements: onLoadGetElements(store),
|
||||||
|
toObject: onLoadToObject(store),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ export default function useFlowStore(preloadedState: FlowState): StoreDefinition
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
addSelectedElements(elements) {
|
addSelectedElements(elements) {
|
||||||
console.log(elements)
|
|
||||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements]
|
const selectedElementsArr = Array.isArray(elements) ? elements : [elements]
|
||||||
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements)
|
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements)
|
||||||
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements
|
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Ref } from 'vue'
|
import { Ref } from 'vue'
|
||||||
import { ElementId, Transform } from './types'
|
import { ElementId, FlowTransform } from './types'
|
||||||
import { D3Zoom, D3Selection, PanOnScrollMode, KeyCode } from './panel'
|
import { D3Zoom, D3Selection, PanOnScrollMode, KeyCode } from './panel'
|
||||||
import { Connection } from '@/src'
|
import { Connection } from '@/src'
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export interface UseZoomOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UseZoom {
|
export interface UseZoom {
|
||||||
transform: Ref<Transform>
|
transform: Ref<FlowTransform>
|
||||||
d3Zoom: Ref<D3Zoom>
|
d3Zoom: Ref<D3Zoom>
|
||||||
d3Selection: Ref<D3Selection>
|
d3Selection: Ref<D3Selection>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user