@@ -165,6 +165,11 @@ <h3>Closest point on the surface</h3>
165165 this . canvas . addEventListener ( "mousemove" , this . mouseMove . bind ( this ) , false ) ;
166166 this . canvas . addEventListener ( "mouseup" , this . mouseUp . bind ( this ) , false ) ;
167167 this . canvas . addEventListener ( "wheel" , this . wheel . bind ( this ) , false ) ;
168+ // register touch event listeners (mobile / tablet)
169+ // passive:false is required so we can call preventDefault() to suppress scrolling
170+ this . canvas . addEventListener ( "touchstart" , this . touchStart . bind ( this ) , { passive : false } ) ;
171+ this . canvas . addEventListener ( "touchmove" , this . touchMove . bind ( this ) , { passive : false } ) ;
172+ this . canvas . addEventListener ( "touchend" , this . touchEnd . bind ( this ) , { passive : false } ) ;
168173 }
169174
170175 // update GUI
@@ -347,7 +352,61 @@ <h3>Closest point on the surface</h3>
347352 this . zoom += event . deltaY * - 0.05 ;
348353 if ( this . zoom < 1 )
349354 this . zoom = 1 ;
350- this . requestID = window . requestAnimationFrame ( this . mainLoop . bind ( this ) ) ;
355+ }
356+
357+ // Convert the first touch point to a plain {clientX, clientY} object
358+ // so it can be passed directly to the existing mouse handlers.
359+ getTouchClient ( event )
360+ {
361+ const t = event . touches . length > 0 ? event . touches [ 0 ] : event . changedTouches [ 0 ] ;
362+ return { clientX : t . clientX , clientY : t . clientY } ;
363+ }
364+
365+ touchStart ( event )
366+ {
367+ event . preventDefault ( ) ;
368+ if ( event . touches . length === 1 )
369+ this . mouseDown ( { which : 1 , ...this . getTouchClient ( event ) } ) ;
370+ else if ( event . touches . length === 2 )
371+ this . lastPinchDist = this . getPinchDist ( event ) ;
372+ }
373+
374+ touchMove ( event )
375+ {
376+ event . preventDefault ( ) ;
377+ if ( event . touches . length === 1 )
378+ {
379+ this . lastPinchDist = null ;
380+ this . mouseMove ( this . getTouchClient ( event ) ) ;
381+ }
382+ else if ( event . touches . length === 2 )
383+ {
384+ // deselect any dragged particle while pinching
385+ this . selectedParticle = - 1 ;
386+ const dist = this . getPinchDist ( event ) ;
387+ if ( this . lastPinchDist !== null )
388+ {
389+ this . zoom += ( dist - this . lastPinchDist ) * 0.3 ;
390+ if ( this . zoom < 1 ) this . zoom = 1 ;
391+ }
392+ this . lastPinchDist = dist ;
393+ }
394+ }
395+
396+ touchEnd ( event )
397+ {
398+ event . preventDefault ( ) ;
399+ if ( event . touches . length < 2 )
400+ this . lastPinchDist = null ;
401+ if ( event . touches . length === 0 )
402+ this . mouseUp ( event ) ;
403+ }
404+
405+ getPinchDist ( event )
406+ {
407+ const dx = event . touches [ 0 ] . clientX - event . touches [ 1 ] . clientX ;
408+ const dy = event . touches [ 0 ] . clientY - event . touches [ 1 ] . clientY ;
409+ return Math . sqrt ( dx * dx + dy * dy ) ;
351410 }
352411 }
353412
0 commit comments