Add lightbox to image feed
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user