Jump to content

PageimageSource


nbcommunication

Recommended Posts

I've been meaning to revise PageimageSrcset for a while now, to remove some features that I felt were unnecessary and to implement a better rendering strategy.

The result is PageimageSource. What does it do?

  • It provides a configurable srcset method/property for Pageimage
  • It allows WebP to be enabled for images it generates.
  • It allows Pageimage:render() to return a <picture> element
  • It provides a Textformatter that replaces <img> elements with the output of Pageimage:render()

Although it is based on a current module, this should still be considered beta and not used in production without a prior development stage.

Here's the README:

PageimageSource

Extends Pageimage with a srcset property/method plus additional rendering options.

Overview

The main purpose of this module is to make srcset implementation as simple as possible in your template code.

For an introduction to srcset, please read this Mozilla article about responsive images.

Installation

  1. Download the zip file at Github or clone the repo into your site/modules directory.
  2. If you downloaded the zip file, extract it in your sites/modules directory.
  3. In your admin, go to Modules > Refresh, then Modules > New, then click on the Install button for this module.

ProcessWire >= 3.0.165 and PHP >= 7.3 are required to use this module.

Configuration

To configure this module, go to Modules > Configure > PageimageSource.

Default Set Rules

These are the default set rules that will be used when none are specified, e.g. when calling the property: $image->srcset.

Each set rule should be entered on a new line, in the format {width}x{height} {inherentwidth}w|{resolution}x.

Not all arguments are required - you will probably find that specifying the width is sufficient for most cases. Here's a few examples of valid set rules and the sets they generate:

Set Rule Set Generated Arguments Used
320 image.320x0-srcset.jpg 320w {width}
480x540 image.480x540-srcset.jpg 480w {width}x{height}
640x480 768w image.640x480-srcset.jpg 768w {width}x{height} {inherentwidth}w
2048 2x image.2048x0-srcset.jpg 2x {width} {resolution}x

How you configure your rules is dependent on the needs of the site you are developing; there are no prescriptive rules that will meet the needs of most situations. This article gives a good overview of some of the things to consider.

When you save your rules, a preview of the sets generated and an equivalent method call will be displayed to the right. Invalid rules will not be used, and you will be notified of this.

WebP

If enabled, WebP versions of the image and srcset variations will be generated and these will be returned by Pageimage::srcset(). As with the default implementation, the image with the smaller file size is returned. In most cases this is the WebP version, but sometimes can be the source.

Make sure to experiment with the quality setting to find a value you find suitable. The default value of 90 is fine, but it is possible that lower values will give you excellent kB savings with little change in overall quality.

For more information on WebP implementation please read the blog posts on the ProcessWire website.

Rendering

These settings control how the output of Pageimage::render() is modified.

Use Lazy Loading?

When enabled this adds loading="lazy" to the <img> attributes. It is useful to have this on by default, and you can always override it in the options for a specific image.

Use the <picture> element?

When enabled, the <img> element is wrapped in a <picture> element and <source> elements for original and WebP variations are provided. This requires WebP to be enabled. For more information on what this does, have a look at the examples in Pageimage::render() below.

Remove Variations

If checked, the image variations generated by this module are cleared on Submit. On large sites, this may take a while. It makes sense to run this after you have made changes to the set rules.

Please note that although the module will generate WebP versions of all images if enabled, it will only remove the variations with the 'srcset' suffix.

Usage

Pageimage::srcset()

// The property, which uses the set rules in the module configuration
$srcset = $image->srcset;

// A method call, using a set rules string
// Delimiting with a newline (\n) would also work, but not as readable
$srcset = $image->srcset('320, 480, 640x480 768w, 1240, 2048 2x');

// The same as above but using an indexed/sequential array
$srcset = $image->srcset([
	'320',
	'480',
	'640x480 768w',
	'1240',
	'2048 2x',
]);

// The same as above but using an associative array
// No rule checking is performed
$srcset = $image->srcset([
	'320w' => [320],
	'480w' => [480],
	'768w' => [640, 480],
	'1240w' => [1240],
	'2x' => [2048],
]);

