ErikHen / PictureRenderer.Optimizely

PictureRenderer for Optimizely CMS (former Episerver)
MIT License
13 stars 4 forks source link

PictureRenderer.Optimizely

PictureRenderer for Optimizely CMS (former Episerver).
Makes it super simple to render html picture element for your Optimizely CMS MVC/Razor pages. CMS editors don't have to care about image sizes or formats. The most optimal image will always be used depending on the capabilities, screen size, and pixel density of the device that is used when visiting your web site.
The result is optimized (width, format, quality), lazy loaded, and responsive images.
PictureRenderer.Optimizely can use Cloudflare Image Resizing or ImageSharp for resizing images.

If you are unfamiliar with the details of the Picture element I recommend reading this and/or this.

How to install

1. Define picture profiles

Create Picture profiles (ImageSharpProfile or CloudflareProfile) for the different types of images that you have on your web site. A Picture profile describes how an image should be scaled in various cases.
You could for example create Picture profiles for: "Top hero image", "Teaser image", "Image gallery thumbnail".

using PictureRenderer.Optimizely;

namespace MyNamespace
{
    public class PictureProfiles
    {
        // Sample image
        // Up to 640 pixels viewport width, the picture width will be 100% of the viewport.
        // Up to 1200 pixels viewport width, the picture width will be 320 pixels.
        // On larger viewport width, the picture width will be 750 pixels.
        // Note that picture width is not the same as image width (but it can be, on screens with a "device pixel ratio" of 1).
        public readonly CloudflareProfile SampleImage = new()
        {
            SrcSetWidths = new[] { 320, 640, 750, 1500 },
            Sizes = new[] { "(max-width: 640px) 100vw", "(max-width: 1200px) 320px", "750px" },
            AspectRatio = 1.777 // 16:9 = 16/9 = 1.777
        };

        // Top hero
        // Picture width is always 100% of the viewport width.
        public readonly CloudflareProfile TopHero = new()
        {
            SrcSetWidths = new[] { 1024, 1366, 1536, 1920 },
            Sizes = new[] { "100vw" },
            AspectRatio = 2
        };

        // Thumbnail
        // Thumbnail is always 150px wide. But the browser may still select the 300px image for a high resolution screen (e.g. mobile or tablet screens).
        public readonly CloudflareProfile Thumbnail = new()
        {
            SrcSetWidths = new[] { 150, 300 },
            Sizes = new[] { "150px" },
            AspectRatio = 1  //square image (equal height and width).
        };
    }
}

2. Render picture element with the Picture Html helper

@Html.Picture(Model.CurrentPage.TestImage1, PictureProfiles.SampleImage)

Parameters

The result (for ImageSharp) would be something like this

<picture>
<source srcset="
 /contentassets/c9c99316ae264c6b9a092b4f56024539/myimage.jpg?width=320&height=180&quality=80 320w,
 /contentassets/c9c99316ae264c6b9a092b4f56024539/myimage.jpg?width=640&height=360&quality=80 640w,
 /contentassets/c9c99316ae264c6b9a092b4f56024539/myimage.jpg?width=750&height=422&quality=80 750w,
 /contentassets/c9c99316ae264c6b9a092b4f56024539/myimage.jpg?width=1500&height=844&quality=80 1500w"
 sizes="(max-width: 640px) 100vw, (max-width: 1200px) 320px, 750px" />
<img alt="Some alt text" src="https://github.com/ErikHen/PictureRenderer.Optimizely/raw/main/contentassets/c9c99316ae264c6b9a092b4f56024539/myimage.jpg?width=1500&height=844&quality=80" loading="lazy" />
</picture>


Webp/AVIF format

Cloudflare's image service automatically converts images to Webp or AVIF format, if the browser supports it.
If using ImageSharp.Web as image processor, the rendered picture element will also contain webp versions of the image. By default this will be rendered for jpg images, but png images can also be configured to have a webp version:

CreateWebpForFormat = new []{ PictureRenderer.ImageFormat.Jpeg, PictureRenderer.ImageFormat.Png }

Alt text

You can add a string field on your Image content model, and name it "AltText". The value of this field will be used when rendering the alt text in the picture element.

namespace MySite.Models.Media
{
    [ContentType(GUID = "0A89E464-56D4-449F-AEA8-2BF774AB8730")]
    [MediaDescriptor(ExtensionString = "jpg,jpeg,jpe,ico,gif,bmp,png")]
    public class ImageFile : ImageData 
    {
        [Display(Name = "Alt text", Order = 10)]
        public virtual string AltText { get; set; }
    }
}

Focal point

Add a string field on your Image content model, and name it "ImageFocalPoint".
Focal point coordinates should be in the format <x value>|<y value>. The values range from 0-1 (ex: 0|0 = left top corner, 0.5|0.5 = center of image).
Possible to use together with ImagePointEditor

namespace MySite.Models.Media
{
    [ContentType(GUID = "0A89E464-56D4-449F-AEA8-2BF774AB8730")]
    [MediaDescriptor(ExtensionString = "jpg,jpeg,jpe,ico,gif,bmp,png")]
    public class ImageFile : ImageData 
    {
        [UIHint(ImagePoint.UIHint)]
        [Display(Name = "Focal point")]
        public virtual string ImageFocalPoint { get; set; }
    }
}

Images added in rich text editor

Img elements rendered by the rich text editor (TinyMCE) can be replaced with a picture element. Enable this by creating a display template for XhtmlString. Create the file /Views/Shared/DisplayTemplates/XhtmlString.cshtml and add:

@using PictureRenderer.Optimizely
@model XhtmlString

@{ if (Model == null) { return; }; }

@{Html.RenderXhtmlString(Model.ReplaceImgWithPicture());}

If you want a more fine grained control of which Xhtml properties that should renderer picture elements, you can do this in your cshtml view:

@{ Html.RenderXhtmlString(Model.CurrentPage.MainBody.ReplaceImgWithPicture(new CloudflareRteProfile())); }

<!-- Set image quality to 85 -->
@{Html.RenderXhtmlString(Model.ReplaceImgWithPicture(new CloudflareRteProfile {Quality = 85})));}

ReplaceImgWithPicture parameters



Version history

3.2 Use PictureRenderer v3.12. Adds support for custom attributes on img element.

:exclamation: Note the new PictureAttributes object
Some settings are moved from the PictureProfile to PictureAttributes object.

3.1 Render with correct url in on-page edit mode. Style attribute set by TinyMCE will be added to img elemement.

3.0 Support for Cloudflare image resizing. Dropped support for .Net5. Removed GetDataFromImage setting.

2.5 Use v3.6 of PictureRenderer. Possible to set fixed height. Thanks Karl!

2.4 Use v3.5 of PictureRenderer. More compliant rendering + possible to show info.

2.3 Suport for rendering picture element in content from rich text editor.

2.2 Use v3.2 of PictureRenderer.

2.1 Possible to show different images depending on media conditions. For example show a different image for mobile screens.

2.0 Possible to render webp format (need to use Baaijte.Optimizely.ImageSharp.Web v2.0+ for webp support). Possible to set css class on img element.

1.2.0 Target both .Net5 & .Net6. Expose focal point parsing as string extension. Thanks David!

1.1.1 Use invariant culture when parsing focal point value. Thanks Gatis!

1.1 Added support for focal point when images are cropped.

Possible roadmap

Fallback to ImageSharp for environments where Cloudflare isn't available

Progressive lazy loading

Make it possible to show a very low-quality image on initial load, and lazy-load the high-quaility image after entire page is loaded.