Last active
February 19, 2023 15:28
-
-
Save MoritzLost/a2af68289d8c4cd382baca544c23f30a to your computer and use it in GitHub Desktop.
A twig extension + template for ProcessWire projects that generates responsive images!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// make sure to register the extension on your twig environment | |
$twig_env->addExtension(new \MoritzLost\ResponsiveImages()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{# | |
# Renders a responsive image tag. | |
# | |
# @var ProcessWire\Pageimage image The image to display. | |
# @var array classes Optional classes for the image. | |
# @var int width The standard / fallback width for the image. | |
# @var int height The standard height for the image. | |
# @var array sizes The sizes queries for the responsive image. | |
# @var array variant_factors The variant factors for the responsive image. | |
#} | |
{% set classes = classes|default([]) %} | |
{% set sizes = sizes is not empty ? sizes|join(', ') : '100vw' %} | |
{% set variant_factors = variant_factors is not empty | |
? variant_factors | |
: [0.25, 0.5, 0.75, 1, 1.5, 2] | |
-%} | |
{%- set srcset -%} | |
{%- if width and height -%} | |
{{- get_srcset(image, width, height, variant_factors) -}} | |
{%- elseif width -%} | |
{{- get_srcset_by_width(image, width, variant_factors) -}} | |
{%- elseif height -%} | |
{{- get_srcset_by_height(image, height, variant_factors) -}} | |
{%- else -%} | |
{{- get_srcset(image, null, null, variant_factors) -}} | |
{%- endif -%} | |
{%- endset -%} | |
<img srcset="{{ srcset }}" sizes="{{ sizes }}" src="{{ image.url }}" | |
{%- if image.description %} alt="{{ image.description }}"{% endif -%} | |
{%- if classes is not empty %} class="{{ classes|join(' ') }}"{% endif %}> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MoritzLost; | |
use Processwire\Pageimage; | |
use Twig\Extension\AbstractExtension; | |
use Twig\TwigFunction; | |
class ResponsiveImages extends AbstractExtension | |
{ | |
public function getFunctions() | |
{ | |
return [ | |
new TwigFunction('get_srcset', [$this, 'getSrcset']), | |
new TwigFunction('get_srcset_by_width', [$this, 'getSrcsetByWidth']), | |
new TwigFunction('get_srcset_by_height', [$this, 'getSrcsetByHeight']), | |
]; | |
} | |
/** | |
* Returns a srcset string containing a list of sources for the passed image, | |
* based on the provided width, height, and variant factors. All required images | |
* will be created automatically. | |
* | |
* @param Pageimage $img The base image. Must be passed in the largest size available. | |
* @param int|null $standard_width The standard width for the generated image. Use NULL to use the inherent width of the passed image. | |
* @param int|null $standard_height The standard height for the generated image. Use NULL to use the inherent height of the passed image. | |
* @param array|null $variant_factors The multiplication factors for the alternate resolutions. | |
* @return string | |
*/ | |
public function getSrcset( | |
Pageimage $img, | |
?int $standard_width = null, | |
?int $standard_height = null, | |
array $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2] | |
): string { | |
// use inherit dimensions of the passed image if standard width/height is empty | |
if (empty($standard_width)) { | |
$standard_width = $img->width(); | |
} | |
if (empty($standard_height)) { | |
$standard_height = $img->height(); | |
} | |
$suffix = 'auto_srcset'; | |
// get original image for resizing | |
$original_image = $img->getOriginal() ?? $img; | |
// the default image for the src attribute | |
$default_image = $original_image->size( | |
$standard_width, | |
$standard_height, | |
['upscaling' => false, 'suffix' => $suffix] | |
); | |
// build the srcset attribute string, and generate the corresponding widths | |
$srcset = []; | |
foreach ($variant_factors as $factor) { | |
// round up, srcset doesn't allow fractions | |
$width = ceil($standard_width * $factor); | |
$height = ceil($standard_height * $factor); | |
// we won't upscale images | |
if ($width <= $original_image->width() && $height <= $original_image->height()) { | |
$current_image = $original_image->size($width, $height, ['upscaling' => false, 'suffix' => $suffix]); | |
$srcset[] = $current_image->url() . " {$width}w"; | |
} | |
} | |
$srcset = implode(', ', $srcset); | |
return $srcset; | |
} | |
/** | |
* Shortcut for getSrcset that only takes a width parameter. | |
* Height is automatically generated based on the aspect ratio of the passed image. | |
* | |
* @param Pageimage $img The base image. Must be passed in the largest size available. | |
* @param int|null $standard_width The standard width for this image. Use NULL to use the inherent size of the passed image. | |
* @param array $variant_factors The multiplication factors for the alternate resolutions. | |
* @return string | |
*/ | |
public function getSrcsetByWidth( | |
Pageimage $img, | |
?int $standard_width = null, | |
array $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2] | |
): string { | |
if (empty($standard_width)) { | |
$standard_width = $img->width(); | |
} | |
// automatically fill the height parameter based | |
// on the aspect ratio of the passed image | |
$factor = $img->height() / $img->width(); | |
$standard_height = ceil($factor * $standard_width); | |
return self::getSrcset( | |
$img, | |
$standard_width, | |
$standard_height, | |
$variant_factors | |
); | |
} | |
/** | |
* Shortcut for getSrcset that only takes a height parameter. | |
* Width is automatically generated based on the aspect ratio of the passed image. | |
* | |
* @param Pageimage $img The base image. Must be passed in the largest size available. | |
* @param int|null $standard_height The standard height for this image. Use NULL to use the inherent size of the passed image. | |
* @param array $variant_factors The multiplication factors for the alternate resolutions. | |
* @return string | |
*/ | |
public function getSrcsetByHeight( | |
Pageimage $img, | |
?int $standard_height = null, | |
array $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2] | |
): string { | |
// automatically fill the width parameter based | |
// on the aspect ratio of the passed image | |
if (empty($standard_height)) { | |
$standard_height = $img->height(); | |
} | |
$factor = $img->width() / $img->height(); | |
$standard_width = ceil($factor * $standard_height); | |
return self::getSrcset( | |
$img, | |
$standard_width, | |
$standard_height, | |
$variant_factors | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{# This assumes the responsive-image.twig file lives inside a 'blocks' folder inside your twig directory, and a sidebar_image field on your page #} | |
{{ include( | |
'blocks/image-responsive.twig', | |
{ | |
image: page.sidebar_image, | |
classes: ['sidebar-image', 'img-fluid'], | |
width: 510, | |
sizes: ['(min-width: 1200px) 350px', '(min-width: 992px) 290px', '(min-width: 768px) 330px', '(min-width: 576px) 510px', 'calc(100vw - 30px)'] | |
}, | |
with_context = false | |
) }} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment