Skip to content

Commit b094683

Browse files
committed
created new viewer event file
1 parent e938d0e commit b094683

File tree

2 files changed

+398
-319
lines changed

2 files changed

+398
-319
lines changed

src/viewer-events.ts

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
import constant from "./const";
2+
import { center, distance } from "./math-util";
3+
import Minimap from "./minimap";
4+
import Table from "./table";
5+
import Callbacks from "./types/callbacks";
6+
import CommonEventListener from "./types/common-event-listener";
7+
import ViewBoxVals from "./types/view-box-vals";
8+
import { isSafari } from "./util";
9+
import Viewer from "./viewer";
10+
11+
interface GestureEvent extends MouseEvent {
12+
scale: number;
13+
}
14+
15+
class ViewerEvents {
16+
private disbleScrollEvent = false;
17+
private zoomResolve?: () => void;
18+
private gestureStart = false;
19+
private safariScale!: number;
20+
private panXResolver?: () => void;
21+
private panYResolver?: () => void;
22+
23+
constructor(
24+
private svgContainer: HTMLElement,
25+
private viewBoxVals: ViewBoxVals,
26+
private viewer: Viewer,
27+
private minimap: Minimap,
28+
private svgElem: SVGGraphicsElement,
29+
private mainElem: ShadowRoot,
30+
private container: HTMLElement,
31+
private tables: Table[],
32+
private callbacks: Callbacks,
33+
private setZoom: (
34+
zoom: number,
35+
targetX: number,
36+
targetY: number
37+
) => Promise<void>,
38+
private onTableMove: (
39+
table: Table,
40+
deltaX: number,
41+
deltaY: number,
42+
cordinatesChanged: boolean
43+
) => void
44+
) {
45+
this.setUpEvents();
46+
}
47+
48+
setPanXResolver(panXResolver: () => void): void {
49+
this.panXResolver = panXResolver;
50+
}
51+
52+
setPanYResolver(panYResolver: () => void): void {
53+
this.panYResolver = panYResolver;
54+
}
55+
56+
setZoomResolver(zoomResolve: () => void): void {
57+
this.zoomResolve = zoomResolve;
58+
}
59+
60+
getGestureStart(): boolean {
61+
return this.gestureStart;
62+
}
63+
64+
private static noneTableAndMinmapEvent(event: Event): boolean {
65+
return !event.composedPath().some((item) => {
66+
const htmlElement = item as HTMLElement;
67+
return (
68+
htmlElement.id === "minimap-container" ||
69+
htmlElement.classList?.contains("table")
70+
);
71+
});
72+
}
73+
74+
private onScroll = () => {
75+
if (!this.disbleScrollEvent) {
76+
const zoom = this.viewer.getZoom();
77+
this.viewBoxVals.x = this.svgContainer.scrollLeft / zoom;
78+
this.viewBoxVals.y = this.svgContainer.scrollTop / zoom;
79+
this.minimap.setMinimapViewPoint(this.viewBoxVals);
80+
if (this.panXResolver) {
81+
this.panXResolver();
82+
delete this.panXResolver;
83+
}
84+
if (this.panYResolver) {
85+
this.panYResolver();
86+
delete this.panYResolver;
87+
}
88+
}
89+
if (this.zoomResolve) {
90+
this.zoomResolve();
91+
delete this.zoomResolve;
92+
}
93+
this.disbleScrollEvent = false;
94+
};
95+
96+
private onWheel = (event: WheelEvent) => {
97+
if (event.ctrlKey) {
98+
const clientRect = this.svgContainer.getBoundingClientRect();
99+
const targetX = event.clientX - clientRect.left;
100+
const targetY = event.clientY - clientRect.top;
101+
this.setZoom(
102+
this.viewer.getZoom() -
103+
event.deltaY * constant.SCROLL_TO_ZOOM_MULTIPLIER,
104+
targetX,
105+
targetY
106+
);
107+
event.preventDefault();
108+
}
109+
};
110+
111+
private onTouchMove = (event: Event) => {
112+
// Don't move viewport when table is being moved
113+
if (!ViewerEvents.noneTableAndMinmapEvent(event)) event.preventDefault();
114+
};
115+
116+
private onGesturestart = (event: GestureEvent) => {
117+
this.gestureStart = true;
118+
if (event.scale != null) {
119+
this.safariScale = event.scale;
120+
}
121+
event.preventDefault();
122+
};
123+
124+
private onGesturechange = (event: GestureEvent) => {
125+
event.preventDefault();
126+
const clientRect = this.svgContainer.getBoundingClientRect();
127+
const targetX = event.clientX - clientRect.left;
128+
const targetY = event.clientY - clientRect.top;
129+
const scaleChange = event.scale - this.safariScale;
130+
this.setZoom(this.viewer.getZoom() + scaleChange, targetX, targetY);
131+
this.safariScale = event.scale;
132+
};
133+
134+
private onGestureend = () => {
135+
this.gestureStart = false;
136+
};
137+
138+
private prevMouseCordX!: number;
139+
private prevMouseCordY!: number;
140+
141+
private onMouseMove = (event: MouseEvent) => {
142+
event.preventDefault();
143+
if (ViewerEvents.noneTableAndMinmapEvent(event)) {
144+
const deltaX = event.clientX - this.prevMouseCordX;
145+
const deltaY = event.clientY - this.prevMouseCordY;
146+
this.prevMouseCordY = event.clientY;
147+
this.prevMouseCordX = event.clientX;
148+
const originalScrollLeft = this.svgContainer.scrollLeft;
149+
this.svgContainer.scrollLeft -= deltaX;
150+
if (originalScrollLeft !== this.svgContainer.scrollLeft) {
151+
this.viewBoxVals.x -= deltaX;
152+
}
153+
const originalScrollTop = this.svgContainer.scrollTop;
154+
this.svgContainer.scrollTop -= deltaY;
155+
if (originalScrollTop !== this.svgContainer.scrollTop) {
156+
this.viewBoxVals.y -= deltaY;
157+
}
158+
this.minimap.setMinimapViewPoint(this.viewBoxVals);
159+
}
160+
};
161+
162+
private onMousedown = (event: MouseEvent) => {
163+
if (event.button === 0 && ViewerEvents.noneTableAndMinmapEvent(event)) {
164+
this.svgElem.classList.add("pan");
165+
this.prevMouseCordX = event.clientX;
166+
this.prevMouseCordY = event.clientY;
167+
this.mainElem.addEventListener(
168+
"mousemove",
169+
this.onMouseMove as CommonEventListener
170+
);
171+
}
172+
};
173+
174+
private onMouseup = () => {
175+
this.svgElem.classList.remove("pan");
176+
this.mainElem.removeEventListener(
177+
"mousemove",
178+
this.onMouseMove as CommonEventListener
179+
);
180+
};
181+
182+
private evCache: PointerEvent[] = [];
183+
private prevDiff?: number;
184+
185+
private onPointerdown = (event: PointerEvent) => {
186+
this.evCache.push(event);
187+
};
188+
189+
private onPointermove = (event: PointerEvent) => {
190+
const index = this.evCache.findIndex(
191+
(item) => item.pointerId === event.pointerId
192+
);
193+
if (index !== -1) {
194+
this.evCache[index] = event;
195+
}
196+
if (this.evCache.length == 2) {
197+
this.gestureStart = true;
198+
// Calculate the distance between the two pointers
199+
const p1 = { x: this.evCache[0].clientX, y: this.evCache[0].clientY };
200+
const p2 = { x: this.evCache[1].clientX, y: this.evCache[1].clientY };
201+
const centerPoint = center(p1, p2);
202+
const curDiff = distance(p1, p2);
203+
if (this.prevDiff != null) {
204+
const delta = curDiff - this.prevDiff;
205+
event.preventDefault();
206+
this.setZoom(
207+
this.viewer.getZoom() + delta * constant.PINCH_TO_ZOOM_MULTIPLIER,
208+
centerPoint.x,
209+
centerPoint.y
210+
);
211+
}
212+
this.prevDiff = curDiff;
213+
}
214+
};
215+
216+
private onPointer = (event: PointerEvent) => {
217+
// Remove this pointer from the cache and reset the target's
218+
// background and border
219+
const index = this.evCache.findIndex(
220+
(item) => item.pointerId === event.pointerId
221+
);
222+
if (index !== -1) this.evCache.splice(index, 1);
223+
224+
// If the number of pointers down is less than two then reset diff tracker
225+
if (this.evCache.length < 2) {
226+
this.prevDiff = undefined;
227+
this.gestureStart = false;
228+
}
229+
};
230+
231+
private onClick = (event: MouseEvent) => {
232+
const zoom = this.viewer.getZoom();
233+
const x = event.offsetX / zoom;
234+
const y = event.offsetY / zoom;
235+
this.callbacks?.viewportClick(x, y);
236+
};
237+
238+
private windowResizeEvent(): void {
239+
const zoom = this.viewer.getZoom();
240+
this.viewBoxVals.width = this.svgContainer.clientWidth / zoom;
241+
this.viewBoxVals.height = this.svgContainer.clientHeight / zoom;
242+
243+
this.viewer.viewportAddjustment();
244+
245+
this.minimap.setMinimapViewPoint(this.viewBoxVals);
246+
}
247+
248+
setUpEvents(): void {
249+
window.addEventListener("resize", this.windowResizeEvent.bind(this));
250+
251+
this.mainElem.addEventListener("touchmove", this.onTouchMove);
252+
253+
this.container.addEventListener("mouseleave", () => {
254+
this.svgElem.classList.remove("pan");
255+
this.mainElem.removeEventListener(
256+
"mousemove",
257+
this.onMouseMove as CommonEventListener
258+
);
259+
});
260+
261+
this.container.addEventListener("mousedown", this.onMousedown);
262+
263+
this.mainElem.addEventListener("mouseup", this.onMouseup);
264+
265+
this.container.addEventListener(
266+
"mouseleave",
267+
this.minimap.onContainerMouseLeave!
268+
);
269+
this.container.addEventListener(
270+
"mouseup",
271+
this.minimap.onContainerMouseUp!
272+
);
273+
274+
if (this.tables) {
275+
this.tables.forEach((table) => {
276+
table.setMoveListener(this.onTableMove);
277+
});
278+
}
279+
280+
this.svgContainer.addEventListener("scroll", this.onScroll);
281+
282+
this.svgContainer.addEventListener("click", this.onClick);
283+
284+
this.container.addEventListener("wheel", this.onWheel);
285+
286+
if (isSafari) {
287+
this.container.addEventListener(
288+
"gesturestart",
289+
this.onGesturestart as CommonEventListener
290+
);
291+
this.container.addEventListener(
292+
"gesturechange",
293+
this.onGesturechange as CommonEventListener,
294+
true
295+
);
296+
297+
this.container.addEventListener("gestureend", this.onGestureend);
298+
} else {
299+
this.container.addEventListener("pointerdown", this.onPointerdown);
300+
this.container.addEventListener("pointermove", this.onPointermove);
301+
302+
this.container.addEventListener("pointerup", this.onPointer, true);
303+
this.container.addEventListener("pointercancel", this.onPointer, true);
304+
this.container.addEventListener("pointerout", this.onPointer, true);
305+
this.container.addEventListener("pointerleave", this.onPointer, true);
306+
}
307+
}
308+
309+
// TODO: call cleanup when appropriate
310+
cleanup(): void {
311+
this.minimap.cleanup();
312+
313+
this.mainElem.removeEventListener(
314+
"mousemove",
315+
this.onMouseMove as CommonEventListener
316+
);
317+
318+
window.removeEventListener("resize", this.windowResizeEvent.bind(this));
319+
this.mainElem.removeEventListener("touchmove", this.onTouchMove);
320+
this.container.removeEventListener("mousedown", this.onMousedown);
321+
this.mainElem.removeEventListener("mouseup", this.onMouseup);
322+
this.container.removeEventListener(
323+
"mouseleave",
324+
this.minimap.onContainerMouseLeave!
325+
);
326+
this.container.removeEventListener(
327+
"mouseup",
328+
this.minimap.onContainerMouseUp!
329+
);
330+
331+
this.tables.forEach((table) => {
332+
table.cleanup();
333+
});
334+
335+
this.svgContainer.removeEventListener("scroll", this.onScroll);
336+
this.svgContainer.removeEventListener("click", this.onClick);
337+
this.container.removeEventListener("wheel", this.onWheel);
338+
339+
if (isSafari) {
340+
this.container.removeEventListener(
341+
"gesturestart",
342+
this.onGesturestart as CommonEventListener
343+
);
344+
this.container.removeEventListener(
345+
"gesturechange",
346+
this.onGesturechange as CommonEventListener,
347+
true
348+
);
349+
350+
this.container.removeEventListener("gestureend", this.onGestureend);
351+
} else {
352+
this.container.removeEventListener("pointerdown", this.onPointerdown);
353+
this.container.removeEventListener("pointermove", this.onPointermove);
354+
355+
this.container.removeEventListener("pointerup", this.onPointer, true);
356+
this.container.removeEventListener("pointercancel", this.onPointer, true);
357+
this.container.removeEventListener("pointerout", this.onPointer, true);
358+
this.container.removeEventListener("pointerleave", this.onPointer, true);
359+
}
360+
}
361+
}
362+
363+
export default ViewerEvents;

0 commit comments

Comments
 (0)