summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEltrac <hi@guhub.cn>2025-09-18 18:54:52 +0800
committerGitHub <noreply@github.com>2025-09-18 18:54:52 +0800
commit2afcb97eeab22b2ae7b3dc697e0cfab318aa0722 (patch)
tree9a101095d390ea3a336427e295210d11eab824f3 /src
parentd26860e424227003122b077c8d2e76223d760e67 (diff)
parent7113655e17a38d9d73cffe469c28b6ddf5b0636f (diff)
Merge pull request #2 from QuarkPixel/master
Fix Multiple UI/UX Issues and Improve Configuration Management
Diffstat (limited to 'src')
-rw-r--r--src/config.js6
-rw-r--r--src/libra.css14
-rw-r--r--src/lightbox.js4
-rw-r--r--src/overlay.js20
-rw-r--r--src/shadow.js75
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() {