Code Monkey home page Code Monkey logo

eleventy-img's Introduction

eleventy Logo

eleventy-img

Requires Node 18+

Low level utility to perform build-time image transformations for both vector and raster images. Output multiple sizes, save multiple formats, cache remote images locally. Uses the sharp image processor.

You maintain full control of your HTML. Use with <picture> or <img> or CSS background-image, or others! Works great to add width and height to your images!

npm Version GitHub issues

Installation

npm install --save-dev @11ty/eleventy-img

The full eleventy-img documentation is on 11ty.dev.

Tests

npm run test

Community Roadmap

eleventy-img's People

Contributors

adamwolf avatar aleksandrhovhannisyan avatar brandenbyers avatar danburzo avatar dweidner avatar eatonz avatar ebeigarts avatar ghf avatar grimlink avatar gyanreyer avatar immitsu avatar jaredreisinger-drizly avatar jonsmithers avatar lmammino avatar mustafaoral avatar nosecreek avatar pdehaan avatar phuhl avatar ryanccn avatar seb-celinedesign avatar snapstromegon avatar solution-loisir avatar spekulatius avatar stedman avatar stlk avatar zachleat avatar zeroby0 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eleventy-img's Issues

generateHTML bug with svgShortCircuit

image

let stats = await Image("https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg", {
    formats: ["webp", "png", "svg"],
    svgShortCircuit: true,
    widths: [1200],
  });
  
  console.log( stats );

  let html = Image.generateHTML(stats, {
    alt: "Tiger",
  });

  console.log( html );

nunjucks template where I use the image shortcode did not render!?

image created, placed in the right place, returned tag looks good, but:

the nunjuck template where i use the shortcode did not render.
without an error.

Why? Any ideas?

i use it this way:

{% extends "_includes/components/sections/base.njk" %}
{% block content %}
<p> some text </p>
{% image "./_includes/assets/img/leistungen/baum-pflege.jpg", "asda", "(min-width: 30em) 50vw, 100vw" %}
{% endblock %}

implemented this way

const Image = require("@11ty/eleventy-img");
async function imageShortcode(src, alt, sizes) {
  let metadata = await Image(src, {
    widths: [null],
    formats: [null],
    outputDir: "./_site/static/img"
    urlPath: "/home/kraut/projects/test/_site/static/img/"
  });

  let imageAttributes = {
    alt,
    sizes,
    loading: "lazy",
    decoding: "async",
  };
  console.log(src);
  console.log(imageAttributes);

  const i = Image.generateHTML(metadata, imageAttributes);
  console.log(i);
  return i;
}

module.exports = function(eleventyConfig) {
  eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);
  eleventyConfig.addLiquidShortcode("image", imageShortcode);
  eleventyConfig.addJavaScriptFunction("image", imageShortcode);
  // ... more 
};

log looks good ...

./_includes/assets/img/leistungen/baum-pflege.jpg
{
  alt: 'asda',
  sizes: '(min-width: 30em) 50vw, 100vw',
  loading: 'lazy',
  decoding: 'async'
}
<img src="/home/kraut/projects/test/_site/static/img/fe913fdc-1400.jpeg" width="1400" height="300" alt="asda" loading="lazy" decoding="async">

image file exists: /home/kraut/projects/test/_site/static/img/fe913fdc-1400.jpeg !

But why did the whole nunjuck template not rendered ? Any ideas?
If i remove the {% image ... %} from the template its rendered fine! of course without image.

Is there any way to get just the image url?

Hi there,
I am new to Eleventy and just trying this plugin. I followed the tutorial here and I am using the the generateHTML function. I'd like to output just the image url for Opengraph meta. Is it possible?

<meta property="og:image" content="">

Support using buffer as an input (real sourceUrl currently required)

I would love to be able to generate SVG code directly in my template (instead of loading a separate SVG file) and then pass it off to eleventy-img as a buffer to turn into a PNG file for social sharing. Sharp supports using a buffer to load the image, so the code below works in eleventy-img 0.6.0.

The 'sourceUrl' option is required (but up to this point was only used to generate the filename hash), so I just passed in the full text I was going to use in the image.

const Image = require("@11ty/eleventy-img");

