1
0
Fork 0
photos/web/shared/js/gallery.js

124 lines
3.2 KiB
JavaScript

export default class Gallery {
constructor(ele) {
this.ele = ele;
this.gap = 12;
this.baseHeight = 100;
this.viewportWidth = 1024;
this.grabGap();
this.grabBaseHeight();
this.grabViewportWidth();
this.registerViewport();
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.gap = parseInt(px) * 2 || 0;
}
// Extract baseHeight
grabBaseHeight() {
const px = window.getComputedStyle(this.ele).getPropertyValue('--gallery-base-height').replace('px', '');
this.baseHeight = parseInt(px) || 0;
}
// Extract viewport width
grabViewportWidth() {
this.viewportWidth = this.ele.clientWidth;
}
// Inform our engine of viewport width
registerViewport() {
window.addEventListener('resize', () => {
if (this.viewportWidth == window.innerWidth) {
return;
}
this.recompute();
});
window.addEventListener('load', () => {
this.recompute();
});
setInterval(this.recompute.bind(this), 500);
}
recompute() {
this.grabGap();
this.grabBaseHeight();
this.grabViewportWidth();
this.draw();
}
// Calculate dimensions and draw them
draw() {
const elements = this.ele.querySelectorAll('.gallery-item');
const rects = Array.from(elements).map(element => parseFloat(element.dataset.ar));
const dimensions = this.layout(rects);
const galleryItemEles = this.ele.querySelectorAll('.gallery-item');
for (const [index, ele] of galleryItemEles.entries()) {
const dimension = dimensions[index];
if (!dimension) {
console.error(`Missing dimensions for element at ${index}`);
return;
}
for (const imgEle of ele.querySelectorAll('img')) {
imgEle.style.height = `${Math.floor(dimension.h)}px`;
imgEle.style.width = `${dimension.w}px`;
}
}
}
// Perform layout
layout(rects) {
let dimensions = [];
let currentWidth = this.gap;
let startIndex = 0;
// Behave like a browser: try to fit as many in a row with baseHeight
for (let index = 0; index < rects.length; index++) {
// Add this rectangle width
const currentRectWidth = (this.baseHeight * rects[index]);
currentWidth += currentRectWidth + this.gap;
if (currentWidth > this.viewportWidth) {
console.error("TODO: fix cases where viewport smaller than image");
break;
}
// Get the next width to add
const hasNextRect = index + 1 < rects.length;
const nextRectWidth = hasNextRect ? (this.baseHeight * rects[index+1]) : 0;
const nextRectOverflow = currentWidth + nextRectWidth + this.gap > this.viewportWidth;
// If the next width is too wide, resolve the current rectangles
if (!hasNextRect || nextRectOverflow) {
const gapTotal = this.gap * (index - startIndex + 2);
let scale = (this.viewportWidth - gapTotal) / (currentWidth - gapTotal);
if (!hasNextRect) {
scale = Math.min(1.8, scale);
}
const rectHeight = this.baseHeight * scale;
// Scale up every previous rectangle
for (; startIndex <= index; startIndex++) {
dimensions.push({
h: rectHeight,
w: rectHeight * rects[startIndex],
})
}
currentWidth = this.gap;
}
}
return dimensions;
}
}