// The set rules above are a demonstration, not a recommendation!

Image variations are only created for set rules which require a smaller image than the Pageimage itself. This may still result in a lot of images being generated. If you have limited storage, please use this module wisely.

Pageimage::render()

This module extends the options available to this method with:

  • srcset: When the module is installed, this will always be added, unless set to false. Any values in the formats described above can be passed.
  • sizes: If no sizes are specified, a default of 100vw is assumed.
  • lazy: Pass true to add loading=lazy, otherwise false to disable if enabled in the module configuration.
  • picture: Pass true to use the <picture> element, otherwise false to disable if enabled in the module configuration.

Please refer to the API Reference for more information about this method.

// Render an image using the default set rules
// WebP and lazy loading are enabled, and example output is given for <picture> disabled and enabled
echo $image->render();
// <img src='image.webp' alt='' srcset='image.jpg...' sizes='100vw' loading='lazy'>
/*
<picture>
	<source srcset="image.webp..." sizes="100vw" type="image/webp">
	<source srcset="image.jpg..." sizes="100vw" type="image/jpeg">
	<img src="image.jpg" alt="" loading="lazy">
</picture>
*/

// Render an image using custom set rules
echo $image->render(['srcset' => '480, 1240x640']);
// <img src='image.webp' alt='' srcset='image.480x0-srcset.webp 480w, image.1240x640-srcset.webp 1240w' sizes='100vw' loading='lazy'>
/*
<picture>
	<source srcset="image.480x0-srcset.webp 480w, image.1240x640-srcset.webp 1240w" sizes="100vw" type="image/webp">
	<source srcset="image.480x0-srcset.jpg 480w, image.1240x640-srcset.jpg 1240w" sizes="100vw" type="image/jpeg">
	<img src="image.jpg" alt="" loading="lazy">
</picture>
*/

// Render an image using custom set rules and sizes
// Also use the `markup` argument
// Also disable lazy loading
// In this example the original jpg is smaller than the webp version
echo $image->render('<img class="image" src="{url}" alt="Image">', [
	'srcset' => '480, 1240',
	'sizes' => '(min-width: 1240px) 50vw',
	'lazy' => false,
]);
// <img class='image' src='image.jpg' alt='Image' srcset='image.480x0-srcset.webp 480w, image.1240x0-srcset.webp 1240w' sizes='(min-width: 1240px) 50vw'>
/*
<picture>
	<source srcset="image.480x0-srcset.webp 480w, image.1240x0-srcset.webp 1240w" sizes="(min-width: 1240px) 50vw" type="image/webp">
	<source srcset="image.480x0-srcset.jpg 480w, image.1240x0-srcset.jpg 1240w" sizes="(min-width: 1240px) 50vw" type="image/jpeg">
	<img class='image' src='image.jpg' alt='Image'>
</picture>
*/

// Render an image using custom set rules and sizes
// These rules will render 'portrait' versions of the image for tablet and mobile
// Note the advanced use of the `srcset` option passing both `rules` and image `options`
// WebP is disabled
// Picture is disabled
echo $image->render([
	'srcset' => [
		'rules' => '320x569, 640x1138, 768x1365, 1024, 1366, 1600, 1920',
		'options' => [
			'upscaling' => true,
			'hidpi' => true,
		],
	],
	'sizes' => '(orientation: portrait) and (max-width: 640px) 50vw',
	'picture' => false,
]);
// <img src='image.jpg' alt='' srcset='image.320x569-srcset-hidpi.jpg 320w, image.640x1138-srcset-hidpi.jpg 640w, image.768x1365-srcset-hidpi.jpg 768w, image.1024x0-srcset-hidpi.jpg 1024w, image.1366x0-srcset-hidpi.jpg 1366w, image.1600x0-srcset-hidpi.jpg 1600w, image.jpg 1920w' sizes='(orientation: portrait) and (max-width: 768px) 50vw' loading="lazy">

TextformatterPageimageSource

Bundled with this module is a Textformatter largely based on TextformatterWebpImages by Ryan Cramer. When applied to a field, it searches for <img> elements and replaces them with the default output of Pageimage::render() for each image/image variation.

