Pixelated image animation/transition when they enter viewport with lazy loading

In short, I’d like my images to appear pixelated and transition to fully rendered versions after a small delay. The only example I can find right now is the one below, which does it great…

Example: https://aino.agency/

This is the desired effect. So pixelated, then a small animation/transition as the image appears rendered.

I have two code examples, both use the approach of having a smaller 64×64 image that is stretched then uses image-rendering: pixelated to give the initial pixelated effect.

The 1st example, which is embed in the post, uses JS to swap out the image when loaded but this happens too fast and also loads all the images even when they’re not in the viewport. So I need a way for that to happen and also enforce the transition. This is for a creative site so it’s ok to delay the image slight for the effect as it’s not a ‘fun’ landing page.

In the 2nd, CodePen version below, I thought I could display a 2nd image over the top of the loading="lazy" images. Then when the image is loaded, add a class to the parent div which in turn could run a simple CSS animations to transition to the fully loaded image – which I’ve currently got set on :hover to demo.

https://codepen.io/moy/pen/QWzQPbG

Really open to peoples ideas on this and the best approach, as I know it’s not too conventional!

document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  } else {
    // Possibly fall back to event handlers here
  }
});
img {
  display: block;
  width: 100%;
  min-width: 100%;
}

.grid {
  display: flex;
  flex-wrap: wrap;
  column-gap: 8px;
  row-gap: 8px;
  margin: auto;
  max-width: 1000px;
}

.grid__item {
  position: relative;
  image-rendering: pixelated;
  width: calc(50% - 4px);
}
<div class="grid">
  <div class="grid__item">
    <img class="lazy" src="https://haddawaynshite.com/lazy-images/bruce--small.jpg" data-src="https://haddawaynshite.com/lazy-images/bruce.jpg" data-srcset="https://haddawaynshite.com/lazy-images/bruce.jpg 2x, https://haddawaynshite.com/lazy-images/bruce.jpg 1x" alt="ALT TEXT">
  </div>
  <div class="grid__item">
    <img class="lazy" src="https://haddawaynshite.com/lazy-images/grace--small.jpg" data-src="https://haddawaynshite.com/lazy-images/grace.jpg" data-srcset="https://haddawaynshite.com/lazy-images/grace.jpg 2x, https://haddawaynshite.com/lazy-images/grace.jpg 1x" alt="ALT TEXT">
  </div>
  <div class="grid__item">
    <img class="lazy" src="https://haddawaynshite.com/lazy-images/nirvana--small.jpg" data-src="https://haddawaynshite.com/lazy-images/nirvana.jpg" data-srcset="https://haddawaynshite.com/lazy-images/nirvana.jpg 2x, https://haddawaynshite.com/lazy-images/nirvana.jpg 1x" alt="ALT TEXT">
  </div>
  <div class="grid__item">
    <img class="lazy" src="https://haddawaynshite.com/lazy-images/nwa--small.jpg" data-src="https://haddawaynshite.com/lazy-images/nwa.jpg" data-srcset="https://haddawaynshite.com/lazy-images/nwa.jpg 2x, https://haddawaynshite.com/lazy-images/nwa.jpg 1x" alt="ALT TEXT">
  </div>
  <div class="grid__item">
    <img class="lazy" src="https://haddawaynshite.com/lazy-images/zep--small.jpg" data-src="https://haddawaynshite.com/lazy-images/zep.jpg" data-srcset="https://haddawaynshite.com/lazy-images/zep.jpg 2x, https://haddawaynshite.com/lazy-images/zep.jpg 1x" alt="ALT TEXT">
  </div>
  <div class="grid__item">
    <img class="lazy" src="https://haddawaynshite.com/lazy-images/beatles--small.jpg" data-src="https://haddawaynshite.com/lazy-images/beatles.jpg" data-srcset="https://haddawaynshite.com/lazy-images/beatles.jpg 2x, https://haddawaynshite.com/lazy-images/beatles.jpg 1x" alt="ALT TEXT">
  </div>
</div>