Skip to content

Commit 09599b1

Browse files
authored
Implement pinch zooming on mobile (#11)
Fixes #10
1 parent 78b6a8d commit 09599b1

File tree

5 files changed

+123
-25
lines changed

5 files changed

+123
-25
lines changed

‎assets/css/gallerydeluxe/styles.css‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
background-color: rgba(0, 0, 0, 0.8);
2828
}
2929

30+
.gd-modal-content-wrapper {
31+
position: relative;
32+
width: 100%;
33+
height: 100%;
34+
}
35+
3036
/* Modal Content (image) */
3137
.gd-modal-content {
3238
max-width: 100%;

‎assets/js/gallerydeluxe/src/helpers.js‎

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
'use strict';
22

33
export function newSwiper(el, callback) {
4+
const debug = 0 ? console.log.bind(console, '[swiper]') : function () {};
5+
6+
const simulateTwoFingers = false;
7+
48
// Number of pixels between touch start/end for us to consider it a swipe.
59
const moveThreshold = 50;
610

7-
var touches = {
8-
touchstart: { x: -1, y: -1 },
9-
touchmove: { x: -1, y: -1 },
11+
const fingerDistance = (touches) => {
12+
return Math.hypot(touches[0].pageX - touches[1].pageX, touches[0].pageY - touches[1].pageY);
13+
};
14+
15+
var touch = {
16+
touchstart: { x: -1, y: -1, d: -1 },
17+
touchmove: { x: -1, y: -1, d: -1 },
18+
multitouch: false,
1019
};
1120

1221
// direction returns right, left, up or down or empty if below moveThreshold.
13-
touches.direction = function () {
22+
touch.direction = function () {
1423
if (this.touchmove.x == -1) {
1524
// A regular click.
1625
return '';
@@ -27,32 +36,78 @@ export function newSwiper(el, callback) {
2736
return distancex > 0 ? 'right' : 'left';
2837
};
2938

30-
touches.reset = function () {
39+
touch.reset = function () {
3140
(this.touchstart.x = -1), (this.touchstart.y = -1);
3241
(this.touchmove.x = -1), (this.touchmove.y = -1);
42+
(this.touchstart.d = -1), (this.touchmove.d = -1);
43+
this.multitouch = false;
44+
};
45+
46+
touch.update = function (event, touches) {
47+
this.multitouch = this.multitouch || touches.length > 1;
48+
if (touches.length > 1) {
49+
this[event.type].d = fingerDistance(touches);
50+
}
51+
this[event.type].x = touches[0].pageX;
52+
this[event.type].y = touches[0].pageY;
3353
};
3454

35-
touches.update = function (event, touch) {
36-
this[event.type].x = touch.pageX;
37-
this[event.type].y = touch.pageY;
55+
const pinch = function (event, touches) {
56+
let scale = 1;
57+
if (touches.length === 2) {
58+
// Two fingers on screen.
59+
if (event.scale) {
60+
scale = event.scale;
61+
} else {
62+
scale = touch.touchmove.d / touch.touchstart.d;
63+
scale = Math.round(scale * 100) / 100;
64+
}
65+
if (scale < 1) {
66+
scale = 1;
67+
}
68+
let distancex = touch.touchmove.x - touch.touchstart.x;
69+
let distancey = touch.touchmove.y - touch.touchstart.y;
70+
71+
el.style.transform = `translate3d(${distancex}px, ${distancey}px, 0) scale(${scale})`;
72+
el.style.zIndex = 1000;
73+
} else {
74+
// One finger on screen.
75+
el.style.transform = '';
76+
el.style.zIndex = '';
77+
}
3878
};
3979

4080
var handleTouch = function (event) {
81+
debug('event', event.type);
4182
if (typeof event !== 'undefined' && typeof event.touches !== 'undefined') {
42-
var touch = event.touches[0];
83+
let touches = event.touches;
84+
85+
if (simulateTwoFingers && touches.length === 1) {
86+
touches = [
87+
{ pageX: touches[0].pageX, pageY: touches[0].pageY },
88+
{ pageX: 0, pageY: 0 },
89+
];
90+
}
4391
switch (event.type) {
4492
case 'touchstart':
45-
touches.reset();
46-
touches.update(event, touch);
93+
touch.reset();
94+
touch.update(event, touches);
4795
break;
4896
case 'touchmove':
49-
touches.update(event, touch);
97+
touch.update(event, touches);
98+
pinch(event, touches);
5099
break;
51100
case 'touchend':
52-
let direction = touches.direction();
53-
if (direction) {
54-
callback(direction);
101+
el.style.transform = '';
102+
if (!touch.multitouch) {
103+
// Only consider a swipe if we have one finger on screen.
104+
let direction = touch.direction();
105+
debug('direction', direction);
106+
if (direction) {
107+
callback(direction);
108+
}
55109
}
110+
56111
break;
57112
default:
58113
break;

‎assets/js/gallerydeluxe/src/index.js‎

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ var debug = 0 ? console.log.bind(console, '[gallery-deluxe]') : function () {};
88

99
let GalleryDeluxe = {
1010
init: async function () {
11+
// Having the gallery zoomable makes for a very confusing UI.
12+
// This seem to be the only way to disable it on Safari on IOS.
13+
document.addEventListener(
14+
'touchmove',
15+
function (event) {
16+
if (event.scale !== 1) {
17+
event.preventDefault();
18+
}
19+
},
20+
{ passive: false }
21+
);
1122
// One gallery per page (for now).
1223
const galleryId = 'gallerydeluxe';
1324
const dataAttributeName = 'data-gd-image-data-url';
@@ -22,22 +33,28 @@ let GalleryDeluxe = {
2233

2334
// The image opened in the lightbox.
2435
let activeImage;
36+
let exifTimeoutId;
2537

2638
// Lightbox setup.
2739
const modal = document.getElementById('gd-modal');
28-
const modalClose = document.getElementById('gd-modal-close');
40+
const modalClose = modal.querySelector('#gd-modal-close');
2941

3042
const preventDefault = function (e) {
3143
// For iphone.
3244
e.preventDefault();
3345
};
3446

47+
let imageWrapper = document.createElement('div');
48+
imageWrapper.classList.add('gd-modal-content-wrapper');
49+
modal.insertBefore(imageWrapper, modal.firstChild);
50+
3551
const closeModal = (e) => {
3652
if (e) {
3753
e.preventDefault();
3854
}
3955

40-
modal.removeEventListener('touchmove', preventDefault);
56+
imageWrapper.removeEventListener('touchmove', preventDefault);
57+
imageWrapper.removeEventListener('gesturestart', preventDefault);
4158

4259
// Hide the modal.
4360
modal.style.display = 'none';
@@ -67,7 +84,7 @@ let GalleryDeluxe = {
6784
};
6885

6986
// Add some basic swipe logic.
70-
newSwiper(modal, function (direction) {
87+
newSwiper(imageWrapper, function (direction) {
7188
swipe(direction);
7289
});
7390

@@ -86,10 +103,12 @@ let GalleryDeluxe = {
86103
});
87104

88105
const openActiveImage = () => {
89-
modal.addEventListener('touchmove', preventDefault);
106+
imageWrapper.addEventListener('touchmove', preventDefault);
107+
imageWrapper.addEventListener('gesturestart', preventDefault);
90108

91109
const classLoaded = 'gd-modal-loaded';
92110
const classThumbnail = 'gd-modal-thumbnail';
111+
93112
// Prevent scrolling of the background.
94113
document.body.style.overflow = 'hidden';
95114
let oldEls = modal.querySelectorAll('.gd-modal-content');
@@ -112,9 +131,13 @@ let GalleryDeluxe = {
112131
let modal = document.getElementById('gd-modal');
113132

114133
if (params.enable_exif) {
115-
let exif = document.getElementById('gd-modal-exif');
134+
if (exifTimeoutId) {
135+
clearTimeout(exifTimeoutId);
136+
}
137+
138+
let exif = modal.querySelector('#gd-modal-exif');
116139
const onTimeOutClass = 'gd-modal-exif-ontimeout';
117-
exif.classList.remove(onTimeOutClass);
140+
118141
let child = exif.lastElementChild;
119142
while (child) {
120143
exif.removeChild(child);
@@ -139,10 +162,11 @@ let GalleryDeluxe = {
139162
for (const tag in tags) {
140163
addTag(tag, tags[tag]);
141164
}
165+
exif.classList.remove(onTimeOutClass);
142166

143-
setTimeout(() => {
167+
exifTimeoutId = setTimeout(() => {
144168
exif.classList.add(onTimeOutClass);
145-
}, 1000);
169+
}, 1200);
146170
}
147171

148172
let thumbnail = new Image();
@@ -160,14 +184,14 @@ let GalleryDeluxe = {
160184

161185
thumbnail.onload = function () {
162186
if (thumbnail) {
163-
modal.appendChild(thumbnail);
187+
imageWrapper.appendChild(thumbnail);
164188
removeOldEls();
165189
}
166190
};
167191

168192
fullImage.onload = function () {
169193
if (fullImage) {
170-
modal.appendChild(fullImage);
194+
imageWrapper.appendChild(fullImage);
171195
fullImage.classList.add(classLoaded);
172196
if (thumbnail) {
173197
thumbnail.classList.add(classLoaded);

‎exampleSite/config.toml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ disableKinds = ["taxonomy", "term", "section"]
66
[[module.imports]]
77
path = "github.com/bep/gallerydeluxe_starter"
88

9+
[params]
10+
# Set to enable Plausible Analytics.
11+
plausible_domain = ""
12+
13+
[params.gallerydeluxe]
14+
shuffle = false
15+
reverse = true
16+
enable_exif = false
17+
918
[imaging]
1019
resampleFilter = "CatmullRom"
1120
quality = 71

‎exampleSite/hugo.work‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
go 1.19
2+
3+
use .
4+
use ../../gallerydeluxe_starter

0 commit comments

Comments
 (0)