refactor(core): remove reactivity transform from viewport component

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
braks
2023-05-10 14:04:53 +02:00
committed by Braks
parent 732e0f4951
commit e5591ee1df
@@ -2,7 +2,7 @@
import type { D3ZoomEvent, ZoomTransform } from 'd3-zoom' import type { D3ZoomEvent, ZoomTransform } from 'd3-zoom'
import { zoom, zoomIdentity } from 'd3-zoom' import { zoom, zoomIdentity } from 'd3-zoom'
import { pointer, select } from 'd3-selection' import { pointer, select } from 'd3-selection'
import type { CoordinateExtent, FlowOptions, ViewportTransform } from '../../types' import type { CoordinateExtent, D3ZoomHandler, FlowOptions, ViewportTransform } from '../../types'
import { PanOnScrollMode } from '../../types' import { PanOnScrollMode } from '../../types'
import Pane from '../Pane/Pane.vue' import Pane from '../Pane/Pane.vue'
import Transform from './Transform.vue' import Transform from './Transform.vue'
@@ -27,36 +27,41 @@ const {
preventScrolling, preventScrolling,
noWheelClassName, noWheelClassName,
noPanClassName, noPanClassName,
setState,
emits, emits,
connectionStartHandle, connectionStartHandle,
userSelectionActive, userSelectionActive,
paneDragging, paneDragging,
} = $(useVueFlow()) d3Zoom: storeD3Zoom,
d3Selection: storeD3Selection,
d3ZoomHandler: storeD3ZoomHandler,
viewport,
viewportRef,
} = useVueFlow()
const viewportEl = ref<HTMLDivElement>() const viewportEl = ref<HTMLDivElement>()
let selectionKeyPressed = $ref(false) const selectionKeyPressed = ref(false)
let isZoomingOrPanning = $ref(false) const isZoomingOrPanning = ref(false)
let zoomedWithRightMouseButton = $ref(false) let zoomedWithRightMouseButton = false
let mouseButton = $ref(0) let mouseButton = 0
const panKeyPressed = useKeyPress(panActivationKeyCode) let prevTransform: ViewportTransform = {
const shouldPanOnDrag = computed(() => !selectionKeyPressed && panOnDrag && panKeyPressed.value)
const isSelecting = computed(
() => (selectionKeyCode !== true && selectionKeyPressed) || (selectionKeyCode === true && shouldPanOnDrag.value !== true),
)
let prevTransform = $ref<ViewportTransform>({
x: 0, x: 0,
y: 0, y: 0,
zoom: 0, zoom: 0,
}) }
const panKeyPressed = useKeyPress(panActivationKeyCode)
const shouldPanOnDrag = computed(() => !selectionKeyPressed.value && panOnDrag.value && panKeyPressed.value)
const isSelecting = computed(
() =>
(selectionKeyCode.value !== true && selectionKeyPressed.value) || (selectionKeyCode.value === true && !shouldPanOnDrag.value),
)
onMounted(() => { onMounted(() => {
useResizeObserver(viewportEl, setDimensions) useResizeObserver(viewportEl, setDimensions)
@@ -68,46 +73,44 @@ onMounted(() => {
onMounted(() => { onMounted(() => {
const viewportElement = viewportEl.value! const viewportElement = viewportEl.value!
const bbox = viewportElement.getBoundingClientRect() const bbox = viewportElement.getBoundingClientRect()
const d3Zoom = zoom<HTMLDivElement, any>().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent) const d3Zoom = zoom<HTMLDivElement, any>().scaleExtent([minZoom.value, maxZoom.value]).translateExtent(translateExtent.value)
const d3Selection = select(viewportElement).call(d3Zoom) const d3Selection = select(viewportElement).call(d3Zoom)
const d3ZoomHandler = d3Selection.on('wheel.zoom') const d3ZoomHandler = d3Selection.on('wheel.zoom')
const updatedTransform = zoomIdentity const updatedTransform = zoomIdentity
.translate(defaultViewport.x ?? 0, defaultViewport.y ?? 0) .translate(defaultViewport.value.x ?? 0, defaultViewport.value.y ?? 0)
.scale(clamp(defaultViewport.zoom ?? 1, minZoom, maxZoom)) .scale(clamp(defaultViewport.value.zoom ?? 1, minZoom.value, maxZoom.value))
const extent: CoordinateExtent = [ const extent: CoordinateExtent = [
[0, 0], [0, 0],
[bbox.width, bbox.height], [bbox.width, bbox.height],
] ]
const constrainedTransform = d3Zoom.constrain()(updatedTransform, extent, translateExtent) const constrainedTransform = d3Zoom.constrain()(updatedTransform, extent, translateExtent.value)
d3Zoom.transform(d3Selection, constrainedTransform) d3Zoom.transform(d3Selection, constrainedTransform)
setState({ storeD3Zoom.value = d3Zoom
d3Zoom, storeD3Selection.value = d3Selection
d3Selection, storeD3ZoomHandler.value = d3ZoomHandler as D3ZoomHandler
d3ZoomHandler, viewport.value = { x: constrainedTransform.x, y: constrainedTransform.y, zoom: constrainedTransform.k }
viewport: { x: constrainedTransform.x, y: constrainedTransform.y, zoom: constrainedTransform.k }, viewportRef.value = viewportElement
viewportRef: viewportElement,
})
useKeyPress($$(selectionKeyCode), (keyPress) => { useKeyPress(selectionKeyCode, (keyPress) => {
selectionKeyPressed = keyPress selectionKeyPressed.value = keyPress
}) })
const zoomKeyPressed = useKeyPress(zoomActivationKeyCode) const zoomKeyPressed = useKeyPress(zoomActivationKeyCode)
watchEffect(() => { watchEffect(() => {
if (selectionKeyPressed && userSelectionActive && !isZoomingOrPanning) { if (selectionKeyPressed.value && userSelectionActive.value && !isZoomingOrPanning.value) {
d3Zoom.on('zoom', null) d3Zoom.on('zoom', null)
} else if (!selectionKeyPressed && !userSelectionActive) { } else if (!selectionKeyPressed.value && !userSelectionActive.value) {
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => { d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
setState({ viewport: { x: event.transform.x, y: event.transform.y, zoom: event.transform.k } }) viewport.value = { x: event.transform.x, y: event.transform.y, zoom: event.transform.k }
const flowTransform = eventToFlowTransform(event.transform) const flowTransform = eventToFlowTransform(event.transform)
zoomedWithRightMouseButton = isRightClickPan(panOnDrag, mouseButton ?? 0) zoomedWithRightMouseButton = isRightClickPan(panOnDrag.value, mouseButton ?? 0)
emits.viewportChange(flowTransform) emits.viewportChange(flowTransform)
emits.move({ event, flowTransform }) emits.move({ event, flowTransform })
@@ -123,12 +126,12 @@ onMounted(() => {
// we need to remember it here, because it's always 0 in the "zoom" event // we need to remember it here, because it's always 0 in the "zoom" event
mouseButton = event.sourceEvent.button mouseButton = event.sourceEvent.button
isZoomingOrPanning = true isZoomingOrPanning.value = true
const flowTransform = eventToFlowTransform(event.transform) const flowTransform = eventToFlowTransform(event.transform)
if (event.sourceEvent?.type === 'mousedown') { if (event.sourceEvent?.type === 'mousedown') {
setState({ paneDragging: true }) paneDragging.value = true
} }
prevTransform = flowTransform prevTransform = flowTransform
@@ -142,11 +145,11 @@ onMounted(() => {
return null return null
} }
isZoomingOrPanning = false isZoomingOrPanning.value = false
setState({ paneDragging: false }) paneDragging.value = false
if (isRightClickPan(panOnDrag, mouseButton ?? 0) && !zoomedWithRightMouseButton) { if (isRightClickPan(panOnDrag.value, mouseButton ?? 0) && !zoomedWithRightMouseButton) {
emits.paneContextMenu(event.sourceEvent) emits.paneContextMenu(event.sourceEvent)
} }
@@ -163,11 +166,11 @@ onMounted(() => {
}) })
watchEffect(() => { watchEffect(() => {
if (panOnScroll && !zoomKeyPressed.value && !userSelectionActive) { if (panOnScroll.value && !zoomKeyPressed.value && !userSelectionActive.value) {
d3Selection.on( d3Selection.on(
'wheel.zoom', 'wheel.zoom',
(event: WheelEvent) => { (event: WheelEvent) => {
if (isWrappedWithClass(event, noWheelClassName)) { if (isWrappedWithClass(event, noWheelClassName.value)) {
return false return false
} }
@@ -176,7 +179,7 @@ onMounted(() => {
const currentZoom = d3Selection.property('__zoom').k || 1 const currentZoom = d3Selection.property('__zoom').k || 1
if (event.ctrlKey && zoomOnPinch) { if (event.ctrlKey && zoomOnPinch.value) {
const point = pointer(event) const point = pointer(event)
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js // taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10 const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
@@ -189,10 +192,14 @@ onMounted(() => {
// increase scroll speed in firefox // increase scroll speed in firefox
// firefox: deltaMode === 1; chrome: deltaMode === 0 // firefox: deltaMode === 1; chrome: deltaMode === 0
const deltaNormalize = event.deltaMode === 1 ? 20 : 1 const deltaNormalize = event.deltaMode === 1 ? 20 : 1
const deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize const deltaX = panOnScrollMode.value === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
const deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize const deltaY = panOnScrollMode.value === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
d3Zoom.translateBy(d3Selection, -(deltaX / currentZoom) * panOnScrollSpeed, -(deltaY / currentZoom) * panOnScrollSpeed) d3Zoom.translateBy(
d3Selection,
-(deltaX / currentZoom) * panOnScrollSpeed.value,
-(deltaY / currentZoom) * panOnScrollSpeed.value,
)
}, },
{ passive: false }, { passive: false },
) )
@@ -200,7 +207,7 @@ onMounted(() => {
d3Selection.on( d3Selection.on(
'wheel.zoom', 'wheel.zoom',
function (event: WheelEvent, d) { function (event: WheelEvent, d) {
if (!preventScrolling || isWrappedWithClass(event, noWheelClassName)) { if (!preventScrolling.value || isWrappedWithClass(event, noWheelClassName.value)) {
return null return null
} }
@@ -213,8 +220,8 @@ onMounted(() => {
}) })
d3Zoom.filter((event: MouseEvent) => { d3Zoom.filter((event: MouseEvent) => {
const zoomScroll = zoomKeyPressed.value || zoomOnScroll const zoomScroll = zoomKeyPressed.value || zoomOnScroll.value
const pinchZoom = zoomOnPinch && event.ctrlKey const pinchZoom = zoomOnPinch.value && event.ctrlKey
if ( if (
event.button === 1 && event.button === 1 &&
@@ -225,55 +232,56 @@ onMounted(() => {
} }
// if all interactions are disabled, we prevent all zoom events // if all interactions are disabled, we prevent all zoom events
if (!panOnDrag && !zoomScroll && !panOnScroll && !zoomOnDoubleClick && !zoomOnPinch) { if (!panOnDrag.value && !zoomScroll && !panOnScroll.value && !zoomOnDoubleClick.value && !zoomOnPinch.value) {
return false return false
} }
// during a selection we prevent all other interactions // during a selection we prevent all other interactions
if (userSelectionActive) { if (userSelectionActive.value) {
return false return false
} }
// if zoom on double click is disabled, we prevent the double click event // if zoom on double click is disabled, we prevent the double click event
if (!zoomOnDoubleClick && event.type === 'dblclick') { if (!zoomOnDoubleClick.value && event.type === 'dblclick') {
return false return false
} }
// if the target element is inside an element with the nowheel class, we prevent zooming // if the target element is inside an element with the nowheel class, we prevent zooming
if (isWrappedWithClass(event, noWheelClassName) && event.type === 'wheel') { if (isWrappedWithClass(event, noWheelClassName.value) && event.type === 'wheel') {
return false return false
} }
// if the target element is inside an element with the nopan class, we prevent panning // if the target element is inside an element with the nopan class, we prevent panning
if (isWrappedWithClass(event, noPanClassName) && event.type !== 'wheel') { if (isWrappedWithClass(event, noPanClassName.value) && event.type !== 'wheel') {
return false return false
} }
if (!zoomOnPinch && event.ctrlKey && event.type === 'wheel') { if (!zoomOnPinch.value && event.ctrlKey && event.type === 'wheel') {
return false return false
} }
// when there is no scroll handling enabled, we prevent all wheel events // when there is no scroll handling enabled, we prevent all wheel events
if (!zoomScroll && !panOnScroll && !pinchZoom && event.type === 'wheel') { if (!zoomScroll && !panOnScroll.value && !pinchZoom && event.type === 'wheel') {
return false return false
} }
// if the pane is not movable, we prevent dragging it with mousestart or touchstart // if the pane is not movable, we prevent dragging it with mousestart or touchstart
if (!panOnDrag && (event.type === 'mousedown' || event.type === 'touchstart')) { if (!panOnDrag.value && (event.type === 'mousedown' || event.type === 'touchstart')) {
return false return false
} }
// if the pane is only movable using allowed clicks // if the pane is only movable using allowed clicks
if ( if (
Array.isArray(panOnDrag) && Array.isArray(panOnDrag.value) &&
!panOnDrag.includes(event.button) && !panOnDrag.value.includes(event.button) &&
(event.type === 'mousedown' || event.type === 'touchstart') (event.type === 'mousedown' || event.type === 'touchstart')
) { ) {
return false return false
} }
// We only allow right clicks if pan on drag is set to right-click // We only allow right clicks if pan on drag is set to right-click
const buttonAllowed = (Array.isArray(panOnDrag) && panOnDrag.includes(event.button)) || !event.button || event.button <= 1 const buttonAllowed =
(Array.isArray(panOnDrag.value) && panOnDrag.value.includes(event.button)) || !event.button || event.button <= 1
// default filter for d3-zoom // default filter for d3-zoom
return (!event.ctrlKey || event.type === 'wheel') && buttonAllowed return (!event.ctrlKey || event.type === 'wheel') && buttonAllowed
@@ -311,8 +319,8 @@ function setDimensions() {
emits.error(new VueFlowError(ErrorCode.MISSING_VIEWPORT_DIMENSIONS)) emits.error(new VueFlowError(ErrorCode.MISSING_VIEWPORT_DIMENSIONS))
} }
dimensions.width = width || 500 dimensions.value.width = width || 500
dimensions.height = height || 500 dimensions.value.height = height || 500
} }
function isWrappedWithClass(event: Event, className: string | undefined) { function isWrappedWithClass(event: Event, className: string | undefined) {