aboutsummaryrefslogtreecommitdiff
path: root/viewer/src/services/ldzoom.ts
diff options
context:
space:
mode:
Diffstat (limited to 'viewer/src/services/ldzoom.ts')
-rw-r--r--viewer/src/services/ldzoom.ts135
1 files changed, 0 insertions, 135 deletions
diff --git a/viewer/src/services/ldzoom.ts b/viewer/src/services/ldzoom.ts
deleted file mode 100644
index 33a64c8..0000000
--- a/viewer/src/services/ldzoom.ts
+++ /dev/null
@@ -1,135 +0,0 @@
1/* ldgallery - A static generator which turns a collection of tagged
2-- pictures into a searchable web gallery.
3--
4-- Copyright (C) 2020 Pacien TRAN-GIRARD
5--
6-- This program is free software: you can redistribute it and/or modify
7-- it under the terms of the GNU Affero General Public License as
8-- published by the Free Software Foundation, either version 3 of the
9-- License, or (at your option) any later version.
10--
11-- This program is distributed in the hope that it will be useful,
12-- but WITHOUT ANY WARRANTY; without even the implied warranty of
13-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14-- GNU Affero General Public License for more details.
15--
16-- You should have received a copy of the GNU Affero General Public License
17-- along with this program. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20import { PictureProperties, Resolution } from "@/@types/gallery";
21import "hammerjs";
22
23/**
24 * Mousewheel and pinch zoom handler.
25 */
26export default class LdZoom {
27 readonly containerElement: HTMLDivElement;
28 readonly imageElement: HTMLImageElement;
29 readonly pictureProperties: PictureProperties;
30 readonly maxScaleFactor: number;
31 readonly scrollZoomSpeed: number;
32 scaleFactor: number = 0.0;
33
34 constructor(
35 containerElement: HTMLDivElement,
36 imageElement: HTMLImageElement,
37 pictureProperties: PictureProperties,
38 maxScaleFactor: number,
39 scrollZoomSpeed: number
40 ) {
41 this.containerElement = containerElement;
42 this.imageElement = imageElement;
43 this.pictureProperties = pictureProperties;
44 this.maxScaleFactor = maxScaleFactor;
45 this.scrollZoomSpeed = scrollZoomSpeed;
46 }
47
48 /**
49 * Register event listeners.
50 */
51 public install() {
52 this.updateImageScale(this.scaleFactor);
53
54 new ResizeObserver(() => {
55 this.updateImageScale(this.scaleFactor);
56 }).observe(this.containerElement);
57
58 this.containerElement.addEventListener("wheel", wheelEvent => {
59 wheelEvent.preventDefault();
60 const scaleFactor = this.scaleFactor - Math.sign(wheelEvent.deltaY) * this.scrollZoomSpeed;
61 this.zoom(wheelEvent.offsetX, wheelEvent.offsetY, scaleFactor);
62 });
63
64 const pinchListener = new Hammer(this.containerElement);
65 pinchListener.get("pinch").set({ enable: true });
66 this.installPinchHandler(pinchListener);
67 }
68
69 private installPinchHandler(pinchListener: HammerManager) {
70 let startScaleFactor = 0.0;
71
72 pinchListener.on("pinchstart", (pinchEvent: HammerInput) => {
73 startScaleFactor = this.scaleFactor;
74 });
75
76 pinchListener.on("pinchmove", (pinchEvent: HammerInput) => {
77 const focusX = pinchEvent.center.x + this.containerElement.scrollLeft;
78 const focusY = pinchEvent.center.y + this.containerElement.scrollTop;
79 const scaleFactor = pinchEvent.scale * startScaleFactor;
80 this.zoom(focusX, focusY, scaleFactor);
81 });
82 }
83
84 /**
85 * Returns the picture resolution as it should be displayed.
86 */
87 private getDisplayResolution(): Resolution {
88 return {
89 width: this.pictureProperties.resolution.width * this.scaleFactor,
90 height: this.pictureProperties.resolution.height * this.scaleFactor,
91 };
92 }
93
94 /**
95 * Applies scaling to the DOM image element.
96 * To call after internal intermediate computations because DOM properties aren't stable.
97 */
98 private resizeImageElement() {
99 const imageDim = this.getDisplayResolution();
100 this.imageElement.width = imageDim.width;
101 this.imageElement.height = imageDim.height;
102 }
103
104 /**
105 * Centers the image element inside its container if it fits, or stick to the top and left borders otherwise.
106 * It's depressingly hard to do in pure CSS…
107 */
108 private recenterImageElement() {
109 const imageDim = this.getDisplayResolution();
110 const marginLeft = Math.max((this.containerElement.clientWidth - imageDim.width) / 2, 0);
111 const marginTop = Math.max((this.containerElement.clientHeight - imageDim.height) / 2, 0);
112 this.imageElement.style.marginLeft = `${marginLeft}px`;
113 this.imageElement.style.marginTop = `${marginTop}px`;
114 }
115
116 private zoom(focusX: number, focusY: number, scaleFactor: number) {
117 const imageDim = this.getDisplayResolution();
118 const ratioX = focusX / imageDim.width;
119 const ratioY = focusY / imageDim.height;
120 this.updateImageScale(Math.min(scaleFactor, this.maxScaleFactor));
121
122 const newImageDim = this.getDisplayResolution();
123 this.containerElement.scrollLeft -= focusX - ratioX * newImageDim.width;
124 this.containerElement.scrollTop -= focusY - ratioY * newImageDim.height;
125 }
126
127 private updateImageScale(newScaleFactor: number) {
128 const horizontalFillRatio = this.containerElement.clientWidth / this.pictureProperties.resolution.width;
129 const verticalFillRatio = this.containerElement.clientHeight / this.pictureProperties.resolution.height;
130 const minScaleFactor = Math.min(horizontalFillRatio, verticalFillRatio, 1.0);
131 this.scaleFactor = Math.max(newScaleFactor, minScaleFactor);
132 this.resizeImageElement();
133 this.recenterImageElement();
134 }
135}