diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/config.js | 6 | ||||
| -rw-r--r-- | src/libra.css | 14 | ||||
| -rw-r--r-- | src/lightbox.js | 4 | ||||
| -rw-r--r-- | src/overlay.js | 20 | ||||
| -rw-r--r-- | src/shadow.js | 75 |
5 files changed, 75 insertions, 44 deletions
diff --git a/src/config.js b/src/config.js index eea8314..c306d8f 100644 --- a/src/config.js +++ b/src/config.js @@ -1,8 +1,10 @@ const config = { selector: '[data-libra]', margin: 50, - offset: { x: 0, y: 0 } -} + offset: { x: 0, y: 0 }, + transitionDuration: 200, // milliseconds + imageCursor: 'zoom-in', +}; function deepMerge(target, source) { for (const key in source) { diff --git a/src/libra.css b/src/libra.css index 1c7277c..f3d6e7b 100644 --- a/src/libra.css +++ b/src/libra.css @@ -5,7 +5,7 @@ width: 100%; height: 100%; padding: 1rem; - transition: opacity 0.2s ease-in-out; + transition: opacity ease-in-out; backdrop-filter: blur(2rem); background-color: rgba(255, 255, 255, 0.75); @@ -17,6 +17,16 @@ cursor: pointer; } +.libra-overlay.libra-visible { + opacity: 1; + z-index: 900; +} + +.libra-overlay.libra-hidden { + opacity: 0; + z-index: -100; +} + @media (prefers-color-scheme: dark) { .libra-overlay { background-color: rgba(0, 0, 0, 0.75); @@ -25,9 +35,9 @@ .libra-shadow { position: absolute; - transition-duration: 0.2s; transition-property: transform; cursor: pointer; + z-index: 1000; } .libra-shadow.open { diff --git a/src/lightbox.js b/src/lightbox.js index bef29eb..7bf23d9 100644 --- a/src/lightbox.js +++ b/src/lightbox.js @@ -33,8 +33,8 @@ export default class LightBox { this.images.forEach(image => { if (image.tagName !== 'IMG') return; image.id = crypto.randomUUID(); // generate unique id for each image - image.style.cursor = 'zoom-in'; - image.addEventListener('click', () => this.open(image)); // click image to open lightbox + image.style.cursor = config.imageCursor; + image.addEventListener('click', () => this.open(image)); // click image to open lightbox }); } diff --git a/src/overlay.js b/src/overlay.js index 613340c..78fb579 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -1,15 +1,13 @@ -export default class Overlay { - zIndex = 900; - zIndexHidden = -100; +import config from './config'; +export default class Overlay { constructor() { const overlay = document.createElement('div'); overlay.id = 'libra-overlay'; - overlay.classList.add('libra-overlay'); + overlay.classList.add('libra-overlay', 'libra-hidden'); - // initialized state - overlay.style.zIndex = this.zIndexHidden; - overlay.style.opacity = '0'; + // Set transition duration from config + overlay.style.transitionDuration = config.transitionDuration + 'ms'; this.element = overlay; document.body.appendChild(overlay); @@ -18,12 +16,12 @@ export default class Overlay { } show() { - this.element.style.opacity = '1'; - this.element.style.zIndex = this.zIndex; + this.element.classList.remove('libra-hidden'); + this.element.classList.add('libra-visible'); } hide() { - this.element.style.opacity = '0'; - this.element.style.zIndex = '-100'; + this.element.classList.remove('libra-visible'); + this.element.classList.add('libra-hidden'); } } diff --git a/src/shadow.js b/src/shadow.js index 9889240..50e3345 100644 --- a/src/shadow.js +++ b/src/shadow.js @@ -2,8 +2,7 @@ import Animation from './animation.js' import config from './config' export default class Shadow { - zIndex = '1000' - isOpen = false + isOpen = false; /** * ============ @@ -13,15 +12,7 @@ export default class Shadow { constructor(image) { this.original = image; - - const rect = this.original.getBoundingClientRect(); - this.originalPosition = { - top: rect.top + window.scrollY + config.offset.y, - left: rect.left + window.scrollX + config.offset.x, - width: rect.width, - height: rect.height - } - + this.fetchOriginalPosition(); this.create(image); return this; @@ -33,16 +24,30 @@ export default class Shadow { shadow.src = image.src; shadow.id = `libra-shadow-${image.id}`; shadow.classList.add('libra-shadow'); + shadow.style.transitionDuration = config.transitionDuration + 'ms'; - // style and position shadow image - shadow.style.top = `${this.originalPosition.top}px`; - shadow.style.left = `${this.originalPosition.left}px`; - shadow.style.width = `${this.originalPosition.width}px`; - shadow.style.height = `${this.originalPosition.height}px`; - shadow.style.zIndex = this.zIndex; - - document.body.appendChild(shadow); this.element = shadow; + + // set positioning and place shadow element + this.positionShadow(); + this.placeItself(); + } + + fetchOriginalPosition() { + const rect = this.original.getBoundingClientRect(); + this.originalPosition = { + top: rect.top + window.scrollY + config.offset.y, + left: rect.left + window.scrollX + config.offset.x, + width: rect.width, + height: rect.height, + }; + } + + positionShadow() { + this.element.style.top = this.originalPosition.top + 'px'; + this.element.style.left = this.originalPosition.left + 'px'; + this.element.style.width = this.originalPosition.width + 'px'; + this.element.style.height = this.originalPosition.height + 'px'; } /** @@ -55,24 +60,40 @@ export default class Shadow { this.isOpen = true; this.element.classList.add('open'); + // Update original position before animation in case viewport changed + this.fetchOriginalPosition(); + this.positionShadow(); + this.original.style.visibility = 'hidden'; if (!document.body.contains(this.element)) this.placeItself(); - Animation.transformMatrix( - this.element, - this.originalPosition, - Animation.calculateFinalState(this.element) - ); + // play animation + const animate = () => + Animation.transformMatrix( + this.element, + this.originalPosition, + Animation.calculateFinalState(this.element), + ); + + if (this.element.naturalWidth === 0 || this.element.naturalHeight === 0) { + // Wait for image to load before animating + this.element.addEventListener('load', animate, { once: true }); + } else { + // animate directly if loaded + animate(); + } } close() { this.isOpen = false; this.element.classList.remove('open'); - this.original.style.visibility = 'visible'; - Animation.resetTransformMatrix(this.element); - setTimeout(() => this.destroyItself(), 300); + // Wait for animation to complete before showing original and destroying shadow + setTimeout(() => { + this.original.style.visibility = 'visible'; + this.destroyItself(); + }, config.transitionDuration); } placeItself() { |
