UNPKG

unlazy

Version:

Universal lazy loading library for placeholder images leveraging native browser APIs

367 lines (366 loc) 10.7 kB
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 };