Rework lazy loading as js controlled fetches
This commit is contained in:
@@ -8,15 +8,101 @@
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
// Gallery Thumbnails Module
|
// Gallery Thumbnails Module
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Thumbnail loading now relies on native loading="lazy" attribute.
|
// Uses fetch() with AbortController to load thumbnails.
|
||||||
// The browser handles deferred loading, connection limits, and
|
// This allows true HTTP request cancellation on navigation,
|
||||||
// automatic cancellation on navigation.
|
// unlike native loading="lazy" where queued requests block.
|
||||||
//
|
|
||||||
// This module is kept as a stub for potential future enhancements.
|
|
||||||
Luxtools.GalleryThumbnails = (function () {
|
Luxtools.GalleryThumbnails = (function () {
|
||||||
|
var controller = null;
|
||||||
|
var maxConcurrent = 4;
|
||||||
|
var activeCount = 0;
|
||||||
|
var queue = [];
|
||||||
|
|
||||||
|
function abortAll() {
|
||||||
|
if (controller) {
|
||||||
|
controller.abort();
|
||||||
|
controller = null;
|
||||||
|
}
|
||||||
|
queue = [];
|
||||||
|
activeCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processQueue() {
|
||||||
|
if (!controller) return;
|
||||||
|
while (activeCount < maxConcurrent && queue.length > 0) {
|
||||||
|
var img = queue.shift();
|
||||||
|
loadThumb(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadThumb(img) {
|
||||||
|
if (!controller) return;
|
||||||
|
|
||||||
|
var src = img.getAttribute('data-src');
|
||||||
|
if (!src) {
|
||||||
|
processQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeCount++;
|
||||||
|
|
||||||
|
fetch(src, { signal: controller.signal })
|
||||||
|
.then(function (response) {
|
||||||
|
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||||
|
return response.blob();
|
||||||
|
})
|
||||||
|
.then(function (blob) {
|
||||||
|
img.src = URL.createObjectURL(blob);
|
||||||
|
img.removeAttribute('data-src');
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
// Aborted or failed - ignore
|
||||||
|
if (err.name !== 'AbortError') {
|
||||||
|
// Keep data-src for potential retry, just log
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(function () {
|
||||||
|
activeCount--;
|
||||||
|
processQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueThumb(img) {
|
||||||
|
if (!controller) return;
|
||||||
|
if (!img.getAttribute('data-src')) return;
|
||||||
|
if (img.getAttribute('data-queued') === '1') return;
|
||||||
|
img.setAttribute('data-queued', '1');
|
||||||
|
queue.push(img);
|
||||||
|
processQueue();
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
// Native lazy loading handles everything.
|
var imgs = document.querySelectorAll(
|
||||||
// No JavaScript intervention needed.
|
'div.luxtools-gallery img.luxtools-thumb[data-src], div.luxtools-imagebox img[data-src]'
|
||||||
|
);
|
||||||
|
if (!imgs || !imgs.length) return;
|
||||||
|
|
||||||
|
// Create abort controller for all requests
|
||||||
|
controller = new AbortController();
|
||||||
|
|
||||||
|
// Abort all pending requests on navigation
|
||||||
|
window.addEventListener('beforeunload', abortAll);
|
||||||
|
window.addEventListener('pagehide', abortAll);
|
||||||
|
|
||||||
|
// Use IntersectionObserver to trigger loading
|
||||||
|
if ('IntersectionObserver' in window) {
|
||||||
|
var io = new IntersectionObserver(function (entries) {
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (!entry.isIntersecting) return;
|
||||||
|
queueThumb(entry.target);
|
||||||
|
io.unobserve(entry.target);
|
||||||
|
});
|
||||||
|
}, { rootMargin: '200px' });
|
||||||
|
|
||||||
|
imgs.forEach(function (img) { io.observe(img); });
|
||||||
|
} else {
|
||||||
|
// Fallback: queue all
|
||||||
|
imgs.forEach(queueThumb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class Output
|
|||||||
$caption = $label;
|
$caption = $label;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build thumbnail URL - rely on native loading="lazy" for deferred loading
|
// Build thumbnail URL - JavaScript will load via fetch() for cancellability
|
||||||
$thumbUrl = $this->withQueryParams($url, [
|
$thumbUrl = $this->withQueryParams($url, [
|
||||||
'thumb' => 1,
|
'thumb' => 1,
|
||||||
'w' => $genThumbW,
|
'w' => $genThumbW,
|
||||||
@@ -107,12 +107,10 @@ class Output
|
|||||||
. '>';
|
. '>';
|
||||||
$renderer->doc .= '<img'
|
$renderer->doc .= '<img'
|
||||||
. ' class="luxtools-thumb"'
|
. ' class="luxtools-thumb"'
|
||||||
. ' src="' . $thumbSrc . '"'
|
. ' data-src="' . $thumbSrc . '"'
|
||||||
. ' alt=""'
|
. ' alt=""'
|
||||||
. ' width="' . $thumbW . '"'
|
. ' width="' . $thumbW . '"'
|
||||||
. ' height="' . $thumbH . '"'
|
. ' height="' . $thumbH . '"'
|
||||||
. ' loading="lazy"'
|
|
||||||
. ' decoding="async"'
|
|
||||||
. ' />';
|
. ' />';
|
||||||
$renderer->doc .= '<span class="luxtools-gallery-caption">' . $caption . '</span>';
|
$renderer->doc .= '<span class="luxtools-gallery-caption">' . $caption . '</span>';
|
||||||
$renderer->doc .= '</a>';
|
$renderer->doc .= '</a>';
|
||||||
|
|||||||
Reference in New Issue
Block a user