UX: show lazy loaded images while they are downloading

UX: show lazy loaded images while they are downloading

Also shows fuzzy image right away prior to it being downloaded

diff --git a/app/assets/javascripts/discourse/lib/lazy-load-images.js.es6 b/app/assets/javascripts/discourse/lib/lazy-load-images.js.es6
index 8835b0c..0700c49 100644
--- a/app/assets/javascripts/discourse/lib/lazy-load-images.js.es6
+++ b/app/assets/javascripts/discourse/lib/lazy-load-images.js.es6
@@ -28,21 +28,37 @@ function hide(image) {
 // Restore an image when onscreen
 function show(image) {
   let sources = imageSources.get(image);
+
   if (sources) {
-    image.setAttribute("src", sources.src);
+    const copyImg = new Image();
+    copyImg.onload = () => {
+      image.src = copyImg.src;
+      if (copyImg.srcset) {
+        image.srcset = copyImg.srcset;
+      }
+      image.classList.remove("d-lazyload-hidden");
+      image.parentNode.removeChild(copyImg);
+      copyImg.onload = null;
+    };
+
+    copyImg.src = sources.src;
     if (sources.srcSet) {
-      image.setAttribute("srcset", sources.srcSet);
+      copyImg.srcset = sources.srcSet;
     }
+
+    copyImg.style.position = "absolute";
+    copyImg.style.top = 0;
+    copyImg.style.left = 0;
+    copyImg.style.height = "100%";
+    copyImg.style.width = "100%";
+
+    image.parentNode.appendChild(copyImg);
+  } else {
+    image.classList.remove("d-lazyload-hidden");
   }
-  image.classList.remove("d-lazyload-hidden");
 }
 
 export function setupLazyLoading(api) {
-  // Old IE don't support this API
-  if (!("IntersectionObserver" in window)) {
-    return;
-  }
-
   const observer = new IntersectionObserver(entries => {
     entries.forEach(entry => {
       const { target } = entry;
@@ -50,15 +66,14 @@ export function setupLazyLoading(api) {
       if (entry.isIntersecting) {
         show(target);
         observer.unobserve(target);
-      } else {
-        // The Observer is triggered when entries are added. This allows
-        // us to hide things that start off screen.
-        hide(target);
       }
     });
   }, OBSERVER_OPTIONS);
 
   api.decorateCooked($post => {
-    $(".lightbox img", $post).each((_, $img) => observer.observe($img));
+    $(".lightbox img", $post).each((_, img) => {
+      hide(img);
+      observer.observe(img);
+    });
   });
 }

GitHub
sha: 400eea4d

1 Like

@eviltrout small one I noticed here.

I tended to use: copyImg.src = sources.src and you tended to use earlier up:

image.setAttribute(
     "src",
     image.getAttribute("data-small-upload") || LOADING_DATA
   );

This could be:

image.src = image.dataset.smallUpload || LOADING_DATA; 

I think we should be consistent here and pick some sort of side… I guess I prefer image.src = cause there is less to type, but this is not a hill I want to die on.

I much prefer consistency so we should pick a side here and follow it through the file.

1 Like

Apparently we should prefer the .property form:

I’ll update my code.

2 Likes