Jump to content

Responsive Image Breakpoints with Field Templates


DaveP
 Share

Recommended Posts

Background - I came across http://www.responsivebreakpoints.com/ the other day and thought it was a nice idea, but that could be done in PW using the API. In a nutshell, what it does is create an image width breakpoint at roughly every 20kb of file size between a minimum and maximum pixel size.
 
According to this article on CSS-Tricks, "If you’re just changing resolutions, use srcset", so the markup is as suggested there.
 
There is already the excellent Srcset Image Textformatter which works on images in RTE fields, but if you want responsive images elsewhere in your templates, you need to do the markup and decide on breakpoint sizes yourself. However, Field Templates have got you covered!
 
Just save this as a field template file in /site/templates/fields/my_image.php as described above.

<?php
$maxWidth = 1000; //largest breakpoint
$minWidth = 200; //smallest breakpoint
$srcQuality = 40; //jpeg quality of the 'src' image
$srcsetQuality = 80; //jpeg quality of the 'srcset' images
$breakpointStepFileSize = 20; //i.e. 20kb
$class = ""; //change this if you want to add eg "class='responsive'"

$horizAspect = $value->width / $value->height;

$minSizeArea = round($minWidth * ($minWidth / $horizAspect));
$maxSizeArea = round($maxWidth * ($maxWidth / $horizAspect));
$areaDiff = $maxSizeArea - $minSizeArea;

$minFile = $value->width($minWidth, array('quality' => $srcsetQuality));
$maxFile = $value->width($maxWidth, array('quality' => $srcsetQuality));

$minFileSize = $minFile->filesize;
$maxFileSize = $maxFile->filesize;
$fileSizeDiff = $maxFileSize - $minFileSize;

if($fileSizeDiff > ($breakpointStepFileSize * 1024)){
  $numBreakpoints = round($fileSizeDiff / ($breakpointStepFileSize * 1024));
  for($s = 1; $s < $numBreakpoints; $s++){
    $breakpointStepArea = $minSizeArea + (($areaDiff / $numBreakpoints) * $s);
    $breakpointWidth = round(sqrt($breakpointStepArea * $horizAspect));
    $breakpoints[] = $breakpointWidth;
  }
}
$src = $value->width($maxWidth, array('quality' => $srcQuality))->url;
$min = "$minFile->url {$minWidth}w, ";
$out = "<img src='$src' srcset='$min";
foreach($breakpoints as $breakpoint){
  $bp = $value->width($breakpoint, array('quality' => $srcsetQuality))->url;
  $out .= "$bp {$breakpoint}w, ";
}
$out .= "$maxFile->url {$maxWidth}w";
$out .= "' alt='$value->description' $class>";
echo $out;

Then use something like 

echo $page->render->my_image;

in your page template and you'll get something like

<img src='/site/assets/files/1/photo.1000x0.jpg' srcset='/site/assets/files/1/photo.200x0.jpg 200w, /site/assets/files/1/photo.482x0.jpg 482w, /site/assets/files/1/photo.651x0.jpg 651w, /site/assets/files/1/photo.785x0.jpg 785w, /site/assets/files/1/photo.899x0.jpg 899w, /site/assets/files/1/photo.1000x0.jpg 1000w' alt='pic' >

(Bear in mind that PW has to create all these image variations on first page load, so it will take a moment.)

Give it a try and see what you think!

  • Like 14
Link to comment
Share on other sites

Just my opinion, but this type of functionality should be included in the core image functions as each image is uploaded and saved. We as developers nust account for the greater diversity in user devices in the future. Having images processed on the backend (vs frontend page render) based on styles specific to a project (config options) would be tremendously helpful. Set it and forget it, type of thing. :)

  • Like 2
Link to comment
Share on other sites

I just put this teeny weeny code at the top of my main.css where I initialize site wide css:

img {
     height: auto;
     max-width: 100%;
     width: auto;
}

With @media rules I simply rearrange/stack boxes holding images.

Less is more, so to speak, works all the time.

Edit: Not relevant,

See next LostKobraKai's post

Link to comment
Share on other sites

@pwired

It's not about fitting images, but about serving images in an appropriate size to each user. Mobile users will thank you about not needing to download that 1920 * 400 herobanner your using on your biggest layout breakpoint.

