UNPKG

@alegendstale/holly-components

Version:

Reusable UI components created using lit

118 lines (117 loc) 4.01 kB
import { html as l } from "lit"; import { query as d, property as p, customElement as f } from "lit/decorators.js"; import w from "quantize"; import { CanvasBase as c } from "./canvas-base.js"; import y from "./canvas-image.styles.js"; var v = Object.defineProperty, x = Object.getOwnPropertyDescriptor, h = (t, a, e, i) => { for (var s = i > 1 ? void 0 : i ? x(a, e) : a, o = t.length - 1, n; o >= 0; o--) (n = t[o]) && (s = (i ? n(a, e, s) : n(s)) || s); return i && s && v(a, e, s), s; }; let r = class extends c { constructor() { super(...arguments), this.imageURL = "", this.smoothing = !1, this.width = 0, this.height = 0, this.status = "idle"; } updated(t) { super.updated(t), t.has("imageURL") ? this.status = this.imageURL ? "loading" : "idle" : (t.has("width") || t.has("height")) && this.status === "success" && this._performDraw(); } get updateComplete() { return (async () => { if (await super.updateComplete, this.status === "loading" && await new Promise((a) => { this.addEventListener("canvas-ready", a, { once: !0 }); }), this.status === "error") throw new Error(`canvas-image failed to load ${this.imageURL}`); return !0; })(); } render() { return l` ${super.render()} <img src=${this.imageURL} crossorigin='anonymous' @load=${() => this._performDraw()} @error=${() => this._handleLoadError()} > `; } /** * Updates & loads the canvas image. * Attempts to preserve aspect ratio based on width. */ _performDraw() { const t = this.image; if (!t || !this.context) return; const a = t.naturalHeight / t.naturalWidth; let e = this.width, i = e * a; i > this.height && (e = this.height / a, i = this.height), this.canvas.width = e, this.canvas.height = i, this.context.imageSmoothingEnabled = this.smoothing, this.context.drawImage(t, 0, 0, e, i), this.status = "success", this.dispatchEvent(new Event("canvas-ready")); } /** @internal Handle errors when the image fails to load. */ _handleLoadError() { this.status = this.imageURL === "" ? "idle" : "error", this.dispatchEvent(new Event("canvas-ready")); } /** * Gets the most frequent colors in an image. * @param numColors Number of colors to return * @param quality Artificially reduce number of pixels (higher = less accurate but faster) * @returns Most frequent colors */ async getPalette(t = 7, a = 10) { const e = t <= 7 ? t : t + 1; await this.updateComplete; const i = await this.createPixelArray(a); if (!i) return null; const s = w(i, e); return s ? s.palette() : []; } /** * Creates an array of pixels from the image. * Inspired by colorthief * @param quality Artificially reduce number of pixels (higher = less accurate but faster) * @returns Array of pixels */ async createPixelArray(t) { await this.updateComplete; const a = [], e = (await this.getImageData())?.data; if (!e) return null; const i = this.height * this.width; for (let s = 0; s < i; s += t) { const o = s * 4, [n, m, u, g] = [e[o + 0], e[o + 1], e[o + 2], e[o + 3]]; (typeof g > "u" || g >= 125) && (n > 250 && m > 250 && u > 250 || a.push([n, m, u])); } return a; } /** Gets the image data from the canvas. */ async getImageData(t = 0, a = 0) { try { return await this.updateComplete, this.context.getImageData(t, a, this.canvas.width, this.canvas.height); } catch { throw new Error("Failed to get image data."); } } }; r.styles = [...c.styles, y]; h([ d("img") ], r.prototype, "image", 2); h([ p({ type: String }) ], r.prototype, "imageURL", 2); h([ p({ type: Boolean }) ], r.prototype, "smoothing", 2); h([ p({ type: Number }) ], r.prototype, "width", 2); h([ p({ type: Number }) ], r.prototype, "height", 2); h([ p({ type: String, reflect: !0, attribute: "data-status" }) ], r.prototype, "status", 2); r = h([ f("canvas-image") ], r); export { r as CanvasImage };