Jump to content

PageimageSource


nbcommunication

Recommended Posts

Hi @uiui,

I've added in this option and released v1.0.4.

From the README:

Use for all dimensions?

If enabled, a set rule will be used regardless of whether it is wider or higher than the dimensions of the original image.

To use this on a srcset call, enable the allSets option:

$srcset = $image->srcset('4096, 2048, 1024, 512, 256', [
	'allSets' => true,
]);
// If passing an image 2000px in width, the above would return a srcset with set rules for each given width
// Without 'allSets' enabled, the srcset generation would terminate at 4096w

 

I hope that is useful!

Cheers,

Chris

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

17 hours ago, fuzenco said:

@nbcommunication Don’t you mean it would terminate at 1024? That’s the next stop down from the 2000 original width.

No, it would terminate at 4096. Because this width is greater than the original, it won't generate a variation and it'll output the original as the source for 4096w. It then wouldn't make sense to output the original as the source for 2048w as it has already been used. This makes more sense in the smallest to largest context:

256 - variation generated
512 - variation generated
1024 - variation generated
2048 - width > than original, original used
4096 - set not output as original already used for 2048w.

I hope that makes sense.

Cheers,

Chris

  • Like 1
Link to comment
Share on other sites

Thx @nbcommunication for the module. It looks great. It's my first time using responsive image sizes (shame on me) and I have a question:

I'm using this ruleset:

Quote

320
640
1024
2048 2x

On a 500px screen with 1x pixel ratio this loads the 640 image. Great.

But on a 2x pixel ratio screen it always loads the 2048 image. Not so great ? 

Can I somehow tell my browser to load a 1280 (2*640) image on 500 pixel screen?

Link to comment
Share on other sites

Hi @bernhard,

Honestly, I find the way in which browsers actually implement srcset and sizes to be really confusing. In the previous PageimageSrcset I included a bit of javascript for debugging, as I found it was sometime difficult to even tell which image was being used (if they were just different width variations). What complicates debugging is that the browser will use the largest image it has in the cache, so if you've loaded the page at desktop width and then try to see how it'll respond at a smaller screen size, it will still display the larger image.

What happens if you add 1280 2x into the set?

Cheers,

Chris

  • Like 3
Link to comment
Share on other sites

2 hours ago, nbcommunication said:

Hi @bernhardWhat complicates debugging is that the browser will use the largest image it has in the cache, so if you've loaded the page at desktop width and then try to see how it'll respond at a smaller screen size, it will still display the larger image.

I use Firefox when debugging srcset as unlike Chrome it doesn't do this!

  • Like 3
Link to comment
Share on other sites

  • 4 weeks later...
On 10/13/2022 at 11:53 AM, bernhard said:

Thx @nbcommunication for the module. It looks great. It's my first time using responsive image sizes (shame on me) and I have a question:

I'm using this ruleset:

On a 500px screen with 1x pixel ratio this loads the 640 image. Great.

But on a 2x pixel ratio screen it always loads the 2048 image. Not so great ? 

Can I somehow tell my browser to load a 1280 (2*640) image on 500 pixel screen?

@bernhard Imho modifiers like 2x should not be necessary at all. It took me quite some time and testing to wrap my head around responsive images and my experiments with the current browsers showed that whenever my sizes attribute was accurate, the browser chose the closest image resolution from the sourceset taking pixel density of the screen into account automatically.

This is a (rather complex) example for my usage of PageimageSource on a page with a masonry style layout that can have from 1 to 5 columns and the sizes also take padding etc. into account (which might be overkill):

$img_markup = $img->render([
  'picture' => true,
  'srcset' => ['360', '480', '640', '800'], 
  'sizes' => '(max-width: 579px) calc(100vw - (2 * 34px)), 
	(min-width: 580px) and (max-width: 767px) calc(50vw - (1.5 * 34px)),
	(min-width: 768px) and (max-width: 1199px) calc(33.3333vw - (1.33 * 34px)),
	(min-width: 1200px) and (max-width: 1499px) calc(25vw - (1.25 * 34px)),
	(min-width: 1500px) calc(20vw - (1.2 * 34px))',
  'class' => 'teaser-img proportional',
  'alt' => $img->description,
  'markup' => "<img src='{$img->width(480)->url}' alt='{alt}' class='{class}' width='".$img->width(480)->width."' height='".$img->width(480)->height."'>"
]);

Output:

