view.js (2.5 KB)


 1 let dpr = window.devicePixelRatio || 1;
 2 let cssW = 0, cssH = 0;
 3 
 4 const ZOOM_STOPS = [2, 3, 4, 5, 6, 8, 11, 15, 21, 30];
 5 const MIN_ZOOM = ZOOM_STOPS[0];
 6 const ZOOM_SENSITIVITY = 3.5;	// fractional levels per unit log(pinch factor)
 7 let zoomIdx = 0;
 8 let zoom = MIN_ZOOM;
 9 
10 let camX = 0, camY = 0;
11 
12 let curX = 0, curY = 0;
13 // used to keep the brush pinned under the real cursor while panning, since
14 // the OS does not emit pointermove events when only the camera moves.
15 let curClientX = null, curClientY = null;
16 let mouseInside = false;
17 let cursorAlpha = 0;
18 let cursorFadeFrom = 0;
19 let cursorFadeTo = 0;
20 let cursorFadeStart = 0;
21 const CURSOR_FADE_IN_MS = 100;
22 const CURSOR_FADE_OUT_MS = 400;
23 function setCursorFadeTarget(target) {
24 	if (target === cursorFadeTo) return;
25 	cursorFadeFrom = cursorAlpha;
26 	cursorFadeTo = target;
27 	cursorFadeStart = performance.now();
28 	requestDraw();
29 }
30 function showCursor() {
31 	mouseInside = true;
32 	setCursorFadeTarget(1);
33 }
34 function hideCursorNow() {
35 	mouseInside = false;
36 	cursorAlpha = 0;
37 	cursorFadeFrom = 0;
38 	cursorFadeTo = 0;
39 }
40 
41 function resize() {
42 	const firstResize = cssW === 0;
43 	dpr = window.devicePixelRatio || 1;
44 	cssW = window.innerWidth;
45 	cssH = window.innerHeight;
46 	view.style.width = cssW + 'px';
47 	view.style.height = cssH + 'px';
48 	view.width = Math.floor(cssW * dpr);
49 	view.height = Math.floor(cssH * dpr);
50 	if (firstResize) {
51 		curX = Math.floor(cssW / (2 * zoom));
52 		curY = Math.floor(cssH / (2 * zoom));
53 	}
54 	requestDraw();
55 }
56 
57 function clientToWorld(clientX, clientY) {
58 	const lx = Math.floor(clientX / zoom + camX);
59 	const ly = Math.floor(clientY / zoom + camY);
60 	return { x: lx, y: ly };
61 }
62 
63 function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
64 
65 function applyZoom(factor, ax, ay) {
66 	const worldAtAnchorX = camX + ax / zoom;
67 	const worldAtAnchorY = camY + ay / zoom;
68 	zoomIdx = clamp(zoomIdx + Math.log(factor) * ZOOM_SENSITIVITY, 0, ZOOM_STOPS.length - 1);
69 	const newZoom = ZOOM_STOPS[Math.round(zoomIdx)];
70 	if (newZoom !== zoom) {
71 		zoom = newZoom;
72 		camX = worldAtAnchorX - ax / zoom;
73 		camY = worldAtAnchorY - ay / zoom;
74 	}
75 }
76 
77 function zoomTo(newZoom) {
78 	if (newZoom === zoom) return;
79 	const ax = cssW / 2, ay = cssH / 2;
80 	const worldAtAnchorX = camX + ax / zoom;
81 	const worldAtAnchorY = camY + ay / zoom;
82 	zoom = newZoom;
83 	zoomIdx = ZOOM_STOPS.indexOf(zoom);
84 	camX = worldAtAnchorX - ax / zoom;
85 	camY = worldAtAnchorY - ay / zoom;
86 	requestDraw();
87 }
88 
89 function stepZoom(dir) {
90 	const i = ZOOM_STOPS.indexOf(zoom);
91 	zoomTo(ZOOM_STOPS[clamp(i + dir, 0, ZOOM_STOPS.length - 1)]);
92 }
93 function resetZoom() { zoomTo(MIN_ZOOM); }