Image Loading Solution
I was looking into a lightweight solution for handling the loading of images on a slow connection. For that, I combined some ideas and came up with the following. I created a new shortcode image.html
{{< image src="watercooling-1.jpg" alt="Yellow tubes!" >}}
The job of this shortcode is to take the input image and retrieve its dimensions and the ratio of the height over the width
{{ $image := ( .Page.Resources.GetMatch (index .Params.src)) }}
{{ $width := $image.Width }}
{{ $height := $image.Height }}
{{ $dimens := print $width "x" $height " q10" }}
{{ $ratio := div (add $height 0.0) $width }}
and generates the link to the image to be used in the html code (and retrieve the alt field)
{{ $src := "" }}
{{ $src = print $image.RelPermalink }}
{{ $alt := .Get "alt" }}
The ratio is used to stop content jumping, based on Javier Villanueva’s solution to the problem. This is done by creating a container div and adjusting the padding-top property to be 100% of the calculated ratio. The former is done in the common scss file
.img_container {
position: relative;
}
and the latter as inline css
<div class="img_container" style=" height: 0; padding-top: calc( {{ print $ratio }} * 100%);>
Next, the image itself is given an absolute position in css with auto height adjustment and a maximum width of 100%
.img_contained {
position: absolute;
top: 0;
left: 0;
max-width: 100%;
height: auto;
}
The image is then included, and additionally given the lazy loading propery
<div class="img_container" style=" height: 0; padding-top: calc( {{ print $ratio }} * 100%);>
<img class="img_contained" src="{{ $src }}" alt="{{ $alt }}" loading="lazy">
</div>
This takes care of content jumping, but now we end up with blank spaces until the images come in. To fix that, a fuzzy low-quality version of the same image is generated
{{ $placeholder := ($image.Resize $dimens) | images.Filter (images.GaussianBlur 6) }}
{{ $placeholder_src := print $placeholder.RelPermalink }}
and used as the background of both the div and the image. This gives a preview of the upcoming image to be loaded in
<div class="img_container" style=" height: 0; padding-top: calc( {{ print $ratio }} * 100%); background-image: url({{ $placeholder_src }}); background-size: cover;">
<img class="img_contained" src="{{ $src }}" alt="{{ $alt }}" loading="lazy" style="background-image: url({{ $placeholder_src }}); background-size: cover;">
</div>
Finally, I was inspired by Matt Hinchliffe’s approach to creating a simple gif free loading animation in css. This is done using a bobble animation
@keyframes bobble {
0% {
opacity: 0;
transform: translateY(0);
}
35% {
opacity: 1;
transform: translateY(-40px);
}
100% {
opacity: 0;
transform: translateY(0);
}
}
which is placed in the div container with the lowest z-index
.img_container::after {
content: ' ';
position: absolute;
top: 50%;
left: 50%;
width: 2rem;
height: 2rem;
margin: -0.5em 0 0 -0.5em;
background: rgba(125, 125, 125, 0.5);
border-radius: 100%;
animation: bobble 2s cubic-bezier(0.6, 1, 1, 1) infinite;
z-index: -1;
}
Alltogether, this results in a lightweight, javascript-free solution for the lazy loading of images with some preview.