Assuming a default set of 480, 960 and lazy loading enabled, here are some examples of what would be returned:

Example

<figure class="align_right hidpi">
	<a href="/site/assets/files/1/example.jpg">
		<img alt="" src="/site/assets/files/1/example.300x0-is-hidpi.jpg" width="300" />
	</a>
</figure>

WebP enabled

<figure class="align_right hidpi">
	<a href="/site/assets/files/1/example.jpg">
		<img alt="" src="/site/assets/files/1/example.300x0-is-hidpi.webp" width="300" srcset="/site/assets/files/1/example.300x0-is-hidpi.webp 480w" sizes="100vw" loading="lazy" />
	</a>
</figure>

<picture> enabled

<figure class="align_right hidpi">
	<a href="/site/assets/files/1/example.jpg">
		<picture>
			<source srcset="/site/assets/files/1/example.300x0-is-hidpi.webp 480w" sizes="100vw" type="image/webp">
			<source srcset="/site/assets/files/1/example.300x0-is-hidpi.jpg 480w" sizes="100vw" type="image/jpeg">
			<img alt="" src="/site/assets/files/1/example.300x0-is-hidpi.jpg" width="300" loading="lazy" />
		</picture>
	</a>
</figure>

Because the variation is small - 300px wide - the srcset only returns the source image variation at the lowest set width (480w). If the source image was > 1000px wide, there would be a variation at both 480w and 960w.

PageimageSrcset

This module is built upon work done for PageimageSrcset, which can be considered a first iteration of this module, and is now deprecated.

Migration

PageimageSource is a simplified version of PageimageSrcset with a different approach to rendering. Most of the features of the old module have been removed. If you were just using $image->srcset(), migration should be possible, as this functionality is essentially the same albeit with some improvements to image variation generation.

  • Like 17
  • Thanks 5
Link to comment
Share on other sites

  • 3 weeks later...

Hi @totoff,

It really depends how you've used the module. I probably wouldn't recommend it unless you have just used the sizes call ($image->srcset) without any additional options in the call.

We've got plenty of sites using PageimageSrcset, and almost all of them are just using the srcset call, but I don't plan on changing this as PageimageSrcset works and will continue to. The development of PageimageSource is more about a change in approach, focusing more on extending Pageimage::render().

If you were to attempt a migration, be aware that the majority of the additional functionality of PageimageSrcset is no longer present. It would make sense to tackle a new project with PageimageSource first to get a feel for the differences.

Cheers,

Chris

Link to comment
Share on other sites

  • 1 month later...

@nbcommunication Thanks for this module.

But I'm having a hard time to understand how to implement srcset in my websites.

I have been using PIA (Pageimage assistant) to serve images with fixed width and then using Bootstrap 'image-fluid' class to fit the image.

I have been experimenting with your module:

I have 2 Mac laptops (a new MBA M1, and an old MBP no retina), using defaults rules (320, 640, 768x480 960w, 1024, 2048 2x).

1.- When I inspect the served images I get the same image in both laptops, in my understanding I should get different images based on pixel density of the device.

2.- Served images end up being bigger.

for example, I have this blog section:

629537346_Screenshot2021-06-16at18-00-53Vafamed-EspecialistasenecografiasyelcuidadodelaMujerenCurico.thumb.png.cec43c8c75ce45cd0374024ede1cef14.png

In this layout, every image is 320 * 200 px (weight 21 kb), but the image being serve is 2048 * 1365 px (weight 438 kb) it fits beause it has width: 100% in CSS.

So the question is, what benefit has the user in this scenario?

Or I'm doing wrong?

 

Edited by Krlos
Typos
  • Like 1
Link to comment
Share on other sites

Hi @Krlos,

Apologies for the late reply, I've been on leave.

I actually find srcset/sizes quite hard to get my head around, even more so to try and explain its use! I'd recommend having a read over https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images and perhaps https://www.sitepoint.com/how-to-build-responsive-images-with-srcset/

