3 ms·
Lazyloading is good, I've written a pretty speedy lazyloader myself that also works for CSS background images, but serving up lo-fi thumbnails when many images
by wanda 9y ago
Lazyloading is good, I've written a pretty speedy lazyloader myself that also works for CSS background images, but serving up lo-fi thumbnails when many images are displayed at once is something I tend to do regardless.
Lazyloading often works by checking if an image element is in the viewport — if so, the src attribute is added dynamically and the image is loaded as soon as this is complete.
However, if the user scrolls quickly or has a large screen, lots of images may be loaded simultaneously — and if those images are all fairly large, this may cause some performance problems.
You could try using requestAnimationFrame to load images one-by-one, but at the fastest possible rate, i.e. to stagger the loading of images. You could do this by listening for the 'load' event on each image in order to trigger the loading of the next image.
Assuming you store the image URL in a data attribute like 'data-src', a simple, vanilla implementation of what I just described might look like this:
/*
In production, we would throttle or debounce this scroll handler,
but for brevity we'll just attach it directly:
*/
window.addEventListener('scroll', function () {
var all_imgs = document.querySelectorAll('img[data-src]');
// [].slice.call to convert qSA's NodeList to an Array
var visible_imgs = [].slice.call(all_imgs).filter(function (img, index, arr) {
/*
your logic for determining if the image is in the viewport would go here
*/
});
return loadImages(visible_imgs);
}, false);
/*
This function will work by being passed an array of images
that were in the viewport at the time of the scroll event
triggering, and will call itself recursively until all
of those images are loaded.
*/
function loadImages (imgs) {
if (imgs.length < 1) {
return;
}
return requestAnimationFrame(function () {
imgs[0].src = imgs[0].getAttribute('data-src');
// remove the data attribute so that the image isn't loaded over and over again
imgs[0].removeAttribute('data-src');
imgs[0].addEventListener('load', function () {
return loadImages(imgs.slice(1));
}, false);
imgs[0].addEventListener('error', function () {
return loadImages(imgs.slice(1));
}, false);
});
}
NB: I have not really tested this approach or the code above, I'm shooting from the hip here because I'm in transit right now — but if it does work, it might help improve perceived performance. Or it might make everything look even slower in which case I'm an idiot.