58 lines
2.4 KiB
JavaScript
58 lines
2.4 KiB
JavaScript
// ── Bounded decoded-image lifecycle ─────────────────────────────────────────
|
|
// Native loading="lazy" controls network work, but browsers may retain decoded
|
|
// pixels for every image that has entered the viewport. Keep a small LRU of
|
|
// visible library images and drop src from old, off-screen elements so their
|
|
// decoded surfaces can be reclaimed. data-ttvc-src restores them on re-entry.
|
|
(function () {
|
|
'use strict';
|
|
var root = window.TTVC = window.TTVC || {};
|
|
var entries = new Map();
|
|
var visible = new Set();
|
|
var observer = 'IntersectionObserver' in window ? new IntersectionObserver(function (records) {
|
|
records.forEach(function (record) {
|
|
var img = record.target;
|
|
if (record.isIntersecting) {
|
|
visible.add(img);
|
|
var source = img.dataset.ttvcSrc;
|
|
if (source && !img.getAttribute('src')) img.setAttribute('src', source);
|
|
entries.delete(img); entries.set(img, Date.now());
|
|
} else {
|
|
visible.delete(img);
|
|
}
|
|
});
|
|
evict();
|
|
}, { rootMargin: '160px' }) : null;
|
|
|
|
function limit() {
|
|
return typeof window.ttvcIsLowMemoryDevice === 'function' && window.ttvcIsLowMemoryDevice() ? 24 : 64;
|
|
}
|
|
function evict() {
|
|
// Disconnected nodes are no longer useful cache entries.
|
|
entries.forEach(function (_, img) { if (!img.isConnected) entries.delete(img); });
|
|
while (entries.size > limit()) {
|
|
var candidate = null;
|
|
for (var img of entries.keys()) {
|
|
if (!visible.has(img)) { candidate = img; break; }
|
|
}
|
|
if (!candidate) break; // never evict an image currently near the viewport
|
|
entries.delete(candidate);
|
|
if (candidate.getAttribute('src')) candidate.dataset.ttvcSrc = candidate.getAttribute('src');
|
|
candidate.removeAttribute('src');
|
|
}
|
|
}
|
|
function observeImages(container) {
|
|
if (!container) return;
|
|
container.querySelectorAll('img').forEach(function (img) {
|
|
if (!img.dataset.ttvcSrc && img.getAttribute('src')) img.dataset.ttvcSrc = img.getAttribute('src');
|
|
if (observer && !img.dataset.ttvcObserved) {
|
|
img.dataset.ttvcObserved = '1';
|
|
observer.observe(img);
|
|
}
|
|
entries.delete(img); entries.set(img, Date.now());
|
|
});
|
|
evict();
|
|
}
|
|
root.imageCache = { observe: observeImages, evict: evict, size: function () { return entries.size; } };
|
|
window.ttvcObserveImages = observeImages;
|
|
})();
|