62 lines
2.1 KiB
JavaScript
62 lines
2.1 KiB
JavaScript
|
|
/* 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); });
|
||
|
|
})();
|