Skip to content

Commit ae6ea1a

Browse files
authored
Merge pull request #73 from thekid/feature/prev-next-swipe
Preview previous and next images when swiping
2 parents 4c755c7 + 3516b39 commit ae6ea1a

File tree

2 files changed

+64
-30
lines changed

2 files changed

+64
-30
lines changed

src/main/handlebars/layout.handlebars

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,12 +668,23 @@
668668
overflow: hidden;
669669
}
670670
671+
.imageset {
672+
display: flex;
673+
flex-direction: row;
674+
position: relative;
675+
}
676+
671677
img {
672678
max-height: calc(100vh - 6rem);
673679
width: auto;
674680
margin: auto;
675681
}
676682
683+
img.next, img.prev {
684+
position: absolute;
685+
visibility: hidden;
686+
}
687+
677688
.meta {
678689
position: absolute;
679690
inset: auto 0 0 0;
@@ -702,7 +713,7 @@
702713
}
703714
}
704715
705-
img:hover ~ .meta {
716+
.imageset:hover ~ .meta {
706717
transform: scale(1, 1);
707718
}
708719
}
@@ -812,7 +823,11 @@
812823

813824
<dialog id="lightbox">
814825
<div class="image">
815-
<img alt="Lightbox" width="100%">
826+
<div class="imageset">
827+
<img class="prev" alt="Lightbox (previous)" width="100%">
828+
<img class="next" alt="Lightbox (next)" width="100%">
829+
<img class="display" alt="Lightbox" width="100%">
830+
</div>
816831
<div class="meta">
817832
<div><output name="datetime"></output></div>
818833
<div><output name="make"></output></div>

src/main/js/lightbox.js

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,41 @@
11
class Lightbox {
2+
#replace = null;
23

3-
#meta($meta, dataset) {
4-
if ('' !== (dataset.make ?? '')) {
5-
$meta.querySelectorAll('output').forEach($o => $o.value = dataset[$o.name]);
6-
$meta.style.visibility = 'visible';
4+
/** Wraps around to last item at beginning, and first item at end of list */
5+
#wrap(offset, length) {
6+
if (offset < 0) {
7+
return length - 1;
8+
} else if (offset >= length) {
9+
return 0;
710
} else {
8-
$meta.style.visibility = 'hidden';
11+
return offset;
912
}
1013
}
1114

1215
/** Opens the given lightbox, loading the image and filling in meta data */
13-
#open($target, $link, offset) {
14-
const $full = $target.querySelector('img');
16+
#open($target, $links, offset) {
17+
offset = this.#wrap(offset, $links.length);
18+
const $display = $target.querySelector('img.display');
19+
const $link = $links.item(offset);
1520
const $img = $link.querySelector('img');
21+
const $meta = $target.querySelector('.meta');
1622

17-
// Use opening image...
18-
$full.src = $img.src;
19-
$target.showModal();
20-
21-
// ...then replace by larger version
22-
this.#meta($target.querySelector('.meta'), $img.dataset);
23+
// Update meta information
2324
$target.dataset.offset = offset;
24-
$full.src = $link.href;
25-
}
25+
if ('' !== ($img.dataset.make ?? '')) {
26+
$meta.querySelectorAll('output').forEach($o => $o.value = $img.dataset[$o.name]);
27+
$meta.style.visibility = 'visible';
28+
} else {
29+
$meta.style.visibility = 'hidden';
30+
}
2631

27-
#navigate($target, $link, offset) {
28-
this.#meta($target.querySelector('.meta'), $link.querySelector('img').dataset);
29-
$target.dataset.offset = offset;
30-
$target.querySelector('img').src = $link.href;
32+
// Exchange images
33+
$display.src = $img.src;
34+
$target.querySelector('img.prev').src = $links.item(this.#wrap(offset - 1, $links.length)).querySelector('img').src;
35+
$target.querySelector('img.next').src = $links.item(this.#wrap(offset + 1, $links.length)).querySelector('img').src;
36+
37+
// ...then replace by larger version after a short duration
38+
this.#replace = setTimeout(() => $display.src = $link.href, 150);
3139
}
3240

3341
/** Attach all of the given elements to open the lightbox specified by the given DOM element */
@@ -38,6 +46,8 @@ class Lightbox {
3846

3947
// Keyboard navigation
4048
$target.addEventListener('keydown', e => {
49+
clearTimeout(this.#replace);
50+
4151
let offset;
4252
switch (e.key) {
4353
case 'Home': offset = 0; break;
@@ -47,27 +57,37 @@ class Lightbox {
4757
default: return;
4858
}
4959

60+
e.preventDefault();
5061
e.stopPropagation();
51-
if (offset >= 0 && offset < selector.length) {
52-
this.#navigate($target, selector.item(offset), offset);
53-
}
62+
this.#open($target, selector, offset);
5463
});
5564

5665
// Swipe left and right
5766
let x, y;
5867
const threshold = 50;
5968
$target.addEventListener('touchstart', e => {
69+
clearTimeout(this.#replace);
6070
x = e.touches[0].clientX;
6171
y = e.touches[0].clientY;
6272
});
6373
$target.addEventListener('touchmove', e => {
64-
$target.querySelector('img').style.transform = `translate(${e.changedTouches[0].clientX - x}px, 0)`;
74+
const delta = e.changedTouches[0].clientX - x;
75+
$target.querySelector('img.display').style.transform = `translate(${delta}px, 0)`;
76+
if (delta > 0) {
77+
$target.querySelector('img.next').style.visibility = null;
78+
$target.querySelector('img.prev').style.visibility = 'visible';
79+
} else {
80+
$target.querySelector('img.next').style.visibility = 'visible';
81+
$target.querySelector('img.prev').style.visibility = null;
82+
}
6583
e.cancelable && e.preventDefault();
6684
}, { passive: false });
6785
$target.addEventListener('touchend', e => {
6886
const width = e.changedTouches[0].clientX - x;
6987
const height = e.changedTouches[0].clientY - y;
70-
$target.querySelector('img').style.transform = null;
88+
$target.querySelector('img.prev').style.visibility = null;
89+
$target.querySelector('img.next').style.visibility = null;
90+
$target.querySelector('img.display').style.transform = null;
7191

7292
// Swipe was mostly vertical, ignore
7393
if (Math.abs(width) <= Math.abs(height)) return;
@@ -82,17 +102,16 @@ class Lightbox {
82102
}
83103

84104
e.stopPropagation();
85-
if (offset >= 0 && offset < selector.length) {
86-
this.#navigate($target, selector.item(offset), offset);
87-
}
105+
this.#open($target, selector, offset);
88106
});
89107

90108
let i = 0;
91109
for (const $link of selector) {
92110
const offset = i++;
93111
$link.addEventListener('click', e => {
94112
e.preventDefault();
95-
this.#open($target, $link, offset);
113+
$target.showModal();
114+
this.#open($target, selector, offset);
96115
});
97116
}
98117
}

0 commit comments

Comments
 (0)