unlazy
Version:
Universal lazy loading library for placeholder images leveraging native browser APIs
370 lines (369 loc) • 10.9 kB
JavaScript
const st = new Uint8Array(128);
for (let t = 0; t < 83; t++)
st["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~".charCodeAt(
t
)] = t;
const j = (t, e, n) => {
let o = 0;
for (; e < n; )
o *= 83, o += st[t.charCodeAt(e++)];
return o;
}, ct = Math.pow, R = Math.PI, At = R * 2, at = 3294.6, lt = 269.025, wt = (t) => t > 10.31475 ? ct(t / lt + 0.052132, 2.4) : t / at, V = (t) => ~~(t > 1227e-8 ? lt * ct(t, 0.416666) - 13.025 : t * at + 1), I = (t) => (t < 0 ? -1 : 1) * t * t, nt = (t) => {
for (t += R / 2; t > R; )
t -= At;
const e = 1.27323954 * t - 0.405284735 * I(t);
return 0.225 * (I(e) - e) + e;
};
function Et(t) {
const e = j(t, 2, 6);
return [e >> 16, e >> 8 & 255, e & 255];
}
function ht(t, e, n, o) {
const c = j(t, 0, 1), s = c % 9 + 1, i = ~~(c / 9) + 1, d = s * i;
let r = 0, p = 0, u = 0, f = 0, a = 0, y = 0, G = 0, h = 0, z = 0, A = 0, x = 0, g = 0;
const U = (j(t, 1, 2) + 1) / 13446 * (o | 1), w = new Float64Array(d * 3), C = Et(t);
for (r = 0; r < 3; r++)
w[r] = wt(C[r]);
for (r = 1; r < d; r++)
g = j(t, 4 + r * 2, 6 + r * 2), w[r * 3] = I(~~(g / 361) - 9) * U, w[r * 3 + 1] = I(~~(g / 19) % 19 - 9) * U, w[r * 3 + 2] = I(g % 19 - 9) * U;
const D = new Float64Array(i * n), H = new Float64Array(s * e);
for (p = 0; p < i; p++)
for (f = 0; f < n; f++)
D[p * n + f] = nt(R * f * p / n);
for (r = 0; r < s; r++)
for (u = 0; u < e; u++)
H[r * e + u] = nt(R * u * r / e);
const M = e * 4, S = new Uint8ClampedArray(M * n);
for (f = 0; f < n; f++)
for (u = 0; u < e; u++) {
for (a = y = G = 0, p = 0; p < i; p++)
for (z = D[p * n + f], r = 0; r < s; r++)
h = H[r * e + u] * z, A = (r + p * s) * 3, a += w[A] * h, y += w[A + 1] * h, G += w[A + 2] * h;
x = 4 * u + f * M, S[x] = V(a), S[x + 1] = V(y), S[x + 2] = V(G), S[x + 3] = 255;
}
return S;
}
const K = 32, xt = "data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3C/svg%3E";
/**
* Encodes an RGBA image to a PNG data URI. RGB should not be premultiplied by A.
*
* @remarks
* This is optimized for speed and simplicity and does not optimize for size
* at all. This does not do any compression (all values are stored uncompressed).
*
* @see https://github.com/evanw/thumbhash
* @author Evan Wallace
* @license MIT
*/
function it(t, e, n) {
const o = t * 4 + 1, c = 6 + e * (5 + o), s = [
137,
80,
78,
71,
13,
10,
26,
10,
0,
0,
0,
13,
73,
72,
68,
82,
0,
0,
t >> 8,
t & 255,
0,
0,
e >> 8,
e & 255,
8,
6,
0,
0,
0,
0,
0,
0,
0,
c >>> 24,
c >> 16 & 255,
c >> 8 & 255,
c & 255,
73,
68,
65,
84,
120,
1
], i = [
0,
498536548,
997073096,
651767980,
1994146192,
1802195444,
1303535960,
1342533948,
-306674912,
-267414716,
-690576408,
-882789492,
-1687895376,
-2032938284,
-1609899400,
-1111625188
];
let d = 1, r = 0;
for (let u = 0, f = 0, a = o - 1; u < e; u++, a += o - 1)
for (s.push(u + 1 < e ? 0 : 1, o & 255, o >> 8, ~o & 255, o >> 8 ^ 255, 0), r = (r + d) % 65521; f < a; f++) {
const y = n[f] & 255;
s.push(y), d = (d + y) % 65521, r = (r + d) % 65521;
}
s.push(
r >> 8,
r & 255,
d >> 8,
d & 255,
0,
0,
0,
0,
0,
0,
0,
0,
73,
69,
78,
68,
174,
66,
96,
130
);
for (let [u, f] of [[12, 29], [37, 41 + c]]) {
let a = -1;
for (let y = u; y < f; y++)
a ^= s[y], a = a >>> 4 ^ i[a & 15], a = a >>> 4 ^ i[a & 15];
a = ~a, s[f++] = a >>> 24, s[f++] = a >> 16 & 255, s[f++] = a >> 8 & 255, s[f++] = a & 255;
}
return `data:image/png;base64,${globalThis.btoa(String.fromCharCode(...s))}`;
}
function St(t, {
ratio: e = 1,
size: n = K
} = {}) {
const { width: o, height: c } = vt(e, n), s = ht(t, o, c);
return it(o, c, s);
}
function vt(t, e) {
const n = t >= 1;
return {
width: n ? e : Math.round(e * t),
height: n ? Math.round(e / t) : e
};
}
function zt(t) {
let { PI: e, min: n, max: o, cos: c, round: s } = Math, i = t[0] | t[1] << 8 | t[2] << 16, d = t[3] | t[4] << 8, r = (i & 63) / 63, p = (i >> 6 & 63) / 31.5 - 1, u = (i >> 12 & 63) / 31.5 - 1, f = (i >> 18 & 31) / 31, a = i >> 23, y = (d >> 3 & 63) / 63, G = (d >> 9 & 63) / 63, h = d >> 15, z = o(3, h ? a ? 5 : 7 : d & 7), A = o(3, h ? d & 7 : a ? 5 : 7), x = a ? (t[5] & 15) / 15 : 1, g = (t[5] >> 4) / 15, U = a ? 6 : 5, w = 0, C = (O, E, P) => {
let L = [];
for (let v = 0; v < E; v++)
for (let m = v ? 0 : 1; m * E < O * (E - v); m++)
L.push(((t[U + (w >> 1)] >> ((w++ & 1) << 2) & 15) / 7.5 - 1) * P);
return L;
}, D = C(z, A, f), H = C(3, 3, y * 1.25), M = C(3, 3, G * 1.25), S = a && C(5, 5, g), q = gt(t), B = s(q > 1 ? 32 : 32 * q), F = s(q > 1 ? 32 / q : 32), T = new Uint8Array(B * F * 4), W = [], Y = [];
for (let O = 0, E = 0; O < F; O++)
for (let P = 0; P < B; P++, E += 4) {
let L = r, v = p, m = u, Q = x;
for (let l = 0, b = o(z, a ? 5 : 3); l < b; l++)
W[l] = c(e / B * (P + 0.5) * l);
for (let l = 0, b = o(A, a ? 5 : 3); l < b; l++)
Y[l] = c(e / F * (O + 0.5) * l);
for (let l = 0, b = 0; l < A; l++)
for (let _ = l ? 0 : 1, N = Y[l] * 2; _ * A < z * (A - l); _++, b++)
L += D[b] * W[_] * N;
for (let l = 0, b = 0; l < 3; l++)
for (let _ = l ? 0 : 1, N = Y[l] * 2; _ < 3 - l; _++, b++) {
let rt = W[_] * N;
v += H[b] * rt, m += M[b] * rt;
}
if (a)
for (let l = 0, b = 0; l < 5; l++)
for (let _ = l ? 0 : 1, N = Y[l] * 2; _ < 5 - l; _++, b++)
Q += S[b] * W[_] * N;
let tt = L - 2 / 3 * v, et = (3 * L - tt + m) / 2, _t = et - m;
T[E] = o(0, 255 * n(1, et)), T[E + 1] = o(0, 255 * n(1, _t)), T[E + 2] = o(0, 255 * n(1, tt)), T[E + 3] = o(0, 255 * n(1, Q));
}
return { w: B, h: F, rgba: T };
}
function gt(t) {
let e = t[3], n = t[2] & 128, o = t[4] & 128, c = o ? n ? 5 : 7 : e & 7, s = o ? e & 7 : n ? 5 : 7;
return c / s;
}
function Ct(t) {
const e = Lt(t), { w: n, h: o, rgba: c } = zt(e);
return it(n, o, c);
}
function Lt(t) {
return Uint8Array.from(
globalThis.atob(mt(t)),
(e) => e.charCodeAt(0)
);
}
function mt(t) {
return t.replaceAll("-", "+").replaceAll("_", "/");
}
const ft = typeof window > "u", Gt = !ft && "loading" in HTMLImageElement.prototype, Ut = !ft && (!("onscroll" in window) || /(?:gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent));
function ut(t, e = document) {
return typeof t == "string" ? [...e.querySelectorAll(t)] : t instanceof Element ? [t] : [...t];
}
function Tt(t) {
const e = Date.now();
return xt.replace(/\s/, ` data-id='${e}-${t}' `);
}
function Ot(t, e) {
let n;
return function(...o) {
n != null && clearTimeout(n), n = setTimeout(() => {
t(...o), n = void 0;
}, e);
};
}
function dt(t = 'img[loading="lazy"]', {
hash: e = !0,
hashType: n = "blurhash",
placeholderSize: o = K,
updateSizesOnResize: c = !1,
onImageLoad: s
} = {}) {
const i = /* @__PURE__ */ new Set();
for (const [d, r] of ut(t).entries()) {
const p = Z(r, { updateOnResize: c });
if (c && p && i.add(p), e) {
const f = Nt({
image: r,
hash: typeof e == "string" ? e : void 0,
hashType: n,
size: o
});
f && (r.src = f);
}
if (!r.dataset.src && !r.dataset.srcset) {
(typeof __UNLAZY_LOGGING__ > "u" || __UNLAZY_LOGGING__) && console.error("[unlazy] Missing `data-src` or `data-srcset` attribute", r);
continue;
}
if (Ut || !Gt) {
pt(r), X(r), k(r);
continue;
}
if (r.src || (r.src = Tt(d)), r.complete && r.naturalWidth > 0) {
J(r, s);
continue;
}
const u = () => J(r, s);
r.addEventListener("load", u, { once: !0 }), i.add(
() => r.removeEventListener("load", u)
);
}
return () => {
for (const d of i) d();
i.clear();
};
}
function Pt(t = 'img[data-sizes="auto"], source[data-sizes="auto"]') {
for (const e of ut(t))
Z(e);
}
function J(t, e) {
if (yt(t)) {
pt(t), X(t), k(t), e == null || e(t);
return;
}
const n = new Image(), { srcset: o, src: c, sizes: s } = t.dataset;
if (s === "auto") {
const i = bt(t);
i && (n.sizes = `${i}px`);
} else t.sizes && (n.sizes = t.sizes);
o && (n.srcset = o), c && (n.src = c), n.addEventListener("load", () => {
X(t), k(t), e == null || e(t);
}, { once: !0 });
}
function Nt({
image: t,
hash: e,
hashType: n = "blurhash",
size: o = K,
ratio: c
} = {}) {
if (t && !e) {
const { blurhash: s, thumbhash: i } = t.dataset;
e = i || s, n = i ? "thumbhash" : "blurhash";
}
if (e)
try {
if (n === "blurhash") {
if (t && !c) {
const s = t.width || t.offsetWidth || o, i = t.height || t.offsetHeight || o;
c = s / i;
}
return St(e, { ratio: c, size: o });
}
return Ct(e);
} catch (s) {
(typeof __UNLAZY_LOGGING__ > "u" || __UNLAZY_LOGGING__) && console.error(`[unlazy] Failed to generate ${n} placeholder:`, s);
}
}
const $ = /* @__PURE__ */ new WeakMap();
function Z(t, e) {
if (t.dataset.sizes !== "auto")
return;
const n = bt(t);
if (n && (t.sizes = `${n}px`), yt(t) && (e != null && e.processSourceElements))
for (const o of [...t.parentElement.getElementsByTagName("source")])
Z(o, { processSourceElements: !0 });
if (e != null && e.updateOnResize) {
if (!$.has(t)) {
const o = Ot(() => Z(t), 500), c = new ResizeObserver(o);
$.set(t, c), c.observe(t);
}
return () => {
const o = $.get(t);
o && (o.disconnect(), $.delete(t));
};
}
}
function k(t) {
t.dataset.src && (t.src = t.dataset.src, t.removeAttribute("data-src"));
}
function X(t) {
t.dataset.srcset && (t.srcset = t.dataset.srcset, t.removeAttribute("data-srcset"));
}
function pt(t) {
const e = t.parentElement;
(e == null ? void 0 : e.tagName.toLowerCase()) === "picture" && ([...e.querySelectorAll("source[data-srcset]")].forEach(X), [...e.querySelectorAll("source[data-src]")].forEach(k));
}
function bt(t) {
var e, n;
return t instanceof HTMLSourceElement ? (n = (e = t.parentElement) == null ? void 0 : e.getElementsByTagName("img")[0]) == null ? void 0 : n.offsetWidth : t.offsetWidth;
}
function yt(t) {
var e;
return ((e = t.parentElement) == null ? void 0 : e.tagName.toLowerCase()) === "picture";
}
const It = Object.freeze({
autoSizes: Pt,
lazyLoad: dt,
loadImage: J
});
var ot;
(ot = document.currentScript) != null && ot.hasAttribute("init") && dt();
export {
Pt as autoSizes,
It as default,
dt as lazyLoad,
J as loadImage
};