@rick

Such things would probably already be in the core if image resizing wouldn't be such a resource consuming task on multiple fronts.

  • Like 2
Link to comment
Share on other sites

@rick

Such things would probably already be in the core if image resizing wouldn't be such a resource consuming task on multiple fronts.

Resource intensive is exactly why processing should not be done on the front end. Even cached, the first time requires processing. That first impression affects the overall perception by the client and customer. As an admin user, having an option to enable this functionality would be, I believe, an acceptable issue because the benefit of having site images pre-processed would outweigh that performance issue. Just my $.02.

Link to comment
Share on other sites

I wasn't talking about having this done on demand. No matter when this resizing does happen it's a hit on the server's resources. Even generating the one thumbnail for the admin backend (on upload) can take around a second, not talking about multiple thumbs per file. Imagine someone throwing 10 images in the image field and going to grab some coffee before he can continue. As always it's up to the individual to implement such a system on their own if really needed.

  • Like 1
Link to comment
Share on other sites

@Rick: If an author modifies a page, i would assume that he inspect the result on the frontend, before publishing a page. Exactly with this inspection, he raises the first page load and all imagevariations get created. And as an author or admin user, he has / should take this time to wait. After that, the first regular visitor gets presented the cached variations. Hhm, or not? :)

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

@Rick: If an author modifies a page, i would assume that he inspect the result on the frontend, before publishing a page. Exactly with this inspection, he raises the first page load and all imagevariations get created. And as an author or admin user, he has / should take this time to wait. After that, the first regular visitor gets presented the cached variations. Hhm, or not? :)

Each visitor to a web page has to have the content initially downloaded so it makes sense to process the imagery prior to going live on a web site. Each visitor would then have the optimum browsing efficiency on that particular device.

I personally code all my sites with no caching and my testing browsers set to not cache. That is, I make a complete request with every request. Once I have a site 'tuned', then I turn the headers back on.

Link to comment
Share on other sites

Each visitor to a web page has to have the content initially downloaded so it makes sense to process the imagery prior to going live on a web site. .....

 
Sure, but horst's idea does not rule it out.
 
If you process the image right after the upload, you (the admin user) need to wait for the resizing process, but what if you change your mind before checking the related page on the frontend and decide to re-upload the image? In that case you need to wait twice, or more (depending on how many times you have changed your mind). So the answer to this question is not as straightforward as it might seem at first.
  • Like 1
Link to comment
Share on other sites

I personally code all my sites with no caching and my testing browsers set to not cache. That is, I make a complete request with every request. Once I have a site 'tuned', then I turn the headers back on.

Browser caching does not have anything to do with the image resizing process. Browser caching is just about downloading the already resized image each time. Actually you cannot deactivate the server side caching of images. It's just about if the generation of the resized images happens directly after the upload or on the first request to the image.

Link to comment
Share on other sites

Thanks for sharing this code...

I thing it's not the best to add this to the core because there are more then one way to implement. I.e. in WordPress there are 3 differrent (fixed) sizes. But personally I think this approach is much better even it may produces more images.

What I've added only is the "sizes" attribute, because without this you always have 100vw, so it may stretch the image above 100% of the "physical" image width if you have no wrapper around it.

    $min = "";
    if ($maxWidth != $minWidth) // because I'm using parameters, this is checked for the same values 
        $min = "$minFile->url {$minWidth}w, ";

    $out = "<img src='$src' srcset='$min";
    foreach($breakpoints as $breakpoint){
      $bp = $imgObj->width($breakpoint, array('quality' => $srcsetQuality))->url;
      $out .= "$bp {$breakpoint}w, ";
    }
    $out .= "$maxFile->url {$maxWidth}w";
    $out .= "' sizes='(min-width: {$maxWidth}px) {$maxWidth}px', 100vw";
    $out .= "' alt='$imgObj->description' $class>";
    return $out;

Because it's difficult to set always indvidual "sizes" values on each image output, I'm just using this one above.

So if the width same or large as the physical width, use the maxWidth and don't scale.

I'm wondering about Firefox because he doesn't show me all srcsets if I'm using the Inspector, but the network analysis show me the right selection of image.

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
 Share

×
×
  • Create New...