diff --git a/src/components/svg-flow/SvgFlowCanvas.tsx b/src/components/svg-flow/SvgFlowCanvas.tsx index 3c9054f..1477830 100644 --- a/src/components/svg-flow/SvgFlowCanvas.tsx +++ b/src/components/svg-flow/SvgFlowCanvas.tsx @@ -1,4 +1,4 @@ -import { type Component, createSignal, onMount, onCleanup } from 'solid-js'; +import { type Component, createSignal, onMount, onCleanup, Show } from 'solid-js'; import SvgFlowCanvasDefs from './SvgFlowCanvasDefs'; import { useSvgFlowContext } from '../../context/SvgFlowContext'; @@ -20,7 +20,9 @@ const SvgFlowCanvas: Component<{ if (props.data) setSvgFlow('data', props.data); if (props.config) setSvgFlow('config', prev => ({ ...prev, ...props.config })); + let svgRef: SVGSVGElement | undefined; let containerRef: HTMLDivElement | undefined; + const containerResizeObserver = new ResizeObserver(() => setSvgSize()); const setSvgSize = () => { if (containerRef) { @@ -39,11 +41,10 @@ const SvgFlowCanvas: Component<{ if (svgFlow.config.resizeOnInit && svgFlow.data.nodes.length > 1) { zoomToFit(); } + svgRef?.addEventListener('touchmove', onTouchMove, { passive: false }); }); onCleanup(() => document.documentElement.style.overscrollBehavior = initialOverscroll()); - - const viewBox = () => { const { x, y } = svgFlow.canvas; const width = svgFlow.state.width / svgFlow.canvas.zoom; @@ -121,30 +122,55 @@ const SvgFlowCanvas: Component<{ } }; - let lastTouches: TouchList | undefined = undefined; + let prevTouches: TouchList | undefined = undefined; + const touchCenter = (touches: Touch[]) => { + if (svgRef) { + const svgRect = svgRef.getBoundingClientRect(); + const sumX = touches.map(touch => touch.pageX).reduce((a, b) => a + b, 0); + const sumY = touches.map(touch => touch.pageY).reduce((a, b) => a + b, 0); + return { + x: (sumX / touches.length - svgRect.x) / svgRect.width, + y: (sumY / touches.length - svgRect.y) / svgRect.height, + }; + } + return { x: 0.5, y: 0.5 }; + } + const onTouchEnd = () => prevTouches = undefined; const onTouchMove = (event: TouchEvent) => { - if (lastTouches) { + event.preventDefault(); + if (prevTouches?.length === event.touches.length) { if (event.touches.length === 1) { - const deltaX = event.touches[0].pageX - lastTouches[0].pageX; - const deltaY = event.touches[0].pageY - lastTouches[0].pageY; - setSvgFlow('canvas', 'x', prev => prev + deltaX / svgFlow.canvas.zoom); - setSvgFlow('canvas', 'y', prev => prev + deltaY / svgFlow.canvas.zoom); + const deltaX = event.touches[0].pageX - prevTouches[0].pageX; + const deltaY = event.touches[0].pageY - prevTouches[0].pageY; + setSvgFlow('canvas', 'x', prev => prev - deltaX / svgFlow.canvas.zoom); + setSvgFlow('canvas', 'y', prev => prev - deltaY / svgFlow.canvas.zoom); + } + if (event.touches.length === 2) { + const prevDist = Math.sqrt( + (prevTouches[0].pageX - prevTouches[1].pageX) ** 2 + + (prevTouches[0].pageY - prevTouches[1].pageY) ** 2 + ); + const dist = Math.sqrt( + (event.touches[0].pageX - event.touches[1].pageX) ** 2 + + (event.touches[0].pageY - event.touches[1].pageY) ** 2 + ); + const center = touchCenter([...event.touches]); + zoom(dist / prevDist, center.x, center.y); } } - lastTouches = event.touches; + prevTouches = event.touches; } - const onTouchEnd = () => lastTouches = undefined; return (
@@ -152,7 +178,9 @@ const SvgFlowCanvas: Component<{ - { false && } + + +
); }; diff --git a/src/context/SvgFlowContext.tsx b/src/context/SvgFlowContext.tsx index 187b346..d5af308 100644 --- a/src/context/SvgFlowContext.tsx +++ b/src/context/SvgFlowContext.tsx @@ -12,6 +12,7 @@ export const DefaultSvgFlowConfig: SvgFlow['config'] = { autoNodeHeight: true, resizeOnInit: true, showControls: true, + showDebug: false, width: '800px', height: '500px', }; diff --git a/src/models/canvas.ts b/src/models/canvas.ts index 7705f99..31bed91 100644 --- a/src/models/canvas.ts +++ b/src/models/canvas.ts @@ -48,6 +48,7 @@ export interface SvgFlowConfig { stroke?: string; strokeWidth?: number; showControls?: boolean; + showDebug?: boolean; width?: string; height?: string; }