Skip to content

Instantly share code, notes, and snippets.

@BryanSchuetz
Created March 12, 2022 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BryanSchuetz/f27e468f81dd0979f12532b6e929009b to your computer and use it in GitHub Desktop.
Save BryanSchuetz/f27e468f81dd0979f12532b6e929009b to your computer and use it in GitHub Desktop.
Responsive image transforms for eleventy
// This file goes in /src/transforms/
// Add this line to .eleventy.js:
// const eleventyImgTransform = require("./src/transforms/eleventy-img-transform.js");
// And add this one to module.exports in .eleventy.js:
// eleventyConfig.addTransform("eleventyImgTransform", eleventyImgTransform);
const Image = require("@11ty/eleventy-img");
const cheerio = require("cheerio");
const { resolve } = require("path");
module.exports = (value, outputPath) => {
// Search for <img> on all pages matching this regex
// Only run on pages in the /dist/blog folder
if (outputPath.match(/^dist\/blog.*\.html$/)) {
let $ = cheerio.load(value);
// Find all the <img> within specified element, that don't have an HTML class
$(".article-text img:not([class])")
.not("picture > img") // Skip any <img> that's already wrapped in <picture>
.each(function (i, elem) {
let imgAlt = $(this).attr("alt") || "";
let imgTitle = $(this).attr("title") || "";
let imgSrc = $(this).attr("src");
let imgLocation = "src" + imgSrc;
let fullImgLocation = resolve(imgLocation);
// TODO: Make this transform work when loading remote images
// Only process image if it's local
if (imgSrc.indexOf("http") == 0) {
return;
}
let options = {
// Each image size roughly 1.5X larger than last
widths: [250, 400, 600, 900, 1300],
formats: ["avif", "webp", "jpeg"], // In order of preference
outputDir: "dist/11ty-img/",
urlPath: "/11ty-img/",
};
Image(fullImgLocation, options); // Start the images processing
let stats = Image.statsSync(fullImgLocation, options); // Get the locations where the processed images will appear
let lowestSrc = stats["jpeg"][0];
let highestSrc = stats["jpeg"][stats["jpeg"].length - 1]; // The last JPG is the largest
let aspectRatio = highestSrc.width / highestSrc.height;
// Set the image width attribute to 800px, unless that's too large
let imageWidth = highestSrc.width;
if (highestSrc.width >= 800) {
imageWidth = 800;
}
// Set height to make the image the correct aspect ratio
let imageHeight = Math.ceil(imageWidth * aspectRatio);
// Iterate over formats and widths
let newElement = `<picture>
${Object.values(stats)
.map((imageFormat) => {
return ` <source type="image/${
imageFormat[0].format
}" data-srcset="${imageFormat
.map((entry) => entry.srcset)
.join(", ")}">`;
})
.join("\n")}
<img
data-src="${lowestSrc.url}"
data-sizes="auto"
width="${imageWidth}"
height="${imageHeight}"
class="lazyload"
title="${imgTitle}"
alt="${imgAlt}">
</picture>`;
$(this).replaceWith(newElement);
});
value = $.html();
}
return value;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment