1

I have the feeling I'm the only one with such complicated code for my images.
With so many devices with different view ports and landscape / portrait...

How do you all handle your picture tag art-direction responsive stuff?

Now i want some images to be rotated if the device is in portrait mode.
Imager has no transform for rotate. So i need to upload the rotated image and set it as alternative to the other image.

Edit:
The code i have now after changing it:

{% if entry.textImages | length %}

  {% for row in entry.textImages.all() %}

    {% set image = row.textImage.one() %}
    {% set imageWidth = row.textImageWidth %}
    {% set imageHeight = row.textImageHeight %}
    {% set imageClass = row.textImageClass %}

    {% set imagePortrait = row.textImagePortrait.one() %}

    {% set imageType = row.textImageType.value %}
    {% set fileName = image.filename|trim('.' ~ image.extension, 'right') %}

    {% set tempImage = craft.imager.transformImage(image, { width: 2000 },
      {
        allowUpscale: false,
        resizeFilter: 'lanczos',
        effects: { unsharpMask: [0, 0.55, 0.55, 0.008] },
        interlace: false,
        convertToRGB: true
      }
    ) %}

    {% if imageHeight is not empty %}
      {% set tempHeight = tempImage.getHeight() %}
      {% set imageSizes = [{ height: tempHeight },
                           { height: imageHeight * 4.5 },
                           { height: imageHeight * 4.25 },
                           { height: imageHeight * 4 },
                           { height: imageHeight * 3.75 },
                           { height: imageHeight * 3.5 },
                           { height: imageHeight * 3.25 },
                           { height: imageHeight * 3 },
                           { height: imageHeight * 2.75 },
                           { height: imageHeight * 2.5 },
                           { height: imageHeight * 2.25 },
                           { height: imageHeight * 2 },
                           { height: imageHeight * 1.75 },
                           { height: imageHeight * 1.5 },
                           { height: imageHeight * 1.25 },
                           { height: imageHeight }] %}
    {% else %}
      {% set tempWidth = tempImage.getWidth() %}
      {% set imageSizes = [{ width: tempWidth },
                           { width: imageWidth * 4.5 },
                           { width: imageWidth * 4.25 },
                           { width: imageWidth * 4 },
                           { width: imageWidth * 3.75 },
                           { width: imageWidth * 3.5 },
                           { width: imageWidth * 3.25 },
                           { width: imageWidth * 3 },
                           { width: imageWidth * 2.75 },
                           { width: imageWidth * 2.5 },
                           { width: imageWidth * 2.25 },
                           { width: imageWidth * 2 },
                           { width: imageWidth * 1.75 },
                           { width: imageWidth * 1.5 },
                           { width: imageWidth * 1.25 },
                           { width: imageWidth }] %}
    {% endif %}

    {% set transformedImages = craft.imager.transformImage(tempImage, imageSizes,
      {
        format: imageType,
        allowUpscale: false,
        resizeFilter: 'lanczos',
        effects: { unsharpMask: [0, 0.55, 0.55, 0.008] },
        interlace: false,
        convertToRGB: true,
        filenamePattern: fileName ~ '_{fullname|hash}.{extension}'
      }
    ) %}

    {% set transformedImagesWidth = transformedImages[15].getWidth() %}
    {% set imagePortraitSizes = [{ width: transformedImagesWidth * 4.5 },
                                 { width: transformedImagesWidth * 4.25 },
                                 { width: transformedImagesWidth * 4 },
                                 { width: transformedImagesWidth * 3.75 },
                                 { width: transformedImagesWidth * 3.5 },
                                 { width: transformedImagesWidth * 3.25 },
                                 { width: transformedImagesWidth * 3 },
                                 { width: transformedImagesWidth * 2.75 },
                                 { width: transformedImagesWidth * 2.5 },
                                 { width: transformedImagesWidth * 2.25 },
                                 { width: transformedImagesWidth * 2 },
                                 { width: transformedImagesWidth * 1.75 },
                                 { width: transformedImagesWidth * 1.5 },
                                 { width: transformedImagesWidth * 1.25 },
                                 { width: transformedImagesWidth }] %}

    {% set transformedPortraitImages = craft.imager.transformImage(imagePortrait, imagePortraitSizes,
      {
        format: imageType,
        allowUpscale: false,
        resizeFilter: 'lanczos',
        effects: { unsharpMask: [0, 0.55, 0.55, 0.008] },
        interlace: false,
        convertToRGB: true
      }
    ) %}

    <picture>
      {% if imagePortrait is not empty %}
        <source media="(orientation: portrait)"
                sizes="80vw"
                srcset="{{ craft.imager.srcset(transformedPortraitImages) }}">
      {% endif %}
      <img class="anchor {% if imageClass is not empty %}{{ imageClass }}{% endif %}"
           src="{{ transformedImages[15].getUrl() }}"
           sizes="{% if row.textImageVW is not empty %}
                    (orientation: portrait) {{ row.textImageVW }}vw,
                    (orientation: landscape) and (max-width: 799px) {{ row.textImageVW }}vw,
                  {% else %}
                    (orientation: portrait) 80vw,
                    (orientation: landscape) and (max-width: 799px) 60vw,
                  {% endif %}
                  {% if imageHeight is not empty %}{{ transformedImagesWidth }}{% else %}{{ imageWidth }}{% endif %}px"
           srcset="{{ craft.imager.srcset(transformedImages) }}"
           title="{{ image.title }}"
           alt="{{ image.altText }}">
    </picture>

  {% endfor %}

