Add lightbox to image feed

This commit is contained in:
pythongosssss
2023-08-12 17:17:07 +01:00
parent 80c16d62e3
commit bb67bfcc40
6 changed files with 278 additions and 2 deletions
+99
View File
@@ -0,0 +1,99 @@
.pysssss-lightbox {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 999;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
transition: opacity 0.2s;
}
.pysssss-lightbox-prev,
.pysssss-lightbox-next {
height: 60px;
padding: 10px;
display: flex;
align-items: center;
}
.pysssss-lightbox-prev:after,
.pysssss-lightbox-next:after {
border-style: solid;
border-width: 0.25em 0.25em 0 0;
display: inline-block;
height: 0.45em;
left: 0.15em;
position: relative;
top: 0.15em;
transform: rotate(-135deg) scale(0.75);
vertical-align: top;
width: 0.45em;
padding: 10px;
font-size: 20px;
margin: 0 10px 0 20px;
transition: color 0.2s;
flex-shrink: 0;
content: "";
}
.pysssss-lightbox-next:after {
transform: rotate(45deg) scale(0.75);
margin: 0 20px 0 0px;
}
.pysssss-lightbox-main {
flex: auto;
text-align: center;
}
.pysssss-lightbox-link {
display: inline-block;
position: relative;
}
.pysssss-lightbox .lds-ring {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.pysssss-lightbox-img {
max-height: 90vh;
max-width: calc(100vw - 130px);
height: auto;
object-fit: contain;
border: 3px solid white;
border-radius: 4px;
transition: opacity 0.2s;
user-select: none;
}
.pysssss-lightbox-img:hover {
border-color: dodgerblue;
}
.pysssss-lightbox-close {
font-size: 80px;
line-height: 1ch;
height: 1ch;
width: 1ch;
position: absolute;
right: 10px;
top: 10px;
padding: 5px;
}
.pysssss-lightbox-close:after {
content: "\00d7";
}
.pysssss-lightbox-close:hover,
.pysssss-lightbox-prev:hover,
.pysssss-lightbox-next:hover {
color: dodgerblue;
cursor: pointer;
}
+102
View File
@@ -0,0 +1,102 @@
import { $el } from "/scripts/ui.js";
import { addStylesheet, getUrl, loadImage } from "./utils.js";
import { createSpinner } from "./spinner.js";
addStylesheet(getUrl("lightbox.css", import.meta.url));
const $$el = (tag, name, ...args) => {
if (name) name = "-" + name;
return $el(tag + ".pysssss-lightbox" + name, ...args);
};
const ani = async (a, t, b) => {
a();
await new Promise((r) => setTimeout(r, t));
b();
};
export class Lightbox {
constructor() {
this.el = $$el("div", "", {
parent: document.body,
onclick: (e) => {
e.stopImmediatePropagation();
this.close();
},
style: {
display: "none",
opacity: 0,
},
});
this.closeBtn = $$el("div", "close", {
parent: this.el,
});
this.prev = $$el("div", "prev", {
parent: this.el,
onclick: (e) => {
this.update(-1);
e.stopImmediatePropagation();
},
});
this.main = $$el("div", "main", {
parent: this.el,
});
this.next = $$el("div", "next", {
parent: this.el,
onclick: (e) => {
this.update(1);
e.stopImmediatePropagation();
},
});
this.link = $$el("a", "link", {
parent: this.main,
target: "_blank",
});
this.spinner = createSpinner();
this.link.appendChild(this.spinner);
this.img = $$el("img", "img", {
style: {
opacity: 0,
},
parent: this.link,
onclick: (e) => {
e.stopImmediatePropagation();
},
});
}
close() {
ani(
() => (this.el.style.opacity = 0),
200,
() => (this.el.style.display = "none")
);
}
async show(images, index) {
this.images = images;
this.index = index || 0;
await this.update(0);
}
async update(shift) {
this.index += shift;
this.prev.style.visibility = this.index ? "unset" : "hidden";
this.next.style.visibility = this.index === this.images.length - 1 ? "hidden" : "unset";
const img = this.images[this.index];
this.el.style.display = "flex";
this.el.clientWidth; // Force a reflow
this.el.style.opacity = 1;
this.img.style.opacity = 0;
this.spinner.style.display = "inline-block";
await loadImage(img);
this.spinner.style.display = "none";
this.link.href = img;
this.img.src = img;
this.img.style.opacity = 1;
}
}
export const lightbox = new Lightbox();
+35
View File
@@ -0,0 +1,35 @@
.lds-ring {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border: 5px solid #fff;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #fff transparent transparent transparent;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
+9
View File
@@ -0,0 +1,9 @@
import { addStylesheet, getUrl } from "./utils.js";
addStylesheet(getUrl("spinner.css", import.meta.url));
export function createSpinner() {
const div = document.createElement("div");
div.innerHTML = `<div class="lds-ring"><div></div><div></div><div></div><div></div></div>`;
return div.firstElementChild;
}
+27
View File
@@ -0,0 +1,27 @@
import { $el } from "/scripts/ui.js";
export function addStylesheet(url) {
$el("link", {
parent: document.head,
rel: "stylesheet",
type: "text/css",
href: url.startsWith("http") ? url : getUrl(url),
});
}
export function getUrl(path, baseUrl) {
if (baseUrl) {
return new URL(path, baseUrl).toString();
} else {
return new URL("../" + path, import.meta.url).toString();
}
}
export async function loadImage(url) {
return new Promise((res, rej) => {
const img = new Image();
img.onload = res;
img.onerror = rej;
img.src = url;
});
}