<picture>
  <source srcset="/site/assets/files/1200/1_klimanagepasster_wohnunsgbau.360x0-srcset.webp 360w, /site/assets/files/1200/1_klimanagepasster_wohnunsgbau.480x0-srcset.webp 480w, /site/assets/files/1200/1_klimanagepasster_wohnunsgbau.640x0-srcset.webp 640w, /site/assets/files/1200/1_klimanagepasster_wohnunsgbau.800x0-srcset.webp 800w" sizes="(max-width: 579px) calc(100vw - (2 * 34px)), (min-width: 580px) and (max-width: 767px) calc(50vw - (1.5 * 34px)), (min-width: 768px) and (max-width: 1199px) calc(33.3333vw - (1.33 * 34px)), (min-width: 1200px) and (max-width: 1499px) calc(25vw - (1.25 * 34px)), (min-width: 1500px) calc(20vw - (1.2 * 34px))" type="image/webp">
  <source srcset="/site/assets/files/1200/1_klimanagepasster_wohnunsgbau.360x0-srcset.jpg 360w, /site/assets/files/1200/1_klimanagepasster_wohnunsgbau.480x0-srcset.jpg 480w, /site/assets/files/1200/1_klimanagepasster_wohnunsgbau.640x0-srcset.jpg 640w, /site/assets/files/1200/1_klimanagepasster_wohnunsgbau.800x0-srcset.jpg 800w" sizes="(max-width: 579px) calc(100vw - (2 * 34px)), (min-width: 580px) and (max-width: 767px) calc(50vw - (1.5 * 34px)), (min-width: 768px) and (max-width: 1199px) calc(33.3333vw - (1.33 * 34px)), (min-width: 1200px) and (max-width: 1499px) calc(25vw - (1.25 * 34px)), (min-width: 1500px) calc(20vw - (1.2 * 34px))" type="image/jpeg">
  <img src="/site/assets/files/1200/1_klimanagepasster_wohnunsgbau.480x0.jpg" alt="Klimanagepasster Wohnunsgbau" class="teaser-img proportional" width="480" height="282" loading="lazy">
</picture>

@nbcommunication Great module, I like it a lot! Nevertheless my example also shows a little incovenience I experienced when using PageimageSource: I need to output width and height attributes for my img tag and using the markup option (as shown above) was the only way to accomplish this. Using the height and width options of the render method rendered the srcset useless (all the same sizes). Maybe you could implement an option that outputs the size of the first entry from the srcset as width and height attributes of the img? Especially when using lazyloading the width and height attributes serve as proportional placeholders and prevent layout shifts.

Cheers,
Flo

  • Like 2
Link to comment
Share on other sites

Hi @snck,

Thanks for the feedback and the great example!

Unfortunately the issue you've raised is more of an issue with how Pageimage::render() is implemented. Adding 'width' or 'height' options is interpreted as a request to resize the Pageimage, which is not what we want in PageimageSource::render(). The default markup does not include width and height attributes, so the only way to add them without resizing the image is to provide them in a specified markup string.

I have a hook in an internal development module which sets a default markup string with these attributes:

<?php

// Pageimage:render
$this->addHookBefore('Pageimage::render', function(HookEvent $event) {
     $markup = $event->arguments(0);
     $options = $event->arguments(1);
     if(!is_string($markup)) {
        $options = $markup;
        $markup = '<img src="{url}" alt="{alt}" width="{width}" height="{height}">';
     }
     $event->arguments(0, $markup);
     $event->arguments(1, $options);
});

This means I can generally call render() without any options. If your example is something you are doing repeatedly, you could utilise a similar strategy in site/ready.php:

<?php

// /site/ready.php
$wire->addHookBefore('Pageimage::render', function(HookEvent $event) {

	$markup = $event->arguments(0);
	$options = $event->arguments(1);

	if(!is_string($markup)) $options = $markup;

	if($options['masonry'] ?? false) { // a custom option

		$img = $event->object;
		$thumb = $img->width(480);

		$markup = "<img src='$thumb->url' alt='{alt}' class='{class}' width='$thumb->width' height='$thumb->height'>";
		$options = array_merge([
			'picture' => true,
			'srcset' => [360, 480, 640, 800], 
			'sizes' => '(max-width: 579px) calc(100vw - (2 * 34px)), 
				(min-width: 580px) and (max-width: 767px) calc(50vw - (1.5 * 34px)),
				(min-width: 768px) and (max-width: 1199px) calc(33.3333vw - (1.33 * 34px)),
				(min-width: 1200px) and (max-width: 1499px) calc(25vw - (1.25 * 34px)),
				(min-width: 1500px) calc(20vw - (1.2 * 34px))',
			'class' => 'teaser-img proportional',
			'alt' => $img->description,
		], $options);
	}
	
	$event->arguments(0, $markup);
	$event->arguments(1, $options);
});

// In your code
$img->render([
	'masonry' => true,
	'class' => 'overriding-the-default-class',
	'alt' => __('Overriding the default alt text'),
]);