{% endif %}

And this is a entry:
enter image description here

To calculate the width for the portrait view image i use the width of the transformed landscape image (transformedImagesWidth). The reason is that the images should align.

You can see it here:
Phantasialand Hörspiel & Videokassetten
On a PC you see the case and two tapes in one row.
On a phone they are one below the other and the tapes rotate.

Only thing i don't like right now is that if i rotate the phone to landscape that i get the landscape image. But below 800px it should still show the portrait image.

  • I think you are over-complicating things. Why not just crop the image to different proportions for different size screens? Alternatively, If you really do think you need to rotate the image, and I can't see why you would, do it with css by simply adding a class like .rotate-90 as required. – Seth Warburton Sep 13 '19 at 14:00
  • @Seth Warburton i need to rotate because it makes no sense to crop a cassette/vhs tape. I tried it with CSS but only CSS does not work without JavaScript to recalculate the viewport. – MrGlasspoole Sep 13 '19 at 14:24
  • Re-calulate the viewport?? Just use add height and width attrs to your <img, no reflow, no .js required. – Seth Warburton Sep 13 '19 at 14:37
  • @SethWarburton How do you know height and width? In sizes you set vw and in CSS you can only use %. If i rotate in CSS on "@media all and (orientation:portrait)" the picture goes over the edges of the screen. Also setting 80% there will not work because that is 80% of the image and not screen. Also that way the browser will not download the smaller image (bandwidth). Isn't the picture tag not for delivering different art-direction? – MrGlasspoole Sep 13 '19 at 15:34
  • @Seth Warburton Also JS would not work: developer.mozilla.org/en-US/docs/Learn/HTML/… – MrGlasspoole Sep 15 '19 at 23:27
2

1.

If you need a rotated version of an image but Imager can't transform the image programmatically, you could just create 2 different asset fields for the same image. One field for the landscape orientation, and the other for portrait.

In that table view example you showed above I'd imagine you would need 2 columns: Image Landscape and Image Portrait.

You could access those images in your template with something like this:

{% for row in entry.textImages.all() %}

  {% set imagePortrait = row.textImagePortrait.one() ?? null %}
  {% set imageLandscape = row.textImageLandscape.one() ?? null %}

  {% if imagePortrait %}
    ...
  {% endif %}

  {% if imageLandscape %}
    ...
  {% endif %}

{% endfor %}

2.

If you want to swap out a different images (size, cropping, orientation, etc) you need "art direction."

sizes and srcset attribtues work together to help the browser choose the right resolution image. But ultimately it's up to the browser which image to use.

