135 lines
3.0 KiB
JavaScript
135 lines
3.0 KiB
JavaScript
class LayoutEngine {
|
|
rects = [];
|
|
|
|
gap = 6.28333 * 2;
|
|
baseHeight = 200;
|
|
// maxHeight = 320;
|
|
viewportWidth = 1024;
|
|
|
|
constructor() {
|
|
|
|
}
|
|
|
|
// Register a new rectangle with an aspect ratio of ar at index
|
|
insert(ar=1, index=-1) {
|
|
const rect = {
|
|
ar: ar,
|
|
};
|
|
if (index == -1) {
|
|
this.rects.push(rect);
|
|
} else {
|
|
this.rects.splice(index, 0, rect);
|
|
}
|
|
}
|
|
|
|
// Unregister a new rectangle at index
|
|
pop(index=-1) {
|
|
// TODO
|
|
}
|
|
|
|
// Generate a list of heights for each rectangle
|
|
calculate() {
|
|
let heights = [];
|
|
|
|
let currentWidth = this.gap;
|
|
let currentIndex = 0;
|
|
|
|
// 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;
|
|
|
|
// 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;
|
|
|
|
// Scale up every previous rectangle
|
|
const rectHeight = this.baseHeight * widthScale;
|
|
for (; currentIndex < index; currentIndex++) {
|
|
heights.push(rectHeight);
|
|
}
|
|
currentWidth = this.gap;
|
|
}
|
|
|
|
currentWidth += rectWidth;
|
|
}
|
|
|
|
// Set remainder to a decent height
|
|
for (; currentIndex < this.rects.length; currentIndex++) {
|
|
heights.push(this.baseHeight)
|
|
}
|
|
|
|
return heights;
|
|
}
|
|
|
|
/*
|
|
* Code for a future implementation that emits events
|
|
|
|
update() {
|
|
console.debug("updating layout");
|
|
|
|
}
|
|
|
|
// Schedule an update at maximum 10Hz
|
|
scheduleUpdate() {
|
|
if (this.scheduledUpdate) {
|
|
return;
|
|
}
|
|
this.scheduledUpdate = setTimeout(() => {
|
|
this.scheduledUpdate = null;
|
|
this.update();
|
|
}, 100);
|
|
}
|
|
*/
|
|
}
|
|
|
|
export default class Gallery {
|
|
constructor(ele) {
|
|
this.ele = ele;
|
|
this.layoutEngine = new LayoutEngine();
|
|
|
|
this.registerViewport();
|
|
this.registerInitial();
|
|
this.draw();
|
|
}
|
|
|
|
// Inform our engine of viewport width
|
|
registerViewport() {
|
|
window.addEventListener('resize', () => {
|
|
if (this.layoutEngine.viewportWidth == window.innerWidth) {
|
|
return;
|
|
}
|
|
this.layoutEngine.viewportWidth = window.innerWidth;
|
|
//this.layoutEngine.scheduleUpdate();
|
|
});
|
|
}
|
|
|
|
// Register initial elements
|
|
registerInitial() {
|
|
const galleryItemEles = this.ele.querySelectorAll('.gallery-item');
|
|
galleryItemEles.forEach(ele => {
|
|
const ar = ele.dataset.ar;
|
|
this.layoutEngine.insert(ar);
|
|
});
|
|
}
|
|
|
|
// Calculate heights and draw them
|
|
draw() {
|
|
const heights = 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}`);
|
|
return;
|
|
}
|
|
ele.querySelectorAll("img").forEach(ele => {
|
|
ele.style.height = `${height}px`;
|
|
});
|
|
});
|
|
}
|
|
}
|