From what you've posted above, Try using a sizes value e.g. sizes="33vw". You may need this to use media queries e.g sizes="(min-width: 640px) 50.00vw, (min-width: 960px) 33.33vw". You should also experiment with a different srcset configuration - you shouldn't need all the variations from the default rules in this case.

Some browsers cache the largest resolution image loaded and use it regardless of resolution which makes debugging srcset really tricky.

Hope that helps!

Cheers,

Chris

 

Link to comment
Share on other sites

  • 4 weeks later...

Hi @nbcommunication! First of all, thanks for this awesome module! I'm a user of PageimageSrcset too!

I was wondering, how do you handle when webp's are larger in size than the original jpegs?? This happens to me far more often than I'd like to and sometimes it's more than double the size of the jpg image ?

I went ahead and set useUrlOnSize option to true, though the source type attribute then it's kinda wrong although it seems to work just fine!

Anyway, just wondering if you had come across a similar issue. Maybe it's worth being able to set the webp options in the render method?

Edited by elabx
Have no idea how this got duplicated :S
  • Like 2
Link to comment
Share on other sites

Hi @elabx,

Great point, I hadn't thought of this. When we were using GD for image resizing the WEBPs were often larger than the JPEGs, but we got ImageMagick in place around the time I developed this module and forgot about this quirk.

I'll need to do a bit of research before implementing something but I'm a bit swamped at the moment. Will let you know when I figure something out.

Cheers,

Chris

  • Like 2
Link to comment
Share on other sites

  • 1 month later...

Hi @elabx,

Apologies it has taken a while to get back to you.

From what I've been able to determine, the browser selects the first <source> in the list in the absence of media queries. In this case the webP version would always be selected as it comes first.

I've added a couple of things that can help with the issue. The first is the ability to disable the render() extensions, falling back to the default Pageimage:render() method. This is done by passing $options as false. Here's an example, where if the webP image url isn't actually webP due to it being larger it uses the default render().

<?php

echo $pageimages->each(function($pageimage) {
	return strpos($pageimage->webp()->url, '.webp') === false ? // If the webP url is not webp
      	$pageimage->render(false) : // Disables the PageimageSource::render() extensions
  		$pageimage->render();
})

The second is a similar option to the $config->webpOptions option 'useSrcUrlOnSize'. If this is enabled, the module checks to see if the webP url is actually webP (as in the example above) and if it is not (e.g. if it is a .jpg), then it disables webP for that image, which in turn disables the picture option:

<?php

echo $pageimage->render([
	'useSrcUrlOnSize' => true,
]);

This takes the module to v 1.0.2, still in beta.

Cheers,

Chris

  • Thanks 1
Link to comment
Share on other sites

  • 2 months later...

Hi!
Is there any possibility to make use of this module for background images? I am using it only for normal rendered images so far.

The srcset attribute is not usable with background images, I would like to output only one single image based on the viewport width.

Something like this:

<div class="image-wrapper" style="background-image:url('<?= $page->image->size(<DETECT WIDTH HERE>)->url; ?>')">

 

 

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

I have a question about the correct workflow for changing focus on images.

When changing the focus of the image in the PW Backend new image variations with that focus point are generated.

However it seems that the *-srcset variants of those images are not generated new again. So in the frontend you will still see the old image variation.

In order to refresh those images I have to make use of the "remove variations" option in the module settings. 
I find that this is some kind of overkill because I simply want to create new variations of one specific image for example, not every image.

Link to comment
Share on other sites

EDIT: So this here is my final workaround: I wrote a hook that deletes the image variations (only those with the -srcset suffix) for one specific image when the page is saved after a focus change.

$wire->addHookAfter('InputfieldImage::processInputFile', function($event) {

    if ($event['return']) {

        $pagefile = $event->arguments('pagefile');
        $suffix = 'srcset';

        $dir = new \DirectoryIterator($event->object->destinationPath);
        foreach($dir as $file) {

            if(strpos($file->getFilename(), '-' . $suffix . '.') !== false && strpos($file->getFilename(), pathinfo($pagefile->name, PATHINFO_FILENAME)) !== false) {
                $this->wire('files')->unlink($file->getPathname());
                $this->message("Focus Changed. Image variations deleted for $pagefile->name.");
            }
        }

    }

});

 

  • Like 2
