@alegendstale/holly-components
Version:
Reusable UI components created using lit
118 lines (117 loc) • 4.01 kB
JavaScript
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'
=${() => this._performDraw()}
=${() => 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
};