Created
March 12, 2022 14:19
-
-
Save BryanSchuetz/f27e468f81dd0979f12532b6e929009b to your computer and use it in GitHub Desktop.
Responsive image transforms for eleventy
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 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