module.exports = eleventyConfig => {
  // Convert SVG to our social media sharing image
  eleventyConfig.addNunjucksAsyncShortcode("socialImage", async function (title, author) {

    let linesArray = wordWrapString(title,20); // Returns an array like ["This is","my title","broken up","into lines."]
    linesArray[0] = linesArray[0] || "";
    linesArray[1] = linesArray[1] || "";
    linesArray[2] = linesArray[2] || "";
    linesArray[3] = linesArray[3] || "";

    let authorLine = author;
    let svgUnique = `${linesArray[0]} ${linesArray[1]} ${linesArray[2]} ${linesArray[3]} ${authorLine}`;

    let svgStr = `
      <svg font-family="sans-serif" font-size="80" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg" style="background-color:#fff;" fill="#4a4a4a">
        <rect x="0" y="0" width="1200" height="630" fill="#ffffff" />
        <circle cx="1000" cy="230" fill="#ebebeb" r="530"/>
        <path d="m957.798 267.25 100.942 201.873h-201.878z" fill="#ebeb5d" xmlns="http://www.w3.org/2000/svg"/>
        <text x="70" y="200" style="font-family:'Bitstream Vera Sans','Helvetica',sans-serif;font-weight:bold;">${linesArray[0]}</text>
        <text x="70" y="300" style="font-family:'Bitstream Vera Sans','Helvetica',sans-serif;font-weight:bold;">${linesArray[1]}</text>
        <text x="70" y="400" style="font-family:'Bitstream Vera Sans','Helvetica',sans-serif;font-weight:bold;">${linesArray[2]}</text>
        <text font-size="40" x="70" y="500" fill="#808080">By ${authorLine}</text>
      </svg>
      `

    let stats = await Image(Buffer.from(svgStr), {
      widths: [1200], // Facebook Opengraph image is 1200 x 630
      formats: ['png'],
      outputDir: "dist/11ty-img/", 
      urlPath: "/11ty-img/",
      sourceUrl: svgUnique // This is only used to generate the output filename hash
    });

    // Iterate over formats and widths
    return stats["png"][0].url;
  });
};

When I try to run this code in 0.7.0, I get this error:
> ENOENT: no such file or directory, stat '...

Am I correct in assuming that 0.7.0 checks if sourceUrl is a real file, and checks whether or not it has changed?

If I want to feed a buffered SVG into Image, what can I do to make sourceUrl work?

"Input file is missing" error; is this line in img.js causing it?