The picture and source tags explicitly tell the browser what to do based on the media attribute. "Browser do this..."

Here's the most basic example of serving a different image for portrait orientation (landscape is the default):

<picture>
  <source media="(orientation: portrait)" srcset="portrait.jpg">
  <img src="landscape.jpg" alt="">
</picture>

Codepen example: Art direction image with orientation, simple

Then you can add on different sizes a resolutions to each source by expanding on srcset and sizes.

<picture>
    <source media="(orientation: portrait)"
            srcset="portrait.jpg 400w,
                    portrait-md.jpg 600w,
                    portrait-lg.jpg 800w" 
            sizes="(min-width: 400px) 400px,
                   100vw">
    <img src="landscape.jpg"
         srcset="landscape.jpg 600w,
                 landscape-md.jpg 800w,
                 landscape-lg.jpg 1200w"
         sizes="(min-width: 600px) 600px,
                100vw"
         alt="">
</picture>

Codepen example: Art direction image with orientation, complex

In that example, both images start out 100% of the viewport width on small screens. But the portrait image doesn't get larger than 400px wide, and the landscape image doesn't get larger than 600px.


3.

Since you have to transform 2 images with similar options, there's a few ways you can optimize this.

You can store your array of options to reuse:

{% set imageOptions = {
  format: imageType,
  allowUpscale: false,
  resizeFilter: 'lanczos',
  effects: { unsharpMask: [0, 0.55, 0.55, 0.008] },
  interlace: false,
  convertToRGB: true,
  filenamePattern: fileName ~ '_{fullname|hash}.{extension}'
} %}

You can use the fillTransforms option in Imager to fill in sizes between your min an max size.

{# Taken from Imager docs #}
{% set transformedImages = craft.imager.transformImage(image, [
    { width: 1200 }, 
    { width: 400 }
    ], { ratio: 16/9, position: image.getFocalPoint() }, 
    { fillTransforms: true }) %}

Here's what this might look like when you output the image:

{% set transformedPortraitImages = craft.imager.transformImage(imagePortrait, [
    { height: 2000 },
    { height: 400 }
  ], imageOptions, { fillTransforms: true }) %}

{% set transformedLandscapeImages = craft.imager.transformImage(imageLandscape, [
    { width: 2000 },
    { width: 400 }
  ], imageOptions, { fillTransforms: true }) %}


<picture>
    {% if imagePortrait %}
      <source media="(orientation: portrait)"
              srcset="{{ craft.imager.srcset(transformedPortraitImages) }}"
              sizes="...">
    {% endif %}  
    <img src="{{ transformedLandscapeImages[(transformedLandscapeImages|length - 1)].url }}"
         srcset="{{ craft.imager.srcset(transformedLandscapeImages) }}"
         sizes="..."
         alt="">
</picture>

This code may not meet your needs completely, but hopefully this is enough to set you on the right path.

| improve this answer | |
  • The problem is more how to handle it in Craft than the HTML. I also thought about the second column. Problem is then a can not set extra style/css on that image. Also i'm not sure how to get the image from the second column (code). Something like this would be nice: i.imgur.com/i0ahCeM.jpg – MrGlasspoole Sep 15 '19 at 12:04
  • Okay, I added an example of accessing 2 images in a row in the template. – Alex Roper Sep 15 '19 at 12:41
  • What does the "?? null" do? First time i see it. That way it means i have to put the Imager code twice in my template. Also I'm not sure if the width and height settings also work on the portrait images. – MrGlasspoole Sep 15 '19 at 12:53
  • 1
    ?? is the null-coalescing operator. twig.symfony.com/doc/2.x/templates.html#other-operators. It prevents errors when calling a method like .one() if there isn't asset selected. Here's an excellent explanation of how and why to use it: straightupcraft.com/events/twigs-null-coalescing-operator – Alex Roper Sep 15 '19 at 20:59
  • Yes, you would transform both portrait and landscape images. I can add some suggestions for simplifying that. – Alex Roper Sep 15 '19 at 21:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.