Add enlarged photo view
parent
b6613ea168
commit
a9a55c3ef9
|
@ -1,13 +1,21 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Faustina:wght@400;550&display=swap');
|
||||
|
||||
/*
|
||||
* Units
|
||||
*/
|
||||
|
||||
:root {
|
||||
--spacing: 0.375rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Typography
|
||||
*/
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Faustina:wght@400;550&display=swap');
|
||||
|
||||
html {
|
||||
font-family: 'Faustina', serif;
|
||||
font-weight: 400;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -19,6 +27,9 @@ body {
|
|||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 550;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: greyscale;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -60,7 +71,7 @@ h4, h5, h6 {
|
|||
*/
|
||||
|
||||
nav ul {
|
||||
padding: calc(0.7862rem / 2);
|
||||
padding: var(--spacing);
|
||||
margin: 0;
|
||||
|
||||
list-style: none;
|
||||
|
@ -69,12 +80,16 @@ nav ul {
|
|||
}
|
||||
|
||||
nav ul li {
|
||||
margin: calc(0.7862rem / 2);
|
||||
margin: var(--spacing);
|
||||
|
||||
display: block;
|
||||
|
||||
font-size: 0.9em;
|
||||
letter-spacing: 0.07em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1rem 0.7862rem;
|
||||
padding: 1rem calc(var(--spacing) * 2);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
.enlarge {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
|
||||
transition: 0.5s opacity;
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
}
|
||||
|
||||
.enlarge-close, .enlarge-zoom-controls {
|
||||
font-size: 0.9em;
|
||||
letter-spacing: 0.07em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.enlarge-close {
|
||||
padding: calc(var(--spacing) * 2);
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.enlarge-zoom-controls {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.enlarge-zoom-in,
|
||||
.enlarge-zoom-out {
|
||||
padding: calc(var(--spacing) * 2);
|
||||
|
||||
display: inline-block;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.enlarge-contents {
|
||||
position: relative;
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.enlarge-photo {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.enlarge-preload {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -1,25 +1,47 @@
|
|||
:root {
|
||||
--gallery-spacing: var(--spacing);
|
||||
--gallery-base-height: 240px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
:root {
|
||||
--gallery-base-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
:root {
|
||||
--gallery-base-height: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
:root {
|
||||
--gallery-base-height: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
.gallery {
|
||||
padding: calc(0.7862rem / 2);
|
||||
padding: var(--gallery-spacing);
|
||||
padding-right: 0;
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
margin-right: -0.5px;
|
||||
}
|
||||
|
||||
.gallery-item picture {
|
||||
margin: calc(0.7862rem / 2);
|
||||
margin: var(--gallery-spacing);
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
.gallery-item img {
|
||||
display: block;
|
||||
height: 280px; /* 320px is native size */
|
||||
height: calc(var(--gallery-base-height) * 1.2); /* 320px is native size on desktop, 160px is native size on mobile */
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.gallery-item img {
|
||||
height: 160px; /* 160px is native size */
|
||||
width: auto;
|
||||
}
|
||||
object-fit: cover;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.esm.browser.js';
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
export default class Enlarge {
|
||||
/*
|
||||
prefix = '#view-';
|
||||
ele = null;
|
||||
closeEle = null;
|
||||
contentsEle = null;
|
||||
photoEle = null;
|
||||
preloadEle = null;
|
||||
|
||||
zoom = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
mouseDown = false;
|
||||
|
||||
preservedScroll = {};
|
||||
preloadEles = {};
|
||||
|
||||
openCloseTimeout = null;
|
||||
*/
|
||||
|
||||
constructor(ele, prefix='#view-') {
|
||||
this.prefix = prefix;
|
||||
this.zoom = 0;
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.mouseDown = false;
|
||||
this.preservedScroll = {};
|
||||
this.preloadEles = {};
|
||||
|
||||
this.constructWrapper();
|
||||
this.bindZoomControls();
|
||||
|
||||
this.bindSize();
|
||||
this.bindWindow();
|
||||
|
||||
this.bindTouch();
|
||||
this.bindDrag();
|
||||
this.bindScroll();
|
||||
|
||||
ele.appendChild(this.ele);
|
||||
}
|
||||
|
||||
constructWrapper() {
|
||||
this.ele = document.createElement('div');
|
||||
this.ele.classList.add('enlarge');
|
||||
this.ele.style.display = 'none';
|
||||
this.ele.style.opacity = '0';
|
||||
|
||||
this.zoomControlsEle = document.createElement('div');
|
||||
this.zoomControlsEle.classList.add('enlarge-zoom-controls');
|
||||
this.ele.appendChild(this.zoomControlsEle);
|
||||
|
||||
this.zoomInEle = document.createElement('a');
|
||||
this.zoomInEle.classList.add('enlarge-zoom-in');
|
||||
this.zoomInEle.innerText = 'Zoom +';
|
||||
this.zoomInEle.href = '#';
|
||||
this.zoomControlsEle.appendChild(this.zoomInEle);
|
||||
|
||||
this.zoomOutEle = document.createElement('a');
|
||||
this.zoomOutEle.classList.add('enlarge-zoom-out');
|
||||
this.zoomOutEle.innerText = 'Zoom -';
|
||||
this.zoomOutEle.href = '#';
|
||||
this.zoomControlsEle.appendChild(this.zoomOutEle);
|
||||
|
||||
this.closeEle = document.createElement('a');
|
||||
this.closeEle.classList.add('enlarge-close');
|
||||
this.closeEle.innerText = 'Close';
|
||||
this.closeEle.href = '#';
|
||||
this.ele.appendChild(this.closeEle);
|
||||
|
||||
this.contentsEle = document.createElement('div');
|
||||
this.contentsEle.classList.add('enlarge-contents');
|
||||
this.ele.appendChild(this.contentsEle);
|
||||
|
||||
this.preloadEle = document.createElement('img');
|
||||
this.preloadEle.classList.add('enlarge-preload');
|
||||
this.preloadEle.src = '';
|
||||
this.contentsEle.appendChild(this.preloadEle);
|
||||
|
||||
this.photoEle = document.createElement('img');
|
||||
this.photoEle.classList.add('enlarge-photo');
|
||||
this.photoEle.src = '';
|
||||
this.contentsEle.appendChild(this.photoEle);
|
||||
}
|
||||
|
||||
bindZoomControls() {
|
||||
this.zoomInEle.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.zoomAt(this.zoom * +0.25, this.viewportSize.w / 2, this.viewportSize.h / 2);
|
||||
this.draw();
|
||||
}, false);
|
||||
this.zoomOutEle.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.zoomAt(this.zoom * -0.25, this.viewportSize.w / 2, this.viewportSize.h / 2);
|
||||
this.draw();
|
||||
}, false);
|
||||
}
|
||||
|
||||
bindSize() {
|
||||
this.preloadEle.addEventListener('load', this.updateSize.bind(this), false);
|
||||
this.photoEle.addEventListener('load', this.updateSize.bind(this), false);
|
||||
}
|
||||
|
||||
bindWindow() {
|
||||
window.addEventListener('resize', this.updateSize.bind(this), false);
|
||||
}
|
||||
|
||||
bindTouch() {
|
||||
|
||||
}
|
||||
|
||||
bindDrag() {
|
||||
this.ele.addEventListener('pointerdown', (e) => {
|
||||
this.mouseDown = true;
|
||||
this.contentsEle.style.cursor = 'grabbing';
|
||||
}, false);
|
||||
this.ele.addEventListener('pointermove', (e) => {
|
||||
e.preventDefault();
|
||||
if (this.mouseDown) {
|
||||
this.pan(e.movementX, e.movementY);
|
||||
this.draw();
|
||||
}
|
||||
}, false);
|
||||
this.ele.addEventListener('pointerup', (e) => {
|
||||
this.mouseDown = false;
|
||||
this.contentsEle.style.cursor = 'grab';
|
||||
}, false);
|
||||
}
|
||||
|
||||
bindScroll() {
|
||||
this.ele.addEventListener('wheel', (e) => {
|
||||
e.preventDefault();
|
||||
// Assume full screen
|
||||
this.zoomAt(e.deltaY * -0.002 * this.zoom, e.clientX, e.clientY);
|
||||
this.draw();
|
||||
}, false);
|
||||
}
|
||||
|
||||
openHash() {
|
||||
const hash = window.location.hash;
|
||||
if (!hash.startsWith(this.prefix)) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
const id = hash.replace(this.prefix, '');
|
||||
this.open(id);
|
||||
}
|
||||
|
||||
watchHash() {
|
||||
window.addEventListener('hashchange', this.openHash.bind(this), false);
|
||||
this.openHash();
|
||||
}
|
||||
|
||||
setPreload(id) {
|
||||
// https://caniuse.com/#feat=mdn-javascript_operators_optional_chaining
|
||||
let preload = null;
|
||||
if (this.preloadEles[id]) {
|
||||
preload = this.preloadEles[id].currentSrc;
|
||||
}
|
||||
|
||||
if (preload) {
|
||||
this.preloadEle.src = preload;
|
||||
this.preloadEle.display = 'block';
|
||||
} else {
|
||||
this.preloadEle.src = '';
|
||||
this.preloadEle.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
setPhoto(id) {
|
||||
this.zoom = 0;
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
if (id) {
|
||||
this.photoEle.src = id;
|
||||
} else {
|
||||
this.photoEle.src = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Update the size of the image
|
||||
updateSize() {
|
||||
this.contentsEle.style.width = `${this.size.w}px`;
|
||||
this.contentsEle.style.height = `${this.size.h}px`;
|
||||
console.debug(`updated size to ${this.size.w}, ${this.size.h}`);
|
||||
this.draw();
|
||||
}
|
||||
|
||||
draw() {
|
||||
this.zoom = Enlarge.clampZoom(this.zoom, this.baseScale);
|
||||
this.y = Enlarge.clamp(this.y, this.viewportSize.h, this.size.h * this.zoom * this.baseScale);
|
||||
this.x = Enlarge.clamp(this.x, this.viewportSize.w, this.size.w * this.zoom * this.baseScale);
|
||||
this.contentsEle.style.transform = `translate3d(${this.x}px, ${this.y}px, 0) scale(${this.zoom * this.baseScale})`;
|
||||
}
|
||||
|
||||
pan(x, y) {
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
}
|
||||
|
||||
zoomAt(by, x, y) {
|
||||
const newZoom = Enlarge.clampZoom(this.zoom + by, this.baseScale);
|
||||
// Change ratio
|
||||
const changedRatio = newZoom / this.zoom;
|
||||
// Rescale existing offset
|
||||
this.x *= changedRatio;
|
||||
this.y *= changedRatio;
|
||||
// Rescale cursor offset
|
||||
this.x -= (changedRatio - 1) * x;
|
||||
this.y -= (changedRatio - 1) * y;
|
||||
// Update new zoom
|
||||
this.zoom = newZoom;
|
||||
}
|
||||
|
||||
static clamp(translation, viewport, size) {
|
||||
if (viewport > size) {
|
||||
// Ensure centred
|
||||
return (viewport - size) / 2;
|
||||
}
|
||||
if (translation > 0) {
|
||||
// Start side
|
||||
return 0;
|
||||
} else if (translation < (viewport - size)) {
|
||||
// End side
|
||||
return viewport - size;
|
||||
} else {
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
|
||||
static clampZoom(zoom, baseScale) {
|
||||
// Cap high resolution scale at 1.5
|
||||
// Cap low resolution zoom at 1.5
|
||||
const maxZoom = Math.max(1.5 / baseScale, 1.5);
|
||||
return Math.min(Math.max(zoom, 1), maxZoom);
|
||||
}
|
||||
|
||||
get baseScale() {
|
||||
if (this.aspectRatio.ar > this.viewportSize.w / this.viewportSize.h) {
|
||||
// Photo is clamped to width
|
||||
return this.viewportSize.w / this.size.w;
|
||||
} else {
|
||||
// Photo is clamped to height
|
||||
return this.viewportSize.h / this.size.h;
|
||||
}
|
||||
}
|
||||
|
||||
get viewportSize() {
|
||||
return {
|
||||
w: this.ele.offsetWidth,
|
||||
h: this.ele.offsetHeight,
|
||||
};
|
||||
}
|
||||
|
||||
get aspectRatio() {
|
||||
return {
|
||||
ar: this.size.w / this.size.h,
|
||||
};
|
||||
}
|
||||
|
||||
get size() {
|
||||
if (this.photoEle.naturalWidth > 0) {
|
||||
return {
|
||||
w: this.photoEle.naturalWidth,
|
||||
h: this.photoEle.naturalHeight,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
w: this.preloadEle.naturalWidth,
|
||||
h: this.preloadEle.naturalHeight,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
open(id) {
|
||||
this.setPreload(id);
|
||||
this.setPhoto(id);
|
||||
|
||||
this.show();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setPreload(null);
|
||||
this.setPhoto(null);
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
// Capture scroll position
|
||||
this.preservedScroll.x = window.scrollX;
|
||||
this.preservedScroll.y = window.scrollY;
|
||||
|
||||
this.ele.style.display = 'block';
|
||||
this.ele.style.pointerEvents = 'all';
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
if (this.openCloseTimeout) {
|
||||
clearTimeout(this.openCloseTimeout);
|
||||
}
|
||||
this.openCloseTimeout = setTimeout(() => {
|
||||
this.ele.style.opacity = '1.0';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
hide() {
|
||||
// Restore scroll position
|
||||
window.scrollTo(this.preservedScroll.x, this.preservedScroll.y);
|
||||
|
||||
this.ele.style.opacity = '0';
|
||||
this.ele.style.pointerEvents = 'none';
|
||||
document.body.style.overflow = 'visible';
|
||||
|
||||
if (this.openCloseTimeout) {
|
||||
clearTimeout(this.openCloseTimeout);
|
||||
}
|
||||
this.openCloseTimeout = setTimeout(() => {
|
||||
this.ele.style.pointerEvents = 'all';
|
||||
this.ele.style.display = 'none';
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
register(id, ele) {
|
||||
// Simple solution
|
||||
//ele.href = this.prefix + id;
|
||||
// Semantic preservation solution
|
||||
ele.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
window.location.hash = this.prefix + id;
|
||||
}, false);
|
||||
}
|
||||
|
||||
registerPreload(id, ele) {
|
||||
this.preloadEles[id] = ele;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
class LayoutEngine {
|
||||
/*
|
||||
rects = [];
|
||||
|
||||
gap = 6.28333 * 2;
|
||||
baseHeight = 200;
|
||||
gap = 12;
|
||||
baseHeight = 100;
|
||||
// maxHeight = 320;
|
||||
viewportWidth = 1024;
|
||||
*/
|
||||
|
||||
constructor() {
|
||||
|
||||
this.rects = [];
|
||||
this.gap = 12;
|
||||
this.baseHeight = 100;
|
||||
this.viewportWidth = 1024;
|
||||
}
|
||||
|
||||
// Register a new rectangle with an aspect ratio of ar at index
|
||||
|
@ -27,29 +32,34 @@ class LayoutEngine {
|
|||
// TODO
|
||||
}
|
||||
|
||||
// Generate a list of heights for each rectangle
|
||||
// Generate a list of dimensions for each rectangle
|
||||
calculate() {
|
||||
let heights = [];
|
||||
let dimensions = [];
|
||||
|
||||
let currentWidth = this.gap;
|
||||
let currentIndex = 0;
|
||||
|
||||
let lastHeight = this.baseHeight * 1.2;
|
||||
|
||||
// Behave like a browser: try to fit as many in a row with baseHeight
|
||||
for (let index = 0; index < this.rects.length; index++) {
|
||||
// Get the next width to add
|
||||
const rectWidth = Math.ceil(this.baseHeight * this.rects[index].ar) + this.gap;
|
||||
const rectWidth = (this.baseHeight * this.rects[index].ar) + this.gap;
|
||||
|
||||
// If the next width is too wide, resolve the current rectangles
|
||||
console.debug(currentWidth, rectWidth);
|
||||
if (currentWidth + rectWidth > this.viewportWidth) {
|
||||
const gapTotal = this.gap * (index - currentIndex + 1);
|
||||
const widthScale = (this.viewportWidth - gapTotal) / (currentWidth - gapTotal);
|
||||
const heightScale = widthScale;
|
||||
const scale = (this.viewportWidth - gapTotal) / (currentWidth - gapTotal);
|
||||
|
||||
const rectHeight = this.baseHeight * scale;
|
||||
lastHeight = rectHeight;
|
||||
|
||||
// Scale up every previous rectangle
|
||||
const rectHeight = this.baseHeight * widthScale;
|
||||
for (; currentIndex < index; currentIndex++) {
|
||||
heights.push(rectHeight);
|
||||
dimensions.push({
|
||||
h: rectHeight,
|
||||
w: rectHeight * this.rects[currentIndex].ar,
|
||||
})
|
||||
}
|
||||
currentWidth = this.gap;
|
||||
}
|
||||
|
@ -59,17 +69,20 @@ class LayoutEngine {
|
|||
|
||||
// Set remainder to a decent height
|
||||
for (; currentIndex < this.rects.length; currentIndex++) {
|
||||
heights.push(this.baseHeight)
|
||||
dimensions.push({
|
||||
h: lastHeight,
|
||||
w: lastHeight * this.rects[currentIndex].ar,
|
||||
})
|
||||
}
|
||||
|
||||
return heights;
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
/*
|
||||
* Code for a future implementation that emits events
|
||||
|
||||
update() {
|
||||
console.debug("updating layout");
|
||||
console.debug('updating layout');
|
||||
|
||||
}
|
||||
|
||||
|
@ -91,44 +104,69 @@ export default class Gallery {
|
|||
this.ele = ele;
|
||||
this.layoutEngine = new LayoutEngine();
|
||||
|
||||
this.grabGap();
|
||||
this.grabBaseHeight();
|
||||
this.grabViewportWidth();
|
||||
|
||||
this.registerViewport();
|
||||
this.registerInitial();
|
||||
this.draw();
|
||||
}
|
||||
|
||||
// Extract gap values
|
||||
grabGap() {
|
||||
// Simple implementation to guess from padding values
|
||||
const px = window.getComputedStyle(this.ele).getPropertyValue('padding-left').replace('px', '');
|
||||
this.layoutEngine.gap = parseInt(px) * 2 || 0;
|
||||
}
|
||||
|
||||
// Extract baseHeight
|
||||
grabBaseHeight() {
|
||||
const px = window.getComputedStyle(this.ele).getPropertyValue('--gallery-base-height').replace('px', '');
|
||||
this.layoutEngine.baseHeight = parseInt(px) || 0;
|
||||
}
|
||||
|
||||
// Extract viewport width
|
||||
grabViewportWidth() {
|
||||
this.layoutEngine.viewportWidth = window.innerWidth;
|
||||
}
|
||||
|
||||
// Inform our engine of viewport width
|
||||
registerViewport() {
|
||||
window.addEventListener('resize', () => {
|
||||
if (this.layoutEngine.viewportWidth == window.innerWidth) {
|
||||
return;
|
||||
}
|
||||
this.layoutEngine.viewportWidth = window.innerWidth;
|
||||
this.grabBaseHeight();
|
||||
this.grabViewportWidth();
|
||||
//this.layoutEngine.scheduleUpdate();
|
||||
this.draw();
|
||||
});
|
||||
}
|
||||
|
||||
// Register initial elements
|
||||
registerInitial() {
|
||||
const galleryItemEles = this.ele.querySelectorAll('.gallery-item');
|
||||
galleryItemEles.forEach(ele => {
|
||||
for (const ele of galleryItemEles) {
|
||||
const ar = ele.dataset.ar;
|
||||
this.layoutEngine.insert(ar);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate heights and draw them
|
||||
// Calculate dimensions and draw them
|
||||
draw() {
|
||||
const heights = this.layoutEngine.calculate();
|
||||
const dimensions = this.layoutEngine.calculate();
|
||||
const galleryItemEles = this.ele.querySelectorAll('.gallery-item');
|
||||
galleryItemEles.forEach((ele, index) => {
|
||||
const height = heights[index];
|
||||
if (!height) {
|
||||
console.error(`Missing height for element at ${index}`);
|
||||
for (const [index, ele] of galleryItemEles.entries()) {
|
||||
const dimension = dimensions[index];
|
||||
if (!dimension) {
|
||||
console.error(`Missing dimensions for element at ${index}`);
|
||||
return;
|
||||
}
|
||||
ele.querySelectorAll("img").forEach(ele => {
|
||||
ele.style.height = `${height}px`;
|
||||
});
|
||||
});
|
||||
for (const imgEle of ele.querySelectorAll('img')) {
|
||||
imgEle.style.height = `${dimension.h}px`;
|
||||
imgEle.style.width = `${dimension.w}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
import Gallery from './gallery.js';
|
||||
import Enlarge from './enlarge.js';
|
||||
|
||||
// Bind elements
|
||||
|
||||
const galleryEle = document.querySelector("main.gallery");
|
||||
const galleryEle = document.querySelector('main.gallery');
|
||||
const gallery = new Gallery(galleryEle);
|
||||
|
||||
const enlargeEles = document.querySelectorAll('[data-enlarge]');
|
||||
const enlarge = new Enlarge(document.body);
|
||||
for (const ele of enlargeEles) {
|
||||
enlarge.register(ele.dataset.enlarge, ele);
|
||||
}
|
||||
|
||||
const enlargePreloadEles = document.querySelectorAll('[data-enlarge-preload-for]');
|
||||
for (const ele of enlargePreloadEles) {
|
||||
enlarge.registerPreload(ele.dataset.enlargePreloadFor, ele);
|
||||
}
|
||||
|
||||
enlarge.watchHash();
|
||||
|
||||
// Debug
|
||||
|
||||
window.g = gallery;
|
||||
window.e = enlarge;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<title>Photos</title>
|
||||
<link rel="stylesheet" href="https://local1:2020/css/base.css">
|
||||
<link rel="stylesheet" href="https://local1:2020/css/gallery.css">
|
||||
<link rel="stylesheet" href="https://local1:2020/css/enlarge.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
|
@ -20,6 +21,7 @@
|
|||
<main class="gallery">
|
||||
{{ range (datasource "samples") }}
|
||||
<div class="gallery-item preloaded-thumbnail" data-ar="{{ div (index . 1) (index . 2) }}">
|
||||
<a href="sample/{{ index . 0 }}" data-enlarge="sample/{{ index . 0 }}">
|
||||
<picture>
|
||||
<source srcset="
|
||||
sample/{{ index . 0 | replaceAll ".jpg" "_320.webp"}} 2x,
|
||||
|
@ -41,7 +43,8 @@
|
|||
alt=""
|
||||
width="{{ div (index . 1) (index . 2) | mul 320 }}"
|
||||
height="320"
|
||||
loading="lazy">
|
||||
loading="lazy"
|
||||
data-enlarge-preload-for="sample/{{ index . 0 }}">
|
||||
</picture>
|
||||
<picture class="preloaded-thumbnail-image">
|
||||
<source srcset="
|
||||
|
@ -66,6 +69,7 @@
|
|||
height="320"
|
||||
>
|
||||
</picture>
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</main>
|
||||
|
|
|
@ -15,17 +15,20 @@ done
|
|||
for size in ${presizes[*]}; do
|
||||
for f in sample/*unsplash.jpg; do
|
||||
echo "Compressing thumbnail $f at $size"
|
||||
gm convert $f -resize x$size -compress jpeg -quality 60 ${f%.jpg}_pre$size.jpg
|
||||
gm convert $f -resize x$size -compress webp -quality 40 ${f%.jpg}_pre$size.webp
|
||||
if [ -f "${f%.jpg}_pre$size.jpg" ]; then
|
||||
continue
|
||||
fi
|
||||
gm convert $f -resize x$size -compress jpeg -quality 40 ${f%.jpg}_pre$size.jpg
|
||||
gm convert $f -resize x$size -compress webp -quality 35 ${f%.jpg}_pre$size.webp
|
||||
done
|
||||
done
|
||||
for size in ${sizes[*]}; do
|
||||
for f in sample/*unsplash.jpg; do
|
||||
echo "Compressing $f at $size"
|
||||
#if [ -f "${f%.jpg}_$size.jpg" ]; then
|
||||
# continue
|
||||
#fi
|
||||
gm convert $f -resize x$size -compress jpeg -quality 70 ${f%.jpg}_$size.jpg
|
||||
gm convert $f -resize x$size -compress webp -quality 65 ${f%.jpg}_$size.webp
|
||||
if [ -f "${f%.jpg}_$size.jpg" ]; then
|
||||
continue
|
||||
fi
|
||||
gm convert $f -resize x$size -compress jpeg -quality 75 ${f%.jpg}_$size.jpg
|
||||
gm convert $f -resize x$size -compress webp -quality 70 ${f%.jpg}_$size.webp
|
||||
done
|
||||
done
|
||||
|
|
Loading…
Reference in New Issue