// Not tested!!!

Cheers,

Chris

  • Like 3
Link to comment
Share on other sites

On 11/11/2022 at 11:59 AM, nbcommunication said:

Unfortunately the issue you've raised is more of an issue with how Pageimage::render() is implemented. Adding 'width' or 'height' options is interpreted as a request to resize the Pageimage, which is not what we want in PageimageSource::render(). The default markup does not include width and height attributes, so the only way to add them without resizing the image is to provide them in a specified markup string.

I have a hook in an internal development module which sets a default markup string with these attributes...

@nbcommunication Thank you for clarification and the great example. I will definitely keep this in mind! 🙂

Link to comment
Share on other sites

  • 1 month later...

I have a question about a possible feature. I read that you are using UIKit. UIkit offers the possibility to use multiple image sources and resolutions for one image.

Like this: (example taken from: https://getuikit.com/docs/image)

<div class="uk-height-medium uk-flex uk-flex-center uk-flex-middle uk-background-cover uk-light"
     sources="srcset: https://images.unsplash.com/photo-1464621922360-27f3bf0eca75?fit=crop&w=650&h=433&q=80 650w,
                      https://images.unsplash.com/photo-1464621922360-27f3bf0eca75?fit=crop&w=1300&h=866&q=80 1300w; 
              media: (min-width: 1200px)"
     data-src="https://images.unsplash.com/photo-1472803828399-39d4ac53c6e5?fit=crop&w=650&h=433&q=80"
     data-srcset="https://images.unsplash.com/photo-1472803828399-39d4ac53c6e5?fit=crop&w=650&h=433&q=80 650w,
                  https://images.unsplash.com/photo-1472803828399-39d4ac53c6e5?fit=crop&w=1300&h=866&q=80 1300w"
     sizes="(min-width: 650px) 650px, 100vw" uk-img>
    <h1>Background Image</h1>
</div>

I would like to achieve the same markup but with the use of your module. Would this be possible? 

Right now I use a device check (mobile, tablet, desktop) to see which image resolution I want to use and render the image that way:

<?php echo $image->size($imgFormat)->srcset() ?>

The set rules are defined globally in the module settings. 

So the image gets cropped into the desired format (=custom resolution), then the srcset is generated. I am using different resolutions for different screen sizes: 16:9 on desktop, 4:3 on Tablet, 1:1 on mobile (for example) so just downscaling the image would not work for me.

The problem is that this approach is not responsive in that way that the image format automatically changes when resizing the viewport. In fact you have to reload the page so that the new image format is applied and rendered.

 

Link to comment
Share on other sites

1 hour ago, nbcommunication said:

Hi @Stefanowitsch,

I've used this UIkit implementation in a few recent projects, I wrote up how I did it here: 

Hope that helps!

Cheers,

Chris

 

I am able to get this code to work. But I don't know where to put the information for which picture source to use. On smaller screens I want to display a different image.

 

Link to comment
Share on other sites

Hi @Stefanowitsch,

Try:

<?php

$img = $image->render([
	'srcset' => [
		'rules' => '320x569, 640x1138, 768x1365, 1024, 1366, 1600, 1920', // first three rules here are for portrait images
		'options' => [
			'upscaling' => true,
			'hidpi' => true,
		],
	],
	'sizes' => '(orientation: portrait) and (max-width: 640px) 50vw',
]);

This is adapted from an example in the README which itself was adapted from an implementation in PageimageSrcset - I can't remember exactly how this works but I have used it in the past for portrait images on mobile.

Cheers,

Chris

  • Like 1
Link to comment
Share on other sites

2 hours ago, nbcommunication said:

Hi @Stefanowitsch,

Try:

<?php

$img = $image->render([
	'srcset' => [
		'rules' => '320x569, 640x1138, 768x1365, 1024, 1366, 1600, 1920', // first three rules here are for portrait images
		'options' => [
			'upscaling' => true,
			'hidpi' => true,
		],
	],
	'sizes' => '(orientation: portrait) and (max-width: 640px) 50vw',
]);

This is adapted from an example in the README which itself was adapted from an implementation in PageimageSrcset - I can't remember exactly how this works but I have used it in the past for portrait images on mobile.

Cheers,

Chris

Hi Chris!

Thanks, now I get where my problem lies.

Can you answer me one question:

The rules that are defined here do crop the image, right? Does it work the same as using the Pageimage:size() method?

$image->size(400, 300);

I am worrying if the focus point which can be set for one image individually will still be used when cropping via the srcet rule.

Another thing:

When scaling down the browser window from very large (1920+) to mobile-small (640) the image which is loaded does not change. Even if I define custom aspect ratios. This is the intended and normal behaviour of a browser I guess. A large image that is loaded will look good on a small viewport too. So there is no need to load a new version.

The other way around it works as you would expect: Going from a small viewport to a large one the image updates to larger versions step by step.

I would like to update the image if the viewport shrinks, too. Which seems to work together with UIKit.

 

 

 

 

Link to comment
Share on other sites

Hi @Stefanowitsch

20 hours ago, Stefanowitsch said:

The rules that are defined here do crop the image, right? Does it work the same as using the Pageimage:size() method?

Yes, and the same focus point would be used.

I think if you are looking for a different focus point / crop you might be better off trying to output the <picture> html manually.

I see from the source code that the 2nd argument (options) is passed to the size() call. Might be worth trying adding 'cropping' to this - https://processwire.com/api/ref/pageimage/size/ - and see if it works! 

As for updating the image when the viewport shrinks... I've no idea I'm afraid. If the UIkit implementation can do this - that is it doesn't use the largest image loaded but always the one specified by sizes - you'd need to ask their devs if it isn't working. 

Actually come to think of it, why not render two images and use uk-hidden@s and uk-visible@s classes to toggle between them?

Cheers,

Chris

  • Like 1
Link to comment
Share on other sites

  • 5 months later...

Hey @nbcommunication, I have a strange problem here. I am using the following code:

$img->render([
		'picture' => true,
		'srcset' => [
		'rules' => ['800x267', '1600x534'],
		'options' => [
			'upscaling' => true,
		],
	], 
	'allSets' => true,
	'sizes' => '(max-width: 900px) 100vw,
		((min-width: 901px) and (max-width: 1200px)) 67vw,
		((min-width: 1201px) and (min-width: 1201px)) 50vw',
	'class' => $img_class,
	'alt' => $alt,
	'markup' => "<img src='".$img->size($img_tag_width, $img_tag_height)->url."' alt='{alt}' class='{class}' width='".$img->size($img_tag_width, $img_tag_height)->width."' height='".$img->size($img_tag_width, $img_tag_height)->height."'>",
]);

My image has a width of 1600px, but a different aspect ratio (original dimensions: 1600 x 1093). I am expecting to get two variations rendered ('800x267', '1600x534'), but instead of a scaled/cropped 1600px wide version, I get the original image, which is bad, because it has the wrong aspect ratio. How can I force PageimageSource to generate the variation with the correct dimensions?

Thank you in advance!

Link to comment
Share on other sites

Hi @snck,

This is definitely a bug. The quick solution would be to resize your image before calling render (e.g. $img->size(1600, 534)) but I've pushed a fix which should resolve this.

Please download the latest version (1.0.5) and let me know if that hasn't fixed it for you. If not could you please let me know what the values for $img_tag_width and $img_tag_height are in the 'markup' option as I'll need this to debug further.

Cheers,

Chris

  • Like 2
Link to comment
Share on other sites

On 6/1/2022 at 4:42 AM, Stefanowitsch said:

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).

