Files
bitvid/dist/js/utils/mediaLoader.js
T
2026-01-30 17:09:51 -05:00

83 lines
1.9 KiB
JavaScript

/**
* Simple IntersectionObserver-based lazy loader for images (or videos).
*
* Usage:
* const mediaLoader = new MediaLoader();
* mediaLoader.observe(imgElement);
*
* This will load the real image source from `imgElement.dataset.lazy`
* once the image enters the viewport.
*/
export class MediaLoader {
constructor(rootMargin = "50px") {
this.observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) {
continue;
}
const el = entry.target;
const lazySrc =
typeof el.dataset.lazy === "string" ? el.dataset.lazy.trim() : "";
if (!lazySrc) {
continue;
}
const fallbackSrc =
(typeof el.dataset.fallbackSrc === "string"
? el.dataset.fallbackSrc.trim()
: "") ||
el.getAttribute("data-fallback-src") ||
"";
const tagName = typeof el.tagName === "string" ? el.tagName : "";
if (tagName === "IMG" || tagName === "IMAGE") {
el.src = lazySrc;
if (fallbackSrc) {
el.onerror = () => {
el.src = fallbackSrc;
};
}
} else if (tagName === "VIDEO") {
el.src = lazySrc;
if (fallbackSrc) {
el.poster = fallbackSrc;
}
} else if ("src" in el) {
el.src = lazySrc;
}
delete el.dataset.lazy;
this.observer.unobserve(el);
}
}, {
rootMargin,
threshold: 0.01,
});
}
observe(el) {
if (!el || typeof el !== "object") {
return;
}
if ("decoding" in HTMLImageElement.prototype) {
el.decoding = el.decoding || "async";
}
const lazySrc =
typeof el.dataset.lazy === "string" ? el.dataset.lazy.trim() : "";
if (lazySrc) {
this.observer.observe(el);
}
}
disconnect() {
this.observer.disconnect();
}
}
export default MediaLoader;