FEATURE: Lazily Load Images as they scroll into the viewport.
This feature uses the Intersection Observer API https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API It should be compatible with all modern browsers. Non-Edge IE is *NOT* supported, so in that particular browser images are loaded by default.
This commit is contained in:
parent
5358f25fc6
commit
6797a710aa
|
@ -1,5 +1,6 @@
|
||||||
import highlightSyntax from "discourse/lib/highlight-syntax";
|
import highlightSyntax from "discourse/lib/highlight-syntax";
|
||||||
import lightbox from "discourse/lib/lightbox";
|
import lightbox from "discourse/lib/lightbox";
|
||||||
|
import { setupLazyLoading } from "discourse/lib/lazy-load-images";
|
||||||
import { setTextDirections } from "discourse/lib/text-direction";
|
import { setTextDirections } from "discourse/lib/text-direction";
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
|
|
||||||
|
@ -14,6 +15,8 @@ export default {
|
||||||
api.decorateCooked(setTextDirections);
|
api.decorateCooked(setTextDirections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupLazyLoading(api);
|
||||||
|
|
||||||
api.decorateCooked($elem => {
|
api.decorateCooked($elem => {
|
||||||
const players = $("audio", $elem);
|
const players = $("audio", $elem);
|
||||||
if (players.length) {
|
if (players.length) {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
const OBSERVER_OPTIONS = {
|
||||||
|
rootMargin: "50%" // load images slightly before they're visible
|
||||||
|
};
|
||||||
|
|
||||||
|
// We hide an image by replacing it with a transparent gif
|
||||||
|
function hide(image) {
|
||||||
|
image.classList.add("d-lazyload");
|
||||||
|
image.classList.add("d-lazyload-hidden");
|
||||||
|
image.setAttribute("data-src", image.getAttribute("src"));
|
||||||
|
image.setAttribute(
|
||||||
|
"src",
|
||||||
|
"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore an image from the `data-src` attribute
|
||||||
|
function show(image) {
|
||||||
|
let dataSrc = image.getAttribute("data-src");
|
||||||
|
if (dataSrc) {
|
||||||
|
image.setAttribute("src", dataSrc);
|
||||||
|
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;
|
||||||
|
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
}
|
|
@ -6,6 +6,16 @@
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
transition: opacity 0.5s;
|
transition: opacity 0.5s;
|
||||||
}
|
}
|
||||||
|
background: rgba($primary, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-lazyload-hidden {
|
||||||
|
opacity: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cooked img.d-lazyload {
|
||||||
|
transition: opacity 0.4s 0.75s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lightbox-wrapper {
|
.lightbox-wrapper {
|
||||||
|
|
Loading…
Reference in New Issue