/* Tile loading, after the 2004-2019 site. * * The original showed animated static (void.gif, 79 frames at 40ms) in each * tile until jquery.lazyload fetched the real image on scroll. On a 2014 * connection fetching 231 library images, that static was visible for a good * while. Served locally it is gone in a few milliseconds and the effect is * lost entirely, so the hold is made deliberate rather than left to latency: * the noise runs for at least HOLD_MS whatever the connection does. * * Loading also starts only once a tile is genuinely near the viewport, and * each tile takes a small stagger, so a screen of them resolves in a ripple * rather than all snapping at once. */ (function () { "use strict"; var HOLD_MS = 320; /* minimum time the static is on screen */ var STAGGER = 35; /* ms between neighbouring tiles resolving */ var MARGIN = "80px"; var tiles = document.querySelectorAll(".art-preview.lazy[data-src]"); if (!tiles.length) return; var queued = 0, lastAt = 0; function reveal(el, src) { el.style.setProperty("--src", "url('" + src + "')"); el.classList.add("loaded"); } function load(el) { var src = el.getAttribute("data-src"); if (!src || el.dataset.done) return; el.dataset.done = "1"; var now = Date.now(); var slot = Math.max(now, lastAt + STAGGER); lastAt = slot; var floor = slot - now + HOLD_MS; /* earliest this tile may resolve */ var began = now, ready = false, elapsed = 0; var img = new Image(); img.onload = function () { ready = true; elapsed = Date.now() - began; setTimeout(function () { reveal(el, src); }, Math.max(0, floor - elapsed)); }; img.onerror = function () { el.classList.add("empty"); }; img.src = src; } if (!("IntersectionObserver" in window)) { Array.prototype.forEach.call(tiles, load); return; } var io = new IntersectionObserver(function (entries) { entries.forEach(function (e) { if (e.isIntersecting) { load(e.target); io.unobserve(e.target); } }); }, { rootMargin: MARGIN }); Array.prototype.forEach.call(tiles, function (t) { io.observe(t); }); })();