Link to comment
Share on other sites

Hi @Stefanowitsch,

Sorry for the late reply. I've had a quick look this morning and for me the variations are being regenerated correctly after a change of focus, but the browser's image cache is the issue - the filename doesn't change after the focus change so it just displays the old one it has cached. Hopefully the brief screencast attached will demonstrate. 

I'm not sure I can remedy this - will have a look when I can to see what's been suggested elsewhere for the issue.

Cheers,

Chris

Link to comment
Share on other sites

Thanks for your reply! 
When testing on my local machine I can verify that the -srcscet variants are not regenerated after a focus change. 
I can confirm that by looking directly at the file system too: The -srcset images still have the old date and the old focus. 
Even when opening the page with a different browser (or deleting the cache) I still see the old image versions.
 

Link to comment
Share on other sites

  • 1 month later...

Hi guys,

I need some help with this gallery:

<?php
            $options = array('cropping' => true, 'upscaling' => false, 'quality' => 80);  // cropping => true is default and same like 'center'

            foreach ($item_gallery->gallery as $img) {
              if ($img->width > $img->height) {              // check for orientation
                $slide = $img->size(2400, 0);  // we have a landscape oriented image
              } else {
                $slide = $img->height(1800);     // we have a portrait oriented image
              }
              // output your markup here
              echo "
                      <figure>
                        <a class='uk-inline' href='{$slide->url}' data-caption='{$slide->description}'>
                          <img class='photo' data-src='{$slide->size(760, 0,$options)->url}' alt='{$slide->description}' uk-img>
                        </a>
                        <figcaption class='uk-hidden@s'>
                          <p>{$slide->description}</p>
                        </figcaption>
                      </figure>
                    ";
            }
            ?>

The goal is to show in a uikit lightbox images that are max. 1200px wide and the max height is 900px. The client will upload images that are 2x bigger, to have resources for the retina output.

How should I define the srcset and the sizes if you have all these different proportions and orientations? I saw that in sizes I can use this: (orientation: portrait)

Thanks in advance.

Link to comment
Share on other sites

Hi @neophron,

Apologies for the late response.

This is a bit of a cop-out answer, but there are no prescriptions for srcset/sizes. What is right is whatever works best for your use case. The only way to get there is to experiment.

That said, perhaps this is what you should do?

<?php

echo "<img class='photo' src='{$slide->size(760, 0, $options)->url}' srcset='{$slide->url} 2x' alt='{$slide->description}' loading='lazy'>";

Regarding the 'orientation: portrait' sizes attribute - this isn't to do with the image's orientation, it is the device orientation. You can use it to specify a size profile for this e.g.

<?php

// Render an image using custom set rules and sizes
// These rules will render 'portrait' versions of the image for tablet and mobile
// Note the advanced use of the `srcset` option passing both `rules` and image `options`
// WebP is disabled
// Picture is disabled
echo $image->render([
	'srcset' => [
		'rules' => '320x569, 640x1138, 768x1365, 1024, 1366, 1600, 1920',
		'options' => [
			'upscaling' => true,
			'hidpi' => true,
		],
	],
	'sizes' => '(orientation: portrait) and (max-width: 640px) 50vw',
	'picture' => false,
]);
// <img src='image.jpg' alt='' srcset='image.320x569-srcset-hidpi.jpg 320w, image.640x1138-srcset-hidpi.jpg 640w, image.768x1365-srcset-hidpi.jpg 768w, image.1024x0-srcset-hidpi.jpg 1024w, image.1366x0-srcset-hidpi.jpg 1366w, image.1600x0-srcset-hidpi.jpg 1600w, image.jpg 1920w' sizes='(orientation: portrait) and (max-width: 768px) 50vw' loading="lazy">

I'm not 100% myself on how the above actually works, but it provides '2x' portrait images for small portrait devices e.g. mobile.

If there's one bit of advice I'd give with srcset, it is keep it simple! I hope that helps.

Cheers,

Chris

  • Like 1