Hi, @Stefanowitsch, can you please explain how you managed to use the module for background images? I have tried to use, but I only get the code for <img> and I'm trying to add my background images using inline css to make it work with Tailwindcss.

Thank you.

Link to comment
Share on other sites

On 6/25/2023 at 4:21 AM, Krlos said:

Hi, @Stefanowitsch, can you please explain how you managed to use the module for background images? I have tried to use, but I only get the code for <img> and I'm trying to add my background images using inline css to make it work with Tailwindcss.

Thank you.

To use background images I use the following plugins:

1. Lazysizes: https://github.com/aFarkas/lazysizes
2. The Lazysizes bgset extension: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/bgset

Note: The bgset extension must be loaded before the Lazysizes plugin.

That's why I disabled the "Use Lazy Loading" in the module settings and load both files above manually in the correct order.

image.png.dba9a4cfb42d718ef931e2f6b9fbc8b4.png

To make use of a background image this code here works for me:

<div class="img-title-wrapper lazyload" data-sizes="auto" data-bgset="<?php echo $image->size($imgFormat)->srcset() ?>">
  /* your content */
</div>
  • Like 1
Link to comment
Share on other sites

On 6/26/2023 at 2:54 AM, nbcommunication said:

Hi @Krlos,

Can you please post a code example? Are you are just calling render() without providing a markup template?

Cheers,

Chris

Hi @nbcommunication, yes I'm using:

 <section class="about-section pt-48 pb-48" style="background-image: url(<?php echo $page->images->render(); ?>); background-repeat: no-repeat; background-size: cover; background-position: center;">

I'm doing it wrong? I thought it was enough to define the sizes in the module configuration.

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...