@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
68 lines (67 loc) • 2.25 kB
JavaScript
/**
* Calculates the width/height of the images to fit in the layout.
*
* Currently does not mutate the cfg.images array, but DOES mutate individual images with .fitWidth, .fitHeight properties.
*
* @experimental
*/
export class ImageFitter {
constructor(cfg) {
this.cfg = {
maxHeight: 300,
margin: 8,
...cfg,
};
this.resizeObserver = new ResizeObserver(entries => this.update(entries));
this.resizeObserver.observe(cfg.containerElement);
}
cfg;
resizeObserver;
containerWidth = -1;
stop() {
this.resizeObserver.disconnect();
}
update(entries) {
const width = Math.floor(entries[0].contentRect.width);
if (width === this.containerWidth)
return; // we're only interested in width changes
this.containerWidth = width;
console.log(`resize ${width}`);
this.doLayout(this.cfg.images);
this.cfg.onChange(this.cfg.images);
}
doLayout(imgs) {
if (imgs.length === 0)
return; // nothing to do
const { maxHeight } = this.cfg;
let imgNodes = imgs.slice(0);
w: while (imgNodes.length > 0) {
let slice;
let h;
for (let i = 1; i <= imgNodes.length; i++) {
slice = imgNodes.slice(0, i);
h = this.getHeigth(slice);
if (h < maxHeight) {
this.setHeight(slice, h);
imgNodes = imgNodes.slice(i);
continue w;
}
}
this.setHeight(slice, Math.min(maxHeight, h));
break;
}
}
getHeigth(images) {
const width = this.containerWidth - images.length * this.cfg.margin;
let r = 0;
images.forEach(img => (r += img.aspectRatio));
return width / r; // have to round down because Firefox will automatically roundup value with number of decimals > 3
}
// mutates/sets images' fitWidth, fitHeight properties
setHeight(images, height) {
images.forEach(img => {
img.fitWidth = Math.floor(height * img.aspectRatio);
img.fitHeight = Math.floor(height);
});
}
}