Last active
June 9, 2024 03:24
-
-
Save tylermercer/8bce964a5ca830efb5754aefb73a40d4 to your computer and use it in GitHub Desktop.
Responsive Image Sizes Calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This function can be applied to the results in the above's `computeSizes` to output the results as a `widths` and `sizes` attribute | |
* to be used in an Astro project | |
*/ | |
const convertToAstroCode = (obj) => { | |
const result = {}; | |
Object.keys(obj).forEach((key) => { | |
const measurements = obj[key]; | |
measurements.sort((a, b) => a.window - b.window); | |
const widths = measurements.map( | |
(measurement) => measurement.measurement, | |
); | |
// Generate the sizes string | |
const sizes = measurements | |
.map((measurement, index) => { | |
if (index === measurements.length - 1) { | |
return `${measurement.measurement}px`; | |
} else { | |
return `(max-width: ${measurement.window}px) ${measurement.measurement}px`; | |
} | |
}) | |
.join(", "); | |
// Create the final string for the key | |
result[key] = | |
`widths={${JSON.stringify(widths)}} sizes={\`${sizes}\`}`; | |
}); | |
return result; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This script records the sizes of images on the page as the window resizes. Images with a data-img-measure-id attr are measured. | |
You can then run imgMeasure.printMeasurements() to see the data (in table form so you can copy it to Excel for graphing). | |
imgMeasure.computeSizes(k) runs a naive algorithm to compute the necessary image sizes and breakpoints to avoid serving image files | |
that are more than k times as wide as the element they are rendered in | |
Note: I noticed that resizing the window slowly from largest to smallest is the best way to get good data. Moving too quickly or back | |
and forth repeatedly can cause sufficient error in measurement that the computeSize algorithm fails (i.e. generates way too many sizes) | |
*/ | |
const images = Array.from( | |
document.querySelectorAll("[data-img-measure-id]"), | |
); | |
const measurements = []; | |
window.addEventListener("resize", () => { | |
measurements.push( | |
Object.fromEntries( | |
images | |
.map((i) => [i.dataset.imgMeasureId, i.clientWidth]) | |
.concat([["window", window.innerWidth]]), | |
), | |
); | |
}); | |
const readData = () => measurements.sort((a, b) => b.window - a.window); | |
const printMeasurements = () => console.table(readData()); | |
const computeSizes = (k = 2) => { | |
if (typeof k !== "number" || k < 1) { | |
console.error("k should be a number greater than 1"); | |
return; | |
} | |
const data = readData(); | |
if (!data.length) { | |
console.error("no data yet"); | |
return; | |
} | |
const ids = Object.keys(data[0]).filter((key) => key !== "window"); | |
const results = Object.fromEntries( | |
ids.map((id) => [ | |
id, | |
data.map((d) => ({ | |
measurement: d[id], | |
window: d.window, | |
})), | |
]) | |
.map(([id, dataset]) => [ | |
id, | |
dataset.reduce( | |
(r, { measurement, window }) => { | |
if ( | |
r.currentSize > k * measurement || | |
r.currentSize < measurement | |
) | |
r.sizes.push({ measurement, window }); | |
return r; | |
}, | |
{ | |
sizes: [dataset[0]], | |
get currentSize() { | |
return this.sizes[this.sizes.length - 1] | |
.measurement; | |
}, | |
}, | |
).sizes, | |
]), | |
); | |
console.log(results); | |
}; | |
window.imgMeasure = { | |
printMeasurements, | |
computeSizes, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment