@@ -4,9 +4,25 @@ import {
44 on_pointer_down ,
55 on_pointer_up ,
66} from '../logic/index.zig'
7+ import clamp from '../utils/clamp'
78
89const OUTSIDE_CANVAS = - 1
910
11+ enum CameraMode {
12+ Pan ,
13+ Zoom ,
14+ None ,
15+ }
16+
17+ let cameraMode = CameraMode . None
18+ let panCameraStart : Point | null = null
19+
20+ export const camera = {
21+ x : 0 ,
22+ y : 0 ,
23+ zoom : 1 ,
24+ }
25+
1026export const pointer = {
1127 x : 0 ,
1228 y : 0 ,
@@ -20,19 +36,29 @@ export const pointer = {
2036
2137export default function initMouseController (
2238 canvas : HTMLCanvasElement ,
39+ onZoom : VoidFunction ,
2340 onStartProcessing : VoidFunction
2441) {
2542 pointer . x = OUTSIDE_CANVAS
2643 pointer . y = OUTSIDE_CANVAS
2744
45+ function getZigAbsolutePointer ( ) : [ number , number ] {
46+ return [
47+ ( pointer . x - camera . x ) / camera . zoom ,
48+ ( canvas . height - pointer . y - camera . y ) / camera . zoom ,
49+ ]
50+ }
51+
2852 function updatePointer ( e : MouseEvent ) {
2953 const rect = canvas . getBoundingClientRect ( )
30- pointer . x = e . clientX - rect . left
31- pointer . y = e . clientY - rect . top
54+ const scale = canvas . width / rect . width
55+ pointer . x = ( e . clientX - rect . left ) * scale
56+ pointer . y = ( e . clientY - rect . top ) * scale
3257 }
3358
3459 canvas . addEventListener ( 'mouseleave' , ( ) => {
3560 onStartProcessing ( )
61+ canvas . style . cursor = 'default'
3662
3763 const update = ( ) => {
3864 pointer . x = OUTSIDE_CANVAS
@@ -50,11 +76,19 @@ export default function initMouseController(
5076 } )
5177
5278 canvas . addEventListener ( 'mousemove' , ( e ) => {
79+ if ( panCameraStart ) {
80+ updatePointer ( e )
81+
82+ camera . x = pointer . x - panCameraStart . x
83+ camera . y = - ( pointer . y - panCameraStart . y )
84+ return
85+ }
86+
5387 onStartProcessing ( )
5488
5589 const move = ( ) => {
5690 updatePointer ( e )
57- on_pointer_move ( pointer . x , canvas . clientHeight - pointer . y )
91+ on_pointer_move ( ... getZigAbsolutePointer ( ) )
5892 }
5993 if ( pointer . afterPickEventsQueue . length > 0 ) {
6094 pointer . afterPickEventsQueue . push ( {
@@ -67,16 +101,31 @@ export default function initMouseController(
67101 } )
68102
69103 canvas . addEventListener ( 'mousedown' , ( e ) => {
104+ if ( cameraMode === CameraMode . Pan ) {
105+ updatePointer ( e )
106+ panCameraStart = {
107+ x : pointer . x - camera . x ,
108+ y : pointer . y + camera . y ,
109+ }
110+ canvas . style . cursor = 'grabbing'
111+ return
112+ }
113+ panCameraStart = null
114+
70115 onStartProcessing ( )
71116
72117 updatePointer ( e )
73118 pointer . afterPickEventsQueue . push ( {
74119 requireNewPick : true ,
75- cb : on_pointer_down . bind ( null , pointer . x , canvas . clientHeight - pointer . y ) ,
120+ cb : on_pointer_down . bind ( null , ... getZigAbsolutePointer ( ) ) ,
76121 } )
77122 } )
78123
79124 canvas . addEventListener ( 'mouseup' , ( ) => {
125+ cameraMode = CameraMode . None
126+ panCameraStart = null
127+ canvas . style . cursor = 'default'
128+
80129 onStartProcessing ( )
81130
82131 if ( pointer . afterPickEventsQueue . length > 0 ) {
@@ -89,7 +138,66 @@ export default function initMouseController(
89138 }
90139 } )
91140
141+ /* panning , supports both scroll and touch, expect Safari */
92142 canvas . addEventListener ( 'wheel' , ( event ) => {
93- console . log ( event . deltaY )
143+ event . preventDefault ( )
144+ if ( cameraMode === CameraMode . Zoom ) {
145+ const oldZoom = camera . zoom
146+ camera . zoom = clamp ( camera . zoom - event . deltaY * 0.005 , 0.1 , 20 )
147+ onZoom ( )
148+
149+ const zoomFactor = camera . zoom / oldZoom
150+
151+ camera . x = pointer . x - ( pointer . x - camera . x ) * zoomFactor
152+ const realY = canvas . height - pointer . y
153+ camera . y = realY - ( realY - camera . y ) * zoomFactor
154+ } else {
155+ camera . x -= event . deltaX
156+ camera . y += event . deltaY
157+ }
158+ } )
159+ // pointer.zoom = clamp(pointer.zoom + event.deltaY * 0.01, 0.1, 100)
160+
161+ document . body . addEventListener ( 'keydown' , ( event ) => {
162+ if ( event . code === 'Space' ) {
163+ event . preventDefault ( )
164+ if ( cameraMode !== CameraMode . Pan ) {
165+ canvas . style . cursor = 'grab'
166+ cameraMode = CameraMode . Pan
167+ }
168+ } else if ( event . key === 'Alt' ) {
169+ event . preventDefault ( )
170+ cameraMode = CameraMode . Zoom
171+ }
172+ } )
173+ document . body . addEventListener ( 'keyup' , ( event ) => {
174+ if ( event . code === 'Space' || event . key === 'Alt' ) {
175+ cameraMode = CameraMode . None
176+ }
177+ if ( event . code === 'Space' && panCameraStart === null ) {
178+ canvas . style . cursor = 'default'
179+ }
180+ } )
181+
182+ let lastTouchY : number
183+
184+ canvas . addEventListener ( 'touchstart' , ( event ) => {
185+ if ( event . touches . length === 2 ) {
186+ event . preventDefault ( )
187+
188+ lastTouchY = event . touches [ 0 ] . clientY
189+ }
190+ } )
191+
192+ canvas . addEventListener ( 'touchmove' , ( event ) => {
193+ if ( event . touches . length === 2 ) {
194+ event . preventDefault ( )
195+
196+ const delta = lastTouchY - event . touches [ 0 ] . clientY
197+ lastTouchY = event . touches [ 0 ] . clientY
198+
199+ camera . zoom = clamp ( camera . zoom - delta * 0.01 , 0.1 , 20 )
200+ onZoom ( )
201+ }
94202 } )
95203}
0 commit comments