Link to comment
Share on other sites

19 hours ago, nbcommunication said:

Hi @neophron,

Apologies for the late response.

This is a bit of a cop-out answer, but there are no prescriptions for srcset/sizes. What is right is whatever works best for your use case. The only way to get there is to experiment.

I'm not 100% myself on how the above actually works, but it provides '2x' portrait images for small portrait devices e.g. mobile.

If there's one bit of advice I'd give with srcset, it is keep it simple! I hope that helps.

Cheers,

Chris

Hi Chris, thanks for the help. Meanwhile the website I needed it for, is »on stage« with a solution from @elabx. But I'm going to check your code in a cloned version of the website.

Quote

it is keep it simple!

You're right!

 

greetings, Nikolai  

Link to comment
Share on other sites

  • 3 months later...

Today I needed to implement a background image with sources, using the new UIkit Image component implementation: https://getuikit.com/docs/image#picture-sources

PageimageSource doesn't do this out of the box, but I found I neat solution, which I thought I'd share:

<?php

// $bannerImage = Pageimage;

preg_match_all('<source\ssrcset="(.*?)"\ssizes="(.*?)"\stype="(.*?)">', $bannerImage->render(), $matches);

$sources = [];
foreach($matches as $index => $match) {
  if(!$index) continue;
  foreach($match as $i => $v) {
    if(!isset($sources[$i])) {
      $sources[$i] = [];
    }
    $sources[$i][[
      'srcset',
      'sizes',
      'type',
    ][$index - 1]] = $v;
  }
}

echo '<div class="uk-background-cover" sources="' . $sanitizer->entities(json_encode(array_values($sources))) . '" data-src="' . $bannerImage->url . '" data-uk-img></div>';

This assumes that the render() function is returning a <picture> element.

Cheers,

Chris

Edited by nbcommunication
Got the index order incorrect in example
  • Like 5
Link to comment
Share on other sites

I was in the need for using this module together with background images too - but in the "classic" way without any components.

I am using the famous Lazysizes JS Plugin for lazyloading the images. To create responsive background images with the PageImageSouce module I also included the lazysizes bgset extension

So the code in my template file looks like this:

<div class="img-title-wrapper lazyload" data-sizes="auto" data-bgset="<?php echo $image->size($imgFormat)->srcset() ?>">
  /* your content */
</div>

The wrapper element then gets it's height either via CSS (height: 100vh for a big introduction title image) or through the elements inside the container.

Adjust the background image styles to your needs (e.g. background-size:cover).

  • Like 4
Link to comment
Share on other sites

  • 1 month later...

Firstly, thank you for this Module, I installed it yesterday and it works well so far ?

Looking at the code I noticed that it's possible to pass a custom picture-opening-tag as the `picture`-options-argument, which I'm gladly using now to insert custom classes into the picture-element.
I was just wondering why that functionality isn't mentioned in your documentation ^^

Link to comment
Share on other sites

  • 2 months later...

Hi @uiui,

Variations are only generated and used for sizes smaller than the original. When it reaches a dimension where the image does not need to be resized, it 'completes' the generation process.

Running your examples on an image 2048px wide gives me:

/site/assets/files/1033/very_large_array_clouds.webp 4096w

/site/assets/files/1033/very_large_array_clouds.1920x0-srcset.webp 1920w, /site/assets/files/1033/very_large_array_clouds.1600x0-srcset.webp 1600w, /site/assets/files/1033/very_large_array_clouds.1280x0-srcset.webp 1280w, /site/assets/files/1033/very_large_array_clouds.980x0-srcset.webp 980w, /site/assets/files/1033/very_large_array_clouds.480x0-srcset.webp 480w, /site/assets/files/1033/very_large_array_clouds.webp 4096w

This is definitely a quirk of the module's implementation that I hadn't considered - all the srcset examples I referred to during development had the sources ordered smallest to largest - and I don't think it is something I can sort in the default implementation.

Perhaps I could add an option to disable the automatic completion e.g. 'Generate variations for all srcset dimensions?' Would this be useful for you?

Cheers,

Chris

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...