@@ -6,11 +6,21 @@ export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width:
66 const scale = ref ( 1 ) ;
77 const isPanning = ref ( false ) ;
88 const startPoint = ref ( { x : 0 , y : 0 } ) ;
9+ const pinchStartDist = ref ( 0 ) ;
10+ const pinchStartViewBox = ref ( null ) ;
11+ const isPinching = ref ( false ) ;
912
1013 let velocity = { x : 0 , y : 0 } ;
1114 let animationFrame = null ;
1215 let zoomAnimationFrame = null ;
1316
17+ function getTouchDistance ( touches ) {
18+ if ( touches . length < 2 ) return 0 ;
19+ const dx = touches [ 0 ] . clientX - touches [ 1 ] . clientX ;
20+ const dy = touches [ 0 ] . clientY - touches [ 1 ] . clientY ;
21+ return Math . sqrt ( dx * dx + dy * dy ) ;
22+ }
23+
1424 function toSvgPoint ( event ) {
1525 const svg = svgRef . value ;
1626 if ( ! svg ) return { x : 0 , y : 0 } ;
@@ -119,6 +129,44 @@ export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width:
119129 scale . value = newScale ;
120130 } ;
121131
132+ function handleTouchStart ( event ) {
133+ if ( event . touches . length === 2 ) {
134+ isPinching . value = true ;
135+ pinchStartDist . value = getTouchDistance ( event . touches ) ;
136+ pinchStartViewBox . value = { ...viewBox . value } ;
137+ } else {
138+ event . preventDefault ( ) ;
139+ startPan ( event ) ;
140+ }
141+ }
142+
143+ function handleTouchMove ( event ) {
144+ if ( isPinching . value && event . touches . length === 2 ) {
145+ event . preventDefault ( ) ;
146+ const dist = getTouchDistance ( event . touches ) ;
147+ if ( pinchStartDist . value ) {
148+ const zoomFactor = dist / pinchStartDist . value ;
149+ const svg = svgRef . value ;
150+ const rect = svg . getBoundingClientRect ( ) ;
151+ const midX = ( event . touches [ 0 ] . clientX + event . touches [ 1 ] . clientX ) / 2 - rect . left ;
152+ const midY = ( event . touches [ 0 ] . clientY + event . touches [ 1 ] . clientY ) / 2 - rect . top ;
153+ const midPoint = toSvgPoint ( { clientX : midX + rect . left , clientY : midY + rect . top } ) ;
154+ viewBox . value = { ...pinchStartViewBox . value } ;
155+ applyZoom ( zoomFactor , midPoint ) ;
156+ }
157+ } else {
158+ event . preventDefault ( ) ;
159+ doPan ( event ) ;
160+ }
161+ }
162+
163+ function handleTouchEnd ( event ) {
164+ if ( event . touches . length < 2 ) {
165+ isPinching . value = false ;
166+ }
167+ endPan ( ) ;
168+ }
169+
122170 onMounted ( addEventListeners ) ;
123171 onUnmounted ( removeEventListeners ) ;
124172
@@ -132,15 +180,10 @@ export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width:
132180 svg . addEventListener ( 'mouseleave' , endPan ) ;
133181 svg . addEventListener ( 'wheel' , zoom , { passive : false } ) ;
134182 svg . addEventListener ( 'dblclick' , doubleClickZoom ) ;
135- svg . addEventListener ( 'touchstart' , ( event ) => {
136- event . preventDefault ( ) ;
137- startPan ( event ) ;
138- } , { passive : false } ) ;
139- svg . addEventListener ( 'touchmove' , ( event ) => {
140- event . preventDefault ( ) ;
141- doPan ( event ) ;
142- } , { passive : false } ) ;
143- svg . addEventListener ( 'touchend' , endPan ) ;
183+ svg . addEventListener ( 'touchstart' , handleTouchStart , { passive : false } ) ;
184+ svg . addEventListener ( 'touchmove' , handleTouchMove , { passive : false } ) ;
185+ svg . addEventListener ( 'touchend' , handleTouchEnd ) ;
186+ svg . addEventListener ( 'touchcancel' , handleTouchEnd ) ;
144187 }
145188
146189 function removeEventListeners ( ) {
@@ -152,9 +195,10 @@ export default function usePanZoom(svgRef, initialViewBox = { x: 0, y: 0, width:
152195 svg . removeEventListener ( 'mouseleave' , endPan ) ;
153196 svg . removeEventListener ( 'wheel' , zoom ) ;
154197 svg . removeEventListener ( 'dblclick' , doubleClickZoom ) ;
155- svg . removeEventListener ( 'touchstart' , startPan ) ;
156- svg . removeEventListener ( 'touchmove' , doPan ) ;
157- svg . removeEventListener ( 'touchend' , endPan ) ;
198+ svg . removeEventListener ( 'touchstart' , handleTouchStart ) ;
199+ svg . removeEventListener ( 'touchmove' , handleTouchMove ) ;
200+ svg . removeEventListener ( 'touchend' , handleTouchEnd ) ;
201+ svg . removeEventListener ( 'touchcancel' , handleTouchEnd ) ;
158202 }
159203
160204 watchEffect ( ( ) => {
0 commit comments