aboutsummaryrefslogtreecommitdiff
path: root/viewer/src
diff options
context:
space:
mode:
Diffstat (limited to 'viewer/src')
-rw-r--r--viewer/src/@types/gallery.d.ts10
-rw-r--r--viewer/src/components/LdPicture.vue56
-rw-r--r--viewer/src/services/ldzoom.ts134
-rw-r--r--viewer/src/views/MainLayout.vue1
4 files changed, 168 insertions, 33 deletions
diff --git a/viewer/src/@types/gallery.d.ts b/viewer/src/@types/gallery.d.ts
index de1c0dd..956ab6b 100644
--- a/viewer/src/@types/gallery.d.ts
+++ b/viewer/src/@types/gallery.d.ts
@@ -49,12 +49,17 @@ declare namespace Gallery {
49 thumbnail?: Thumbnail 49 thumbnail?: Thumbnail
50 properties: OtherProperties | PictureProperties | DirectoryProperties, 50 properties: OtherProperties | PictureProperties | DirectoryProperties,
51 } 51 }
52 interface Resolution {
53 width: number,
54 height: number,
55 }
52 interface OtherProperties { 56 interface OtherProperties {
53 type: "other", 57 type: "other",
54 } 58 }
55 interface PictureProperties { 59 interface PictureProperties {
56 type: "picture", 60 type: "picture",
57 resource: string, 61 resource: string,
62 resolution: Resolution
58 } 63 }
59 interface DirectoryProperties { 64 interface DirectoryProperties {
60 type: "directory", 65 type: "directory",
@@ -62,10 +67,7 @@ declare namespace Gallery {
62 } 67 }
63 interface Thumbnail { 68 interface Thumbnail {
64 resource: string, 69 resource: string,
65 resolution: { 70 resolution: Resolution
66 width: number,
67 height: number,
68 }
69 } 71 }
70 type RawTag = string; 72 type RawTag = string;
71 type ItemType = "other" | "picture" | "directory"; 73 type ItemType = "other" | "picture" | "directory";
diff --git a/viewer/src/components/LdPicture.vue b/viewer/src/components/LdPicture.vue
index 3170c81..de46bcb 100644
--- a/viewer/src/components/LdPicture.vue
+++ b/viewer/src/components/LdPicture.vue
@@ -18,17 +18,21 @@
18--> 18-->
19 19
20<template> 20<template>
21 <!-- FIXME: v-dragscroll interferes with pinch-to-zoom -->
21 <div 22 <div
23 ref="containerElement"
22 v-dragscroll 24 v-dragscroll
23 class="scrollbar" 25 class="scrollbar ld-picture-container"
24 :class="{'fit-to-screen': !$uiStore.fullscreen, 'original-size': $uiStore.fullscreen}"
25 @click.capture="e => dragScrollClickFix.onClickCapture(e)" 26 @click.capture="e => dragScrollClickFix.onClickCapture(e)"
26 @click="$uiStore.toggleFullscreen()" 27 @dblclick="$uiStore.toggleFullscreen()"
28 @dragstart.prevent
27 @dragscrollstart="dragScrollClickFix.onDragScrollStart()" 29 @dragscrollstart="dragScrollClickFix.onDragScrollStart()"
28 @dragscrollend="dragScrollClickFix.onDragScrollEnd()" 30 @dragscrollend="dragScrollClickFix.onDragScrollEnd()"
29 > 31 >
30 <v-lazy-image 32 <v-lazy-image
33 ref="imageElement"
31 :src="pictureSrc(picture.properties.resource)" 34 :src="pictureSrc(picture.properties.resource)"
35 class="ld-picture-element"
32 :class="{'slow-loading': Boolean(slowLoadingStyle)}" 36 :class="{'slow-loading': Boolean(slowLoadingStyle)}"
33 :style="slowLoadingStyle" 37 :style="slowLoadingStyle"
34 @load="clearSlowLoading" 38 @load="clearSlowLoading"
@@ -38,12 +42,15 @@
38</template> 42</template>
39 43
40<script lang="ts"> 44<script lang="ts">
41import { Component, Vue, Prop } from "vue-property-decorator"; 45import { Component, Vue, Prop, Ref } from "vue-property-decorator";
46import LdZoom from "@/services/ldzoom";
42import DragScrollClickFix from "@/services/dragscrollclickfix"; 47import DragScrollClickFix from "@/services/dragscrollclickfix";
43 48
44@Component 49@Component
45export default class LdPicture extends Vue { 50export default class LdPicture extends Vue {
46 @Prop({ required: true }) readonly picture!: Gallery.Picture; 51 @Prop({ required: true }) readonly picture!: Gallery.Picture;
52 @Ref() readonly containerElement!: HTMLDivElement;
53 @Ref() readonly imageElement!: Vue;
47 54
48 readonly SLOW_LOADING_TIMEOUT_MS: number = 1500; 55 readonly SLOW_LOADING_TIMEOUT_MS: number = 1500;
49 readonly dragScrollClickFix = new DragScrollClickFix(); 56 readonly dragScrollClickFix = new DragScrollClickFix();
@@ -54,6 +61,7 @@ export default class LdPicture extends Vue {
54 61
55 mounted() { 62 mounted() {
56 this.timer = setTimeout(this.generateSlowLoadingStyle, this.SLOW_LOADING_TIMEOUT_MS); 63 this.timer = setTimeout(this.generateSlowLoadingStyle, this.SLOW_LOADING_TIMEOUT_MS);
64 new LdZoom(this.containerElement, this.imageElement.$el as HTMLImageElement, this.picture.properties, 10, 1 / 5).install();
57 } 65 }
58 66
59 destroyed() { 67 destroyed() {
@@ -85,13 +93,17 @@ export default class LdPicture extends Vue {
85<style lang="scss"> 93<style lang="scss">
86@import "~@/assets/scss/theme.scss"; 94@import "~@/assets/scss/theme.scss";
87 95
88.ld-picture-loader { 96.ld-picture-container {
89 position: relative; 97 height: 100%;
90 & .loading-background {
91 background: none !important;
92 }
93} 98}
94img.slow-loading { 99
100.ld-picture-element {
101 max-width: unset;
102 max-height: unset;
103 cursor: grab;
104}
105
106.slow-loading {
95 background-repeat: no-repeat; 107 background-repeat: no-repeat;
96 background-position: center; 108 background-position: center;
97 background-size: contain; 109 background-size: contain;
@@ -99,25 +111,11 @@ img.slow-loading {
99 background-blend-mode: soft-light; 111 background-blend-mode: soft-light;
100 opacity: 1 !important; 112 opacity: 1 !important;
101} 113}
102.fit-to-screen { 114
103 display: flex; 115.ld-picture-loader {
104 justify-content: space-around; 116 position: relative;
105 height: 100%; 117 & .loading-background {
106 & > img { 118 background: none !important;
107 object-fit: contain;
108 cursor: zoom-in;
109 }
110}
111.original-size {
112 display: block;
113 text-align: center;
114 cursor: grab;
115 height: 100%;
116 & > img {
117 max-width: unset;
118 max-height: unset;
119 object-fit: none;
120 cursor: zoom-out;
121 } 119 }
122} 120}
123</style> 121</style>
diff --git a/viewer/src/services/ldzoom.ts b/viewer/src/services/ldzoom.ts
new file mode 100644
index 0000000..c28c2c8
--- /dev/null
+++ b/viewer/src/services/ldzoom.ts
@@ -0,0 +1,134 @@
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
20// polyfill still required for IE and Safari, see https://caniuse.com/#feat=resizeobserver
21import ResizeObserver from 'resize-observer-polyfill';
22import "hammerjs";
23
24/**
25 * Mousewheel and pinch zoom handler.
26 */
27export default class LdZoom {
28 readonly containerElement: HTMLDivElement;
29 readonly imageElement: HTMLImageElement;
30 readonly pictureProperties: Gallery.PictureProperties;
31 readonly maxScaleFactor: number;
32 readonly scrollZoomSpeed: number;
33 scaleFactor: number = 0.0;
34
35 constructor(
36 containerElement: HTMLDivElement, imageElement: HTMLImageElement,
37 pictureProperties: Gallery.PictureProperties,
38 maxScaleFactor: number, scrollZoomSpeed: number
39 ) {
40 this.containerElement = containerElement;
41 this.imageElement = imageElement;
42 this.pictureProperties = pictureProperties;
43 this.maxScaleFactor = maxScaleFactor;
44 this.scrollZoomSpeed = scrollZoomSpeed;
45 }
46
47 /**
48 * Register event listeners.
49 */
50 public install() {
51 this.updateImageScale(this.scaleFactor);
52
53 new ResizeObserver(() => {
54 this.updateImageScale(this.scaleFactor);
55 }).observe(this.containerElement);
56
57 this.containerElement.addEventListener('wheel', wheelEvent => {
58 wheelEvent.preventDefault();
59 const scaleFactor = this.scaleFactor - Math.sign(wheelEvent.deltaY) * this.scrollZoomSpeed;
60 this.zoom(wheelEvent.offsetX, wheelEvent.offsetY, scaleFactor);
61 });
62
63 const pinchListener = new Hammer(this.containerElement);
64 pinchListener.get('pinch').set({enable: true});
65 this.installPinchHandler(pinchListener);
66 }
67
68 private installPinchHandler(pinchListener: HammerManager) {
69 let startScaleFactor = 0.0;
70
71 pinchListener.on('pinchstart', (pinchEvent: HammerInput) => {
72 startScaleFactor = this.scaleFactor;
<