11class 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