I was looking for a good way to lazy load image son my website and stumbled upon https://www.botreetechnologies.com/blog/page-load-optimization-by-progressive-image-loading-like-medium . This post describes how to do image lazy loading similar to what medium does
Show a div
the same size as the resulting image (so the content does not “jump” after the image is loaded)
Load a very small low quality placeholder and blur it in the div
Load the final image
I made it into a reusable shortcode
{{ $image := (.Page.Resources.GetMatch (index .Params 0)) }}
{{ $placeholder := $image.Resize "48x q20" }}
<div class="image_placeholder" style="max-width: {{$image.Width}}px">
<div class="placeholder" data-large="{{ $image.Permalink }}">
<img src="{{ $placeholder.Permalink }}" class="img-small">
<div style="padding-bottom: {{ div (mul $image.Height 100.0) $image.Width }}%;"></div>
</div>
</div>
that can be called using
{{% post-image "image.png" %}}
but only works with page bundle images to be able to use the Hugo image resizing functionality.
The JavaScript and CSS stay the nearly same as in the post, I just adjusted them for my theme
.placeholder {
background-color: #f6f6f6;
background-size: cover;
background-repeat: no-repeat;
position: relative;
overflow: hidden;
}
.placeholder img {
position: absolute;
opacity: 0;
top: 0;
left: 0;
width: 100%;
transition: opacity 1s linear;
}
.img-small {
filter: blur(50px);
/* this is needed so Safari keeps sharp edges */
transform: scale(1);
}
.placeholder img.loaded {
opacity: 1;
}
.image_placeholder{
display: block;
}
<script type="text/javascript">
window.onload = function() {
var placeholder = $('.placeholder');
placeholder.each( function(index) {
// 1: load small image and show it
var smallImgElement = $(this).find('.img-small');
var img = new Image();
img.src = smallImgElement.attr('src');
img.onload = function () {
smallImgElement.addClass('loaded');
};
// 2: load large image
var imgLarge = new Image();
imgLarge.src = $(this).data('large');
imgLarge.onload = function () {
imgLarge.classList.add('loaded');
};
$(this).append(imgLarge);
})
}
</script>
You can see it in action at https://blog.kulman.sk/ , especially in the Projects section.
15 Likes
This is pretty cool. And you can also use the hasShortcode function to only load the JS in page where it is being used.
Thanks for this!! I modified it a bit to use only vanilla JS in case anyone’s interested.
window.onload = function() {
var placeholder = document.querySelectorAll('.progressive_figure');
placeholder.forEach(element => {
var smallImage = element.getElementsByClassName('img-small')[0];
// Load placeholder image
var img = new Image();
img.src = smallImage.getAttribute('src')
img.onload = function () {
smallImage.classList.add('loaded')
}
// Load large image
var largeImage = new Image();
largeImage.srcset = element.getAttribute('data-imgset');
largeImage.src = element.getAttribute('data-src');
largeImage.classList.add('lozad')
largeImage.onload = function () {
largeImage.classList.add('loaded');
smallImage.replaceWith(largeImage);
}
})};
4 Likes
I haven’t really read the detail of this thread, but this link is very important: https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video/
It provides guidance on how to do lazy loading images so they can be seen by Google.
1 Like
From what I understand (I’m only getting started with JS), the technique in the OP of this thread is about only the progressive-loading part of lazy loading.
For lazy loading (loading image only when scrolled to), you’ll have to use the Intersection Observer etc, as described in the Google link. This library does it quite well if you’re interested.
2 Likes
One new trick. Instead of linking the 48px placeholder image, you can just generate a much smaller image, say 2px and embed it directly to the HTML as base64:
{{ $image := (.Page.Resources.GetMatch (index .Params 0)) }}
{{ $placeholder := $image.Resize “2x q20” }}
<div class="image_placeholder" style="max-width: {{ $image.Width }}px">
<div class="placeholder" data-large="{{ $image.Permalink }}">
<img src="data:image/jpeg;base64,{{ $placeholder.Content | base64Encode }}" class="img-small loaded">
<div style="padding-bottom: {{ div (mul $image.Height 100.0) $image.Width }}%;"></div>
</div>
</div>
4 Likes
ju52
March 10, 2019, 8:50am
7
my 2 cents
to avoid the content jumping around, put the image size in the html content, like this
<img class="imageclass" src="{{ $image.RelPermalink }}" width={{ $image.Width }} height={{ $image.Height }} />
With the right CSS-class you can define the needed borders etc.
@pritamps suggests a way to load images during scrolling - different beast
2 Likes