@s2ui/justified-gallery
Version:
A justifed gallery by s2ui.
59 lines (52 loc) • 1.56 kB
text/typescript
import { Photo } from "../types";
// Determines last row alignment (if not justified)
function calculateLastRowOffset(
alignment: "left" | "center" | "right",
containerWidth: number,
row: Photo[],
adjustedRowHeight: number,
gap: number
): number {
const rowWidth =
row.reduce(
(sum, photo) => sum + Math.round(adjustedRowHeight * photo.ratio),
0
) +
(row.length - 1) * gap;
return alignment === "center"
? (containerWidth - rowWidth) / 2
: alignment === "right"
? containerWidth - rowWidth
: 0;
}
// Efficiently splits images into justified rows
function splitIntoRows(
photos: Photo[],
containerWidth: number,
targetRowHeight: number,
gap: number
): number[][] {
let rowWidth = 0,
start = 0;
const rowLen: number[][] = [];
photos.forEach((photo, i) => {
rowWidth += photo.ratio * targetRowHeight;
if (
rowWidth + (i - start) * gap > containerWidth ||
i === photos.length - 1
) {
rowLen.push([start, i, rowWidth]);
start = i + 1;
rowWidth = 0;
}
});
return rowLen;
}
// Optimized Image Aspect Ratio Calculation
async function loadImageAspectRatio(img: HTMLImageElement): Promise<Photo> {
if (!img.complete || img.naturalWidth === 0 || img.naturalHeight === 0) {
await img.decode().catch(() => {}); // Avoids blocking on error
}
return { img, ratio: img.naturalWidth / img.naturalHeight };
}
export { calculateLastRowOffset, splitIntoRows, loadImageAspectRatio };