unlazy
Version:
Universal lazy loading library for placeholder images leveraging native browser APIs
367 lines (366 loc) • 10.7 kB
JavaScript
const nt = new Uint8Array(128);
for (let t = 0; t < 83; t++)
nt["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~".charCodeAt(
t
)] = t;
const j = (t, e, n) => {
let o = 0;
for (; e < n; )
o *= 83, o += nt[t.charCodeAt(e++)];
return o;
}, st = Math.pow, R = Math.PI, _t = R * 2, ct = 3294.6, at = 269.025, At = (t) => t > 10.31475 ? st(t / at + 0.052132, 2.4) : t / ct, V = (t) => ~~(t > 1227e-8 ? at * st(t, 0.416666) - 13.025 : t * ct + 1), N = (t) => (t < 0 ? -1 : 1) * t * t, ot = (t) => {
for (t += R / 2; t > R; )
t -= _t;
const e = 1.27323954 * t - 0.405284735 * N(t);
return 0.225 * (N(e) - e) + e;
};
function wt(t) {
const e = j(t, 2, 6);
return [e >> 16, e >> 8 & 255, e & 255];
}
function Et(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, f = 0, u = 0, a = 0, y = 0, I = 0, g = 0, x = 0, A = 0, L = 0, S = 0;
const G = (j(t, 1, 2) + 1) / 13446 * (o | 1), w = new Float64Array(d * 3), v = wt(t);
for (r = 0; r < 3; r++)
w[r] = At(v[r]);
for (r = 1; r < d; r++)
S = j(t, 4 + r * 2, 6 + r * 2), w[r * 3] = N(~~(S / 361) - 9) * G, w[r * 3 + 1] = N(~~(S / 19) % 19 - 9) * G, w[r * 3 + 2] = N(S % 19 - 9) * G;
const D = new Float64Array(i * n), H = new Float64Array(s * e);
for (p = 0; p < i; p++)
for (u = 0; u < n; u++)
D[p * n + u] = ot(R * u * p / n);
for (r = 0; r < s; r++)
for (f = 0; f < e; f++)
H[r * e + f] = ot(R * f * r / e);
const M = e * 4, m = new Uint8ClampedArray(M * n);
for (u = 0; u < n; u++)
for (f = 0; f < e; f++) {
for (a = y = I = 0, p = 0; p < i; p++)
for (x = D[p * n + u], r = 0; r < s; r++)
g = H[r * e + f] * x, A = (r + p * s) * 3, a += w[A] * g, y += w[A + 1] * g, I += w[A + 2] * g;
L = 4 * f + u * M, m[L] = V(a), m[L + 1] = V(y), m[L + 2] = V(I), m[L + 3] = 255;
}
return m;
}
const K = 32, gt = "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 lt(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 f = 0, u = 0, a = o - 1; f < e; f++, a += o - 1)
for (s.push(f + 1 < e ? 0 : 1, o & 255, o >> 8, ~o & 255, o >> 8 ^ 255, 0), r = (r + d) % 65521; u < a; u++) {
const y = n[u] & 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 [f, u] of [[12, 29], [37, 41 + c]]) {
let a = -1;
for (let y = f; y < u; y++)
a ^= s[y], a = a >>> 4 ^ i[a & 15], a = a >>> 4 ^ i[a & 15];
a = ~a, s[u++] = a >>> 24, s[u++] = a >> 16 & 255, s[u++] = a >> 8 & 255, s[u++] = a & 255;
}
return `data:image/png;base64,${globalThis.btoa(String.fromCharCode(...s))}`;
}
function Lt(t, {
ratio: e = 1,
size: n = K
} = {}) {
const { width: o, height: c } = mt(e, n), s = Et(t, o, c);
return lt(o, c, s);
}
function mt(t, e) {
const n = t >= 1;
return {
width: n ? e : Math.round(e * t),
height: n ? Math.round(e / t) : e
};
}
function ht(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, f = (i >> 12 & 63) / 31.5 - 1, u = (i >> 18 & 31) / 31, a = i >> 23, y = (d >> 3 & 63) / 63, I = (d >> 9 & 63) / 63, g = d >> 15, x = o(3, g ? a ? 5 : 7 : d & 7), A = o(3, g ? d & 7 : a ? 5 : 7), L = a ? (t[5] & 15) / 15 : 1, S = (t[5] >> 4) / 15, G = a ? 6 : 5, w = 0, v = (T, E, O) => {
let z = [];
for (let h = 0; h < E; h++)
for (let C = h ? 0 : 1; C * E < T * (E - h); C++)
z.push(((t[G + (w >> 1)] >> ((w++ & 1) << 2) & 15) / 7.5 - 1) * O);
return z;
}, D = v(x, A, u), H = v(3, 3, y * 1.25), M = v(3, 3, I * 1.25), m = a && v(5, 5, S), q = xt(t), B = s(q > 1 ? 32 : 32 * q), F = s(q > 1 ? 32 / q : 32), U = new Uint8Array(B * F * 4), W = [], Y = [];
for (let T = 0, E = 0; T < F; T++)
for (let O = 0; O < B; O++, E += 4) {
let z = r, h = p, C = f, Q = L;
for (let l = 0, b = o(x, a ? 5 : 3); l < b; l++)
W[l] = c(e / B * (O + 0.5) * l);
for (let l = 0, b = o(A, a ? 5 : 3); l < b; l++)
Y[l] = c(e / F * (T + 0.5) * l);
for (let l = 0, b = 0; l < A; l++)
for (let _ = l ? 0 : 1, P = Y[l] * 2; _ * A < x * (A - l); _++, b++)
z += D[b] * W[_] * P;
for (let l = 0, b = 0; l < 3; l++)
for (let _ = l ? 0 : 1, P = Y[l] * 2; _ < 3 - l; _++, b++) {
let rt = W[_] * P;
h += H[b] * rt, C += M[b] * rt;
}
if (a)
for (let l = 0, b = 0; l < 5; l++)
for (let _ = l ? 0 : 1, P = Y[l] * 2; _ < 5 - l; _++, b++)
Q += m[b] * W[_] * P;
let tt = z - 2 / 3 * h, et = (3 * z - tt + C) / 2, yt = et - C;
U[E] = o(0, 255 * n(1, et)), U[E + 1] = o(0, 255 * n(1, yt)), U[E + 2] = o(0, 255 * n(1, tt)), U[E + 3] = o(0, 255 * n(1, Q));
}
return { w: B, h: F, rgba: U };
}
function xt(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 St(t) {
const e = vt(t), { w: n, h: o, rgba: c } = ht(e);
return lt(n, o, c);
}
function vt(t) {
return Uint8Array.from(
globalThis.atob(zt(t)),
(e) => e.charCodeAt(0)
);
}
function zt(t) {
return t.replaceAll("-", "+").replaceAll("_", "/");
}
const it = typeof window > "u", Ct = !it && "loading" in HTMLImageElement.prototype, It = !it && (!("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 Gt(t) {
const e = Date.now();
return gt.replace(/\s/, ` data-id='${e}-${t}' `);
}
function Ut(t, e) {
let n;
return function(...o) {
n != null && clearTimeout(n), n = setTimeout(() => {
t(...o), n = void 0;
}, e);
};
}
function ft(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 u = Ot({
image: r,
hash: typeof e == "string" ? e : void 0,
hashType: n,
size: o
});
u && (r.src = u);
}
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 (It || !Ct) {
dt(r), X(r), k(r);
continue;
}
if (r.src || (r.src = Gt(d)), r.complete && r.naturalWidth > 0) {
J(r, s);
continue;
}
const f = () => J(r, s);
r.addEventListener("load", f, { once: !0 }), i.add(
() => r.removeEventListener("load", f)
);
}
return () => {
for (const d of i) d();
i.clear();
};
}
function Tt(t = 'img[data-sizes="auto"], source[data-sizes="auto"]') {
for (const e of ut(t))
Z(e);
}
function J(t, e) {
if (bt(t)) {
dt(t), X(t), k(t), e?.(t);
return;
}
const n = new Image(), { srcset: o, src: c, sizes: s } = t.dataset;
if (s === "auto") {
const i = pt(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?.(t);
}, { once: !0 });
}
function Ot({
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 Lt(e, { ratio: c, size: o });
}
return St(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 = pt(t);
if (n && (t.sizes = `${n}px`), bt(t) && e?.processSourceElements)
for (const o of [...t.parentElement.getElementsByTagName("source")])
Z(o, { processSourceElements: !0 });
if (e?.updateOnResize) {
if (!$.has(t)) {
const o = Ut(() => 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 dt(t) {
const e = t.parentElement;
e?.tagName.toLowerCase() === "picture" && ([...e.querySelectorAll("source[data-srcset]")].forEach(X), [...e.querySelectorAll("source[data-src]")].forEach(k));
}
function pt(t) {
return t instanceof HTMLSourceElement ? t.parentElement?.getElementsByTagName("img")[0]?.offsetWidth : t.offsetWidth;
}
function bt(t) {
return t.parentElement?.tagName.toLowerCase() === "picture";
}
const Pt = Object.freeze({
autoSizes: Tt,
lazyLoad: ft,
loadImage: J
});
document.currentScript?.hasAttribute("init") && ft();
export {
Tt as autoSizes,
Pt as default,
ft as lazyLoad,
J as loadImage
};