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 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.
- Add PictureRenderer.Optimizely to your solution.
- If using Cloudflare Image Resizing, this needs to be enabled in your Cloudflare CDN. See here for how to do that for an Optimizely DXP solution (currently a beta feature).
- If resizing using ImageSharp you also need to add Baaijte.OptimizelyImageSharp.Web to your solution. Check the needed configuration.
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 static 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 static 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 static 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 static readonly CloudflareProfile Thumbnail = new()
{
SrcSetWidths = new[] { 150, 300 },
Sizes = new[] { "150px" },
AspectRatio = 1 //square image (equal height and width).
};
// Multi-image
// Show different images depending on media conditions (e.g. different image for mobile sized screen).
public static readonly CloudflareProfile MultiImageSample = new()
{
// First image will be resized to 600px width, and will be shown when viewport width is greater than 600px.
// Second image will be resized to 300px width, and will be shown when viewport width is less than 600px.
// Note: if second image isn't available, the first image will be used instead.
MultiImageMediaConditions = new[] { new MediaCondition("(min-width: 600px)", 600), new MediaCondition("(max-width: 600px)", 300) },
AspectRatio = 1.777
};
}
}
- SrcSetWidths (for single image) – The different image widths you want the browser to select from. These values are used when rendering the srcset attribute.
- Sizes (for single image) – Define the size (width) the image should be according to a set of “media conditions” (similar to css media queries). Values are used when rendering the sizes attribute.
- MultiImageMediaConditions (for multi image) - Define image widths for different media conditions.
- AspectRatio (optional) – The wanted aspect ratio of the image (width/height). Ex: An image with aspect ratio 16:9 = 16/9 = 1.777.
- FixedHeight (optional) – Set a fixed height for all image sizes. Fixed height is ignored if aspect ratio is set.
- Quality (optional) - Image quality. Lower value = less file size. Not valid for all image formats. Default value: 80.
- FallbackWidth (optional) – This image width will be used in browsers that don’t support the picture element. Will use the largest image if not set.
- ImgWidthHeight (optional) - If true, width and height attributes will be rendered on the img element.
- ShowInfo (optional) - If true, an overlay will show info about the currently selected image.
- CreateWebpForFormat (optional, ImageSharp only) - The image formats that should be offered as webp versions. Jpg format is added by default.
- IsDisabled (optional, Cloudflare only) - Do not alter the image url at all. May be useful in a development environment where you are not using a CDN..
See also the sample site
@Html.Picture(Model.CurrentPage.TestImage1, PictureProfiles.SampleImage)
- imageReference/imageReferences - ContentReference to your image, or array for multi image.
- profile - The Picture profile that specifies image widths, etc..
- altText (optional) - Img element
alt
attribute (will overrride alt text set on image). - lazyLoading (optional) - Type of lazy loading. Currently only browser native lazy loading, or none (defaults to browser native).
- cssClass (optional) - Css class for img element.
The result (for ImageSharp, single image) 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="/contentassets/c9c99316ae264c6b9a092b4f56024539/myimage.jpg?width=1500&height=844&quality=80" loading="lazy" />
</picture>
This feature is currently for ImageSharp only.
If you set ShowInfo = true
in the picture profile, an overlay with information about the currently selected image will be rendered.
You can see that different images are selected for different devices and screen sizes. Note that the Chrome (Chromium based) browser will not select a smaller image if a larger one is already downloaded. It may be easier to see the behaviour when using e.g. Firefox.
This setting should of course never be used in your live/production environment, it's only meant for testing.
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 }
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; }
}
}
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; }
}
}
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})));}
- profile (optional) - A picture profile where you can set maximum image width (default value: 1024), image quality (default value: 80), and what image types that should be converted to WebP (default: jpg).
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.
1.0 Initial version.
Since basically all browsers now support picture element and webp format, it could be made optional to render fallback image/format.
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.