let buffer = await await CacheAsset(src, Object.assign({

Hello folks. The above-referenced line contains two await statements in one line and I'm wondering if this is causing my issue: I get Input file is missing when following the myResponsiveImage example in the README. I don't know what two proceeding await statements does in JavaScript, but I have a hunch this could be a typo.

I've tried loads of permutations of stating where the input file should be in the example's config, to no luck, and I'm 99% sure I've got them right at the moment.

I've tried editing the code and removing one of the await statements in my node_modules and restarting the eleventy development server, but I'm not sure if that's the right way to test my theory. Doing that doesn't remove the error. I'm a bit stuck and could do with some help.

Responsive Image shortcode example in docs doesn't work?

Hi,

I'm using v0.5.0

The first eleventyConfig example using the myImage shortcode works OK.

But when I try using the myResponsiveImage example (below)

eleventyConfig.addNunjucksAsyncShortcode("myResponsiveImage", async function(src, alt) {
    if(alt === undefined) {
      // You bet we throw an error on missing alt (alt="" works okay)
      throw new Error(`Missing \`alt\` on myResponsiveImage from: ${src}`);
    }

    let stats = await Image(src, {
      widths: [350, null],
      formats: ['webp', 'jpeg']
    });
    let lowestSrc = stats[outputFormat][0];
    let sizes = "100vw"; // Make sure you customize this!

    // Iterate over formats and widths
    return `<picture>
      ${Object.values(stats).map(imageFormat => {
        return `  <source type="image/${imageFormat[0].format}" srcset="${imageFormat.map(entry => entry.srcset).join(", ")}" sizes="${sizes}">`;
      }).join("\n")}
        <img
          src="${lowestSrc.url}"
          width="${lowestSrc.width}"
          height="${lowestSrc.height}"
          alt="${alt}">
      </picture>`;
    });

Then I get a build error in the console

  EleventyShortcodeError: Error with Nunjucks shortcode `myResponsiveImage`

`Template render error` was thrown
> outputFormat is not defined

Just wondering if the code changed recently but the responsive image shortcode hasn't been updated?

Thanks,
Ben

generateHtml : img tag has an invalid sizes attributes

generateHtml generate this kind of img tag code :
<img src="images.jpeg" width="730" height="792" alt="My image" sizes=" (min-width: 1280px) 730px, (min-width: 1024px) calc((550 + 90 + 90) / (550 + 90 + 90 + 550) * 100vw), (min-width: 730px) 730px, 100vw " loading="lazy" decoding="async">
which is an invalid as the W3C validator says : "The sizes attribute may be specified only if the srcset attribute is also present".

Allow arbitrary "sourceUrl" for cache checking when processing SVG buffers

A little followup on #40 ...

I've been able to get SVGs turned into PNG files for OG images, which is great.

Here's my new issue: As far as I can tell, Sharp doesn't seem to have any way of loading an external image into an SVG (lovell/sharp#2519 - "sharp does not provide any networking features, you'll have to write your own code to download the data and provide it as a Buffer."

When I try to load an image into my SVG string via an absolute URL (see code below), it just doesn't show up in the output image
`<image id="_Image3" width="202px" height="202px" xlink:href="/Users/stephenjbell/Documents/Projects/steedgood/src/forestry-img/poolportrait.jpg" />`

The easiest solution seems to be turning the image into a datauri (through https://www.npmjs.com/package/datauri), and then dropping the datauri into the SVG string.

const datauri = require("datauri");

let photoDataUri = "";

try {
  photoDataUri = await datauri(authorPhoto);
} catch (error) {
   console.log(error); // Leave photo blank if we can't load the file
}

let svgStr = `<svg><image id="_Image3" width="202px" height="202px" xlink:href="${photoDataUri}" /></svg>`;

// Then run svgStr through eleventy-img

I /think/ when you made changes based on #40 , you used the entire SVG string to check if there was a cached image or not.

This is a great idea, except that each time I want to check if there's a cached version of my PNG file, I need to generate a datauri from the external image, which is /slow/ for performance, especially if I use my socialImage shortcode several times in the same page (i.e. dropping the image url into the page for both Twitter and Facebook meta):

<meta property="og:image" content="{% socialImage title, author.data.title, author.data.feature_image %}">
<meta name="twitter:image" content="{% socialImage title, author.data.title, author.data.feature_image %}" />

So, I think it might make sense to add the ability to use a different string instead of sourceUrl for checking if the cached image is available? It's possible I'm not understanding correctly how things are currently set up.

Multiple pass image encoding

I think the images are encoded using the optimized baseline. This is great for small images. A nice enhancement would be the ability to choose how the image gets encoded. For example, loading large images in multiple passes would be my preference, so at least users on slow connections would see some resemblance of an image start to load immediately.

Lossy/lossless compression?

Hi,

This plugin looks great!
Only thing I would love to see added is image compression (e.g. set PNG and/or JPG quality to 80).
Is that on the roadmap?

potential Windows and path.join issue

If I use eleventy-img on Windows, the output URLs are composed with backslashes. I believe that is because path.join is cross-platform and will use the OS default (which is a backslash in the case of Windows), so it's not necessarily suitable to construct URLs without some intervention on Windows.

Question: How do I properly render an image using the sample code and Nunjucks?

Hi everyone,

I was wondering if anyone could be of assistance with helping me understand how this is implemented.

I'm currently working off Eleventail provided by Phil Hawksworth: https://github.com/philhawksworth/eleventail

I'm learning how to use eleventy-img by attempting to replace the existing possum image on the index page with one rendered by eleventy-img.

I'm using nunjucks for this and I'm using only two arguments: the img path and the alt copy.

I'm also using the sample code from the readme but instead of using addJavaScriptFunction, I'm using addNunjucksAsyncShortcode.

I'm receiving a message in my console stating that the Input file is missing, however this is the same img src as what is in the the original code that is working.

Am I not properly referencing the image file path?

I was going to ask this on @stedman issue, but I didn't want to pollute that issue with my question.

I'm pretty sure this is something I'm either missing or not understanding.

Regards,
Kaeside

# index.njk
...
<div>
    ...
    {% myResponsiveImage "/images/possum-balloon.png", "possum" %}
    ...
</div>
...
# eleventy,js

...
eleventyConfig.addNunjucksAsyncShortcode("myResponsiveImage", async function (src, alt, options) {
    if (alt === undefined) {
      // You bet we throw an error on missing alt (alt="" works okay)
      throw new Error(`Missing \`alt\` on myResponsiveImage from: ${src}`);
    }

    let stats = await Image(src, options);
    let lowestSrc = stats.jpeg[0];
    let sizes = "100vw"; // Make sure you customize this!

    // Iterate over formats and widths
    return `<picture>
      ${Object.values(stats).map(imageFormat => {
      return `  <source type="image/${imageFormat[0].format}" srcset="${imageFormat.map(entry => `${entry.url} ${entry.width}w`).join(", ")}" sizes="${sizes}">`;
    }).join("\n")}
        <img
          alt="${alt}"
          src="${lowestSrc.url}"
          width="${lowestSrc.width}"
          height="${lowestSrc.height}">
      </picture>`;
  });
...
`TemplateContentRenderError` was thrown
> (./src/site/index.njk)
  EleventyShortcodeError: Error with Nunjucks shortcode `myResponsiveImage`

`Template render error` was thrown
> Input file is missing

How to use with `pathprefix`configuration?

Hey

I'm trying to get the eleventy-img to work with the pathprefix option, but I can't figure out how to make it work.
Am I supposed to handle it in the filter implementation in .eleventry.js or when calling the filter in the .njk template with
{% image "/img/1337.jpg", "1337" %}?

In this case pathPrefix: "/foo" would serve all the static html files from server path ./foo, but the imageShortcode will look for images at ./img/

I'm using the following configuration :

//removed for readability 
const Image = require("@11ty/eleventy-img");


module.exports = function (eleventyConfig) {
  eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);

  //removed for readability 

  return {
    templateFormats: ["md", "njk", "11ty.js"],
    pathPrefix: "/foo",
    markdownTemplateEngine: "njk",
    htmlTemplateEngine: "njk",
    dataTemplateEngine: "njk",
    dir: {
      input: ".",
      includes: "_includes",
      data: "_data",
      output: "_site",
    },
  };
};

async function imageShortcode(src, alt, sizes) {
  let metadata = await Image(path.join(__dirname, src), {
    outputDir: "./_site/img/",
    widths: [300, 600, 900, 1200],
    formats: ["avif", "webp", "jpeg"],
  });

  let imageAttributes = {
    alt,
    sizes,
    loading: "lazy",
    decoding: "async",
  };

  return Image.generateHTML(metadata, imageAttributes);
}

Changing the outputDir option doesn’t appear to be having an effect on the resulting URL

Thanks for making this plugin, Zach. It’s really, really good! I just noticed this lil’ issue while I was messing around with it.

How to reproduce

This is my Image instantiation:

    const stats = await Image(`./dist/${src}`, {
      formats: [format],
      widths,
      outputDir: './dist/images/'
    });

Note: It's set to look in dist because I already pre-compress and treat images with a gulp task.

This is a sample of the output:

{
  jpeg: [
    {
      format: 'jpeg',
      width: 320,
      height: 216,
      url: '/img/121af431-320.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/121af431-320.jpeg 320w',
      outputPath: 'dist/images/121af431-320.jpeg',
      size: 16523
    },
    {
      format: 'jpeg',
      width: 600,
      height: 406,
      url: '/img/121af431-600.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/121af431-600.jpeg 600w',
      outputPath: 'dist/images/121af431-600.jpeg',
      size: 46880
    },
    {
      format: 'jpeg',
      width: 1600,
      height: 1082,
      url: '/img/121af431.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/121af431.jpeg 1600w',
      outputPath: 'dist/images/121af431.jpeg',
      size: 149347
    }
  ];
}

How to use in Nunjucks?

Sorry, I'm still having a hard time understanding, trying to get this to work inside my NJK file.
Receiving ,Error: Input file is missing .
I'm using the Eleventy Base Blog.

Please help!

.eleventyconfig

const Image = require("@11ty/eleventy-img");

module.exports = function(eleventyConfig) {
	eleventyConfig.addNunjucksAsyncShortcode("myImage", async function(src, alt, outputFormat = "jpeg") {
		let stats = await Image(src, {
			formats: [outputFormat],
			widths: [360, null],
			urlPath: "/img/",
			outputDir: "img/",
		});
		let props = stats[outputFormat].pop();
		if (alt === undefined) {
			throw new Error(`Missing \`alt\` on myImage from: ${src}`);
		}
		return `<img src="${props.src}" width="${props.width}" height="${props.height}" alt="${alt}">`;
	});
};

Nunjucks template:

{% myImage "someimage.jpg", "Some alt text" %}

Cannot read property 'format' of undefined. Error when including SVG format?

"@11ty/eleventy-img": "^0.7.8"

const Image = require("@11ty/eleventy-img");

async function imageShortcode(src, alt, sizes) {
  let metadata = await Image(src, {
    widths: [300, 600],
    formats: ["avif", "webp", "jpeg", "svg"],
    svgShortCircuit: true,
    outputDir: "./src/_includes/assets/img/",
    urlPath: "./assets/img/"
  });

  let imageAttributes = {
    alt,
    sizes,
    loading: "lazy",
    decoding: "async",
  };

  // You bet we throw an error on missing alt in `imageAttributes` (alt="" works okay)
  return Image.generateHTML(metadata, imageAttributes);
}

module.exports = eleventyConfig => {
eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);
}

HTML

      {% image "src/_assets/img/social-share-img.jpg", "photo of my cat", "(min-width: 30em) 50vw, 100vw" %}
      {% image "src/_assets/svg/arkell.svg", "photo of my cat", "(min-width: 30em) 50vw, 100vw" %}

Hi,

When I include svg in my formats an error is thrown saying Cannot read property 'format' of undefined. If I remove svg everything runs as expected.

This is most likely user error but I can't figure out what I'm not doing correctly?

Keep original filenames

It would be nice to have a way to keep the original filenames for the exported images, instead of random letters and numbers eg. for better SEO.

Persist in-memory cache

In-memory cache is empty whenever Eleventy build starts from scratch. It's not an issue for sites with small number of images. But for sites with large number of large images, compressing them takes quite a lot of time.

In my case (https://kamituel.pl) I've over 300 high resolution source images that I'd ideally serve in 2-3 formats (avif, jpg, maybe webp) in multiple sizes.

I propose a new config option that would, if enabled, persist the in-memory cache to disk and reuse it the next time the build starts.

If maintainers consider this idea to be a good candidate to exist in eleventy-img itself, I'd be happy to formulate this proposal more concretely and later help / submit a PR.

"metadata.format == outputFormat" then "sharpJpegOptions" parameters aren't used

My ..eleventy.js file contains:

    ...

    eleventyConfig.addNunjucksAsyncShortcode("image", async function(src, { width }) {
        let stats = await Image(
            src,
            {
                widths: [width],
                formats: ['jpeg'],
                urlPath: "/images/",
                outputDir: "./dist/images/",
                sharpJpegOptions: {
                    quality: 99,
                    progressive: true
                }
            }
        );

        let props = stats['jpeg'].pop();

        return `<img src="${props.url}">`;
    });
    ...

When I check output jpeg file with magick identify I see:

$  identify -verbose dist/images/7fd33d05-300.jpeg
...
  Dispose: Undefined
  Iterations: 0
  Compression: JPEG
  Quality: 80
  Orientation: Undefined

The issue: bad quality value, 99 != 80.

When I debug the code, I found that the problem is here

        if(outputFormat && metadata.format !== outputFormat) {
          let sharpFormatOptions = getSharpOptionsForFormat(outputFormat, options);
          sharpInstance.toFormat(outputFormat, sharpFormatOptions);
        }

This is this variable value:

  • outputFormat=jpeg
  •  metadata = {
       format: 'jpeg',
       width: 800,
       height: 800,
       space: 'srgb',
       channels: 3,
       depth: 'uchar',
       density: 72,
       chromaSubsampling: '4:4:4',
       isProgressive: false,
       hasProfile: false,
       hasAlpha: false,
       exif=....
     }
    

metadata.format == outputFormat then sharpJpegOptions parameters aren't used.

Question: how can I configure Quality option?

Can plugin respect directory configurations?

when this is set in .eleventy.js

  dir: {
    input: "src",
    output: "dist"
  }

Normally in my markup I would include an image by pointing it to /img/etc.jpg, but to utilize the plugin I have to point it to src/img/etc.jpg. Which makes things a bit confusing. Also complicated if, for instance, I only wanted to use the plugin on production builds.

Also (but not as major) I have to set outputDir: 'dist/img/' in the plugin config rather than just /img/.

Possible for the plugin to include the directory settings?

0.7.2 generateHTML minor issues : markdown and jpg

Some minor issues for me with generateHTML when trying to replace my custom HTML string :

  1. Not sure why, result is not the same with image shortcode within markdown (.md) file. E.g. :
lorem ipsum.

{% Image "assets/img/kevin-young-Z5qD8T3wVvk-unsplash.jpg", "Chêne au feuillage élancé sous lequel marche une jeune femme", 230 %}Lorem ipsum

second paragraph will result picture + text is not into a <p> tag. It works when I generate my own HTML string.

  1. jpg format (not jpeg) will result in
    <source type="undefined"
    jpeg works

Here is my full shortcode :

eleventyConfig.addNunjucksAsyncShortcode("Image", async (src, alt, maxWidth = 800, seo = false) => {
    if (!alt) {
      throw new Error(`Missing \`alt\` on Image from: ${src}`);
    }

    const possibleWidths = [25, 320, 500, 680, 768, 1024, 1280, 1536, 1920, 2400];
    var generatedWidths = possibleWidths.filter((value) => value <= maxWidth); // retricted to less than maxWidth
    if (!possibleWidths.includes(maxWidth)) generatedWidths.push(maxWidth); // generated max-width image
    if (seo && !possibleWidths.includes(500)) generatedWidths.push(500); // 500 is required when used as SEO image

    let stats = await Image(src, {
      widths: generatedWidths, 
      formats: ["jpg", "webp"],
      urlPath: "/assets/img/",
      outputDir: "./_site/assets/img/",
      cacheOptions: { // remote image (isn't used yet)
        duration: "1d",
        directory: ".cache",
        removeUrlQueryParams: false,
      },
      // eleventy-img v0.4.2+
      // Extra options to pass to the Sharp image format converter
      // https://sharp.pixelplumbing.com/api-output#webp
      sharpWebpOptions: {
        quality: 90,
      },
      // eleventy-img v0.6.0+
      // Extra options to pass to the Sharp image format converter
      // https://sharp.pixelplumbing.com/api-output#avif
      // sharpAvifOptions: {
      //   quality: 50,
      // },
      // https://sharp.pixelplumbing.com/api-output#png
      // sharpPngOptions: {},
      // https://sharp.pixelplumbing.com/api-output#jpeg
      sharpJpegOptions: {
        quality: 60,
        progressive: true
      },

      // eleventy-img v0.4.0+
      // Define custom filenames for generated images
      filenameFormat: function (id, src, width, format, options) {
        // id: hash of the original image
        // src: original image path
        // width: current width in px
        // format: current file format
        // options: set of options passed to the Image call
        var filename = src.replace(/^.*[\\\/]/, ''); // filename from full path
        filename = filename.split('.').slice(0, -1).join('.'); // remove extension
        if (width) {
          return `${filename}-${width}.${format}`;
        }
        return `${filename}.${format}`;
      }
      
    });

    let lowestSrc = stats["jpg"][0];

    const srcset = Object.keys(stats).reduce(
      (acc, format) => ({
        ...acc,
        [format]: stats[format].reduce(
          (_acc, curr) => `${_acc} ${curr.srcset} ,`,
          ""
        ),
      }),
      {}
    );

    const sourceWebp = `<source
      type="image/webp"
      srcset="${srcset["webp"]}"
      sizes="(max-width: ${maxWidth}px) 100vw, ${maxWidth}px"
    >`;

    const sourceJpg = `<source
      srcset="${srcset["jpg"]}"
      sizes="(max-width: ${maxWidth}px) 100vw, ${maxWidth}px"
    >`;

    const img = `<img
      loading="lazy"
      decoding="async"
      alt="${alt}"
      src="${lowestSrc.url}"
      sizes="(max-width: ${maxWidth}px) 100vw, ${maxWidth}px"
      srcset="${srcset["jpg"]}"
      >`;

    return `<picture> ${sourceWebp} ${sourceJpg} ${img} </picture>`;
  });

Broken urls for images smaller than requested sizes

While trying out the responsive image based on the Output Optimized Multi-Format, Multi-Size Responsive Images using <picture> example in the Readme I noticed that if the original is smaller than the requested widths it will generate url:s that doesn't match the output files.

Options:

{
  widths: [500, 1000, 2000, 3000, 4000],
  outputDir: "_site/img",
  formats: ["jpeg"],
};

Output

{
  jpeg: [
    {
      format: 'jpeg',
      width: 500,
      height: 500,
      url: '/img/22a3e283-500.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/22a3e283-500.jpeg 500w',
      outputPath: '_site/img/22a3e283-500.jpeg',
      size: 31094
    },
    {
      format: 'jpeg',
      width: 1000,
      height: 1000,
      url: '/img/22a3e283-1000.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/22a3e283-1000.jpeg 1000w',
      outputPath: '_site/img/22a3e283-1000.jpeg',
      size: 148677
    },
    {
      format: 'jpeg',
      width: 1024,
      height: 1024,
      url: '/img/22a3e283-1024.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/22a3e283-1024.jpeg 1024w',
      outputPath: '_site/img/22a3e283-2000.jpeg',
      size: 182066
    },
    {
      format: 'jpeg',
      width: 1024,
      height: 1024,
      url: '/img/22a3e283-1024.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/22a3e283-1024.jpeg 1024w',
      outputPath: '_site/img/22a3e283-3000.jpeg',
      size: 182066
    },
    {
      format: 'jpeg',
      width: 1024,
      height: 1024,
      url: '/img/22a3e283-1024.jpeg',
      sourceType: 'image/jpeg',
      srcset: '/img/22a3e283-1024.jpeg 1024w',
      outputPath: '_site/img/22a3e283-4000.jpeg',
      size: 182066
    }
  ]
}

Notices that the output's url doesn't match the outputPath as soon as the requested size is larger than the original (for example url: '/img/22a3e283-1024.jpeg', outputPath: '_site/img/22a3e283-4000.jpeg')

Don’t use <picture> with one image format

For example, this isn’t a problem but it doesn’t need it:

<picture><source type="image/jpeg" srcset="/img/built/6dfd7ac6-200.jpeg 200w, /img/built/6dfd7ac6-400.jpeg 400w"><img src="/img/built/6dfd7ac6-200.jpeg" width="200" height="200" loading="lazy" decoding="async" alt="The sample Nebula Image from Unsplash on the Eleventy Image docs" src="https://images.unsplash.com/photo-1608178398319-48f814d0750c"></picture>

Add ability to crop images

I just started using this plugin, and this is the main feature I’m missing the most.

Its use case is when you need your images to conform to specific aspect ratios, such as square thumbnails.

(Sure, you can accomplish this with CSS too, but I’d prefer cropping the images during the build process.)

Since the widths option is already an array, perhaps there could be a single aspectRatio option too? e.g. [1, 1] or [16, 9].

Bonus points if you could also pass all of the sharp resize options too, e.g. position: "center" https://sharp.pixelplumbing.com/api-resize

shortcode is not working in for loop, if looping over more than 4 items !?

{% image ... %} is not working in nunjucks for loop:

this is working, all fine:

<div class="accordion" id="accordionExample">
    {% image "assets/img/service_baum-pflege.jpg", "img-thumbnail", "asda", "(min-width: 30em) 50vw, 100vw" %}
    {% for service in services.services %}
        {% set id = service.title | slug %}
        <div class="card">
            <a class="service-btn" data-toggle="collapse" data-target="#collapse-{{id}}" aria-expanded="false" aria-controls="collapseOne">
            <div class="card-header service-header" id="{{ id }}">
                <div class="row service-row">
                    <div class="col-lg-1 col-md-2 col-sm-2 col-3">
                            <img class="service-icon" src="{{ service.icon }}" />
                    </div>
                    <div class="col-lg-3 col-md-10 col-sm-10 col-9">
                            <h4 class="mb-0 service-title">{{ service.title }}</h4>
                    </div>
                    <div class="col-lg-8 col-md-12 col-sm-12 col-12">
                        <h5 class="text-muted service-subtitle">{{ service.subtitle }}</h5>
                    </div>
                </div>
            </div>
            </a>
            <div id="collapse-{{id}}" class="collapse" aria-labelledby="{{ id }}" data-parent="#accordionExample">
                <div class="card-body">
                    {{ service.body | renderMarkdown | safe }}
                </div>
            </div>
        </div>
    {% endfor %}
</div>

this is not working:

<div class="accordion" id="accordionExample">
    {% for service in services.services %}
        {% image "assets/img/service_baum-pflege.jpg", "img-thumbnail", "asda", "(min-width: 30em) 50vw, 100vw" %}
        {% set id = service.title | slug %}
        <div class="card">
            <a class="service-btn" data-toggle="collapse" data-target="#collapse-{{id}}" aria-expanded="false" aria-controls="collapseOne">
            <div class="card-header service-header" id="{{ id }}">
{# ... more ... #}
        </div>
    {% endfor %}
</div>

Any Ideas? Help would be very much appreciated!

'Input file is missing' error when passing path as variable

Hi there, I am trying to pass the path of the image source to the shortcode because I am iterating in a for loop and the data itself is coming from a JSON file.

For example:
{% myResponsiveImage "./assets/images/white.jpg", item.description %} works fine
but {% myResponsiveImage item.imageUrl, item.description %} gives an error where item.imageUrl is "./assets/images/white.jpg"

The error I am receiving is

Having trouble rendering njk template ./source/menu.njk

TemplateContentRenderError was thrown

(./source/menu.njk)
EleventyShortcodeError: Error with Nunjucks shortcode myResponsiveImage

Template render error was thrown

Input file is missing

Template render error was thrown:
Error: Input file is missing

It successfully makes the correct sizes of the images. I tried escaping the / in the path as well.

Any ideas?

How to implement?

@zachleat Once we add the plugin and configuration, what syntax do we need to add to our template / markdown file to get the responsive image size markup and files?

Only first widths size is generated

Hello, I'm using Windows. I have the following code I've copied from Zach's blog (https://www.zachleat.com/web/eleventy-image/):

const Image = require("@11ty/eleventy-img");  
(async () => {  
  let stats = await Image("source/photo-1462331940025-496dfbfc7564.jpg", {  
    formats: ["avif", "webp", "jpeg"],  
    widths: [600, 1200, 1800],  
  });  
  console.log(
    Image.generateHTML(stats, {
      alt: "A bomb ass nebula",
      loading: "lazy",
      decoding: "async",
    })
  );
})();

When I run node index.js only the 600 sized image is generated. That is also the only size output in generateHTML. Is my code wrong please?

Add options for image optimization (other than resizing)

For heavily optimized images, the image size often increases (quite significantly in some cases). A simple fix would be to check the image size and use the original version if it's smaller than the "optimized" one, but it'd be great to include better image compression support by default.

Has this already been investigated?

how can i use it synchronous?

It can also work in synchronous environments but requires a bit more (undocumented as of yet) setup.

please give me a hint how :)
my projects needs a synchronous setup.
I get big problems using it asynchronous.

Thx a lot !

Option to skip if output file already exists

Must be off by default, but would work nicely with new watch arguments that tell what files changed.

Maybe also check that the image width is the same for an extra level? Not sure here yet how deep to go, would be very confusing to change the source file and the output doesn’t change.

Allow upscaling SVGs

Right now, eleventy-img never upscales images, which is great for raster images. It’s a problem for SVGs, though, since SVGs are designed to scale, and they are often displayed at higher resolutions than their source.

Image shortcode not working in macros.

The image shortcode works as expected in my Nunjucks Template.

It also works as expected in a Nunjucks include.

However when the same shortcode is used in a Nunjucks macro, it fails silently. And any HTML after the Image tag within the macro is not rendered.

Any ideas?

{# index.njk #}

<!DOCTYPE html>
{% macro myMacro() %}
  <p>But an image in a macro does not. It fails siently too</p>
  {% image "./src/images/nebula.jpg",  "a nebula"  %}
  <p>And any later HTML doesn't show either</p>
{% endmacro  %}

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="shortcut icon" href="favicon.ico">
  <title>Example page</title>
</head>
<body>
  <p>Image tag in the page appears.</p>
  {% image "./src/images/nebula.jpg",  "a nebula"  %}  
  {% include "_image.njk" %}   
  {{ myMacro() }}
</body>
</html>
{# _includes/_image.njk #}

<p>As does the image in an include...</p>
{% image "./src/images/nebula.jpg",  "a nebula"  %}
// .eleventy.js

const Image = require("@11ty/eleventy-img");

async function imageShortcode(src, alt, sizes) {
  let metadata = await Image(src, {
    widths: [100],
    formats: ['jpeg'],
    outputDir: "./dist/img/",
  });

  let imageAttributes = {
    alt,
    sizes,
    loading: "lazy",
    decoding: "async",
  };

  console.log(Image.generateHTML(metadata, imageAttributes));
  
  // You bet we throw an error on missing alt in `imageAttributes` (alt="" works okay)
  return Image.generateHTML(metadata, imageAttributes);
}

module.exports = function(eleventyConfig) {
  eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);
  eleventyConfig.addLiquidShortcode("image", imageShortcode);
  eleventyConfig.addJavaScriptFunction("image", imageShortcode);

  return {
    dir: {
      input: 'src',
      output: 'dist',
    },
  };
};

Result :
Screen Shot 2021-01-27 at 08 29 50

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.