UNPKG

unlazy

Version:

Universal lazy loading library for placeholder images leveraging native browser APIs

370 lines (369 loc) 10.9 kB
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 };