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.ts60
1 files changed, 41 insertions, 19 deletions
diff --git a/viewer/src/services/ldzoom.ts b/viewer/src/services/ldzoom.ts
index 50f006e..c28c2c8 100644
--- a/viewer/src/services/ldzoom.ts
+++ b/viewer/src/services/ldzoom.ts
@@ -27,28 +27,31 @@ import "hammerjs";
27export default class LdZoom { 27export default class LdZoom {
28 readonly containerElement: HTMLDivElement; 28 readonly containerElement: HTMLDivElement;
29 readonly imageElement: HTMLImageElement; 29 readonly imageElement: HTMLImageElement;
30 readonly pictureProperties: Gallery.PictureProperties;
30 readonly maxScaleFactor: number; 31 readonly maxScaleFactor: number;
31 readonly scrollZoomSpeed: number; 32 readonly scrollZoomSpeed: number;
32 scaleFactor: number = 0.0; 33 scaleFactor: number = 0.0;
33 34
34 constructor( 35 constructor(
35 containerElement: HTMLDivElement, imageElement: HTMLImageElement, 36 containerElement: HTMLDivElement, imageElement: HTMLImageElement,
37 pictureProperties: Gallery.PictureProperties,
36 maxScaleFactor: number, scrollZoomSpeed: number 38 maxScaleFactor: number, scrollZoomSpeed: number
37 ) { 39 ) {
38 this.containerElement = containerElement; 40 this.containerElement = containerElement;
39 this.imageElement = imageElement; 41 this.imageElement = imageElement;
42 this.pictureProperties = pictureProperties;
40 this.maxScaleFactor = maxScaleFactor; 43 this.maxScaleFactor = maxScaleFactor;
41 this.scrollZoomSpeed = scrollZoomSpeed; 44 this.scrollZoomSpeed = scrollZoomSpeed;
42 } 45 }
43 46
44 /** 47 /**
45 * Register event listeners. 48 * Register event listeners.
46 * The dimension of the image should be known before calling this method.
47 */ 49 */
48 public install() { 50 public install() {
51 this.updateImageScale(this.scaleFactor);
52
49 new ResizeObserver(() => { 53 new ResizeObserver(() => {
50 this.setImageScale(this.scaleFactor); 54 this.updateImageScale(this.scaleFactor);
51 this.recenterImageElement();
52 }).observe(this.containerElement); 55 }).observe(this.containerElement);
53 56
54 this.containerElement.addEventListener('wheel', wheelEvent => { 57 this.containerElement.addEventListener('wheel', wheelEvent => {
@@ -60,8 +63,6 @@ export default class LdZoom {
60 const pinchListener = new Hammer(this.containerElement); 63 const pinchListener = new Hammer(this.containerElement);
61 pinchListener.get('pinch').set({enable: true}); 64 pinchListener.get('pinch').set({enable: true});
62 this.installPinchHandler(pinchListener); 65 this.installPinchHandler(pinchListener);
63
64 this.setImageScale(this.scaleFactor);
65 } 66 }
66 67
67 private installPinchHandler(pinchListener: HammerManager) { 68 private installPinchHandler(pinchListener: HammerManager) {
@@ -72,7 +73,6 @@ export default class LdZoom {
72 }); 73 });
73 74
74 pinchListener.on('pinchmove', (pinchEvent: HammerInput) => { 75 pinchListener.on('pinchmove', (pinchEvent: HammerInput) => {
75 // FIXME: v-dragscroll interferes with our focus point scroll adjustment
76 const focusX = pinchEvent.center.x + this.containerElement.scrollLeft; 76 const focusX = pinchEvent.center.x + this.containerElement.scrollLeft;
77 const focusY = pinchEvent.center.y + this.containerElement.scrollTop; 77 const focusY = pinchEvent.center.y + this.containerElement.scrollTop;
78 const scaleFactor = pinchEvent.scale * startScaleFactor; 78 const scaleFactor = pinchEvent.scale * startScaleFactor;
@@ -81,32 +81,54 @@ export default class LdZoom {
81 } 81 }
82 82
83 /** 83 /**
84 * Returns the picture resolution as it should be displayed.
85 */
86 private getDisplayResolution(): Gallery.Resolution {
87 return {
88 width: this.pictureProperties.resolution.width * this.scaleFactor,
89 height: this.pictureProperties.resolution.height * this.scaleFactor,
90 };
91 }
92
93 /**
94 * Applies scaling to the DOM image element.
95 * To call after internal intermediate computations because DOM properties aren't stable.
96 */
97 private resizeImageElement() {
98 const imageDim = this.getDisplayResolution();
99 this.imageElement.width = imageDim.width;
100 this.imageElement.height = imageDim.height;
101 }
102
103 /**
84 * Centers the image element inside its container if it fits, or stick to the top and left borders otherwise. 104 * Centers the image element inside its container if it fits, or stick to the top and left borders otherwise.
85 * It's depressingly hard to do in pure CSS… 105 * It's depressingly hard to do in pure CSS…
86 */ 106 */
87 private recenterImageElement() { 107 private recenterImageElement() {
88 const marginLeft = Math.max((this.containerElement.clientWidth - this.imageElement.clientWidth) / 2, 0); 108 const imageDim = this.getDisplayResolution();
89 const marginTop = Math.max((this.containerElement.clientHeight - this.imageElement.clientHeight) / 2, 0); 109 const marginLeft = Math.max((this.containerElement.clientWidth - imageDim.width) / 2, 0);
110 const marginTop = Math.max((this.containerElement.clientHeight - imageDim.height) / 2, 0);
90 this.imageElement.style.marginLeft = `${marginLeft}px`; 111 this.imageElement.style.marginLeft = `${marginLeft}px`;
91 this.imageElement.style.marginTop = `${marginTop}px`; 112 this.imageElement.style.marginTop = `${marginTop}px`;
92 } 113 }
93 114
94 private zoom(focusX: number, focusY: number, scaleFactor: number) { 115 private zoom(focusX: number, focusY: number, scaleFactor: number) {
95 const ratioX = focusX / this.imageElement.clientWidth; 116 const imageDim = this.getDisplayResolution();
96 const ratioY = focusY / this.imageElement.clientHeight; 117 const ratioX = focusX / imageDim.width;
97 this.setImageScale(Math.min(scaleFactor, this.maxScaleFactor)); 118 const ratioY = focusY / imageDim.height;
98 this.containerElement.scrollLeft -= focusX - ratioX * this.imageElement.clientWidth; 119 this.updateImageScale(Math.min(scaleFactor, this.maxScaleFactor));
99 this.containerElement.scrollTop -= focusY - ratioY * this.imageElement.clientHeight; 120
121 const newImageDim = this.getDisplayResolution();
122 this.containerElement.scrollLeft -= focusX - ratioX * newImageDim.width;
123 this.containerElement.scrollTop -= focusY - ratioY * newImageDim.height;
100 } 124 }
101 125
102 private setImageScale(newScaleFactor: number) { 126 private updateImageScale(newScaleFactor: number) {
103 const horizontalFillRatio = this.containerElement.clientWidth / this.imageElement.naturalWidth; 127 const horizontalFillRatio = this.containerElement.clientWidth / this.pictureProperties.resolution.width;
104 const verticalFillRatio = this.containerElement.clientHeight / this.imageElement.naturalHeight; 128 const verticalFillRatio = this.containerElement.clientHeight / this.pictureProperties.resolution.height;
105 const minScaleFactor = Math.min(horizontalFillRatio, verticalFillRatio, 1.0); 129 const minScaleFactor = Math.min(horizontalFillRatio, verticalFillRatio, 1.0);
106 this.scaleFactor = Math.max(newScaleFactor, minScaleFactor); 130 this.scaleFactor = Math.max(newScaleFactor, minScaleFactor);
107 131 this.resizeImageElement();
108 this.imageElement.width = this.scaleFactor * this.imageElement.naturalWidth;
109 this.imageElement.height = this.scaleFactor * this.imageElement.naturalHeight;
110 this.recenterImageElement(); 132 this.recenterImageElement();
111 } 133 }
112} 134}