UNPKG

ambilighter

Version:

Ambilight around the video, image or canvas HTML elements

400 lines (396 loc) 11.1 kB
(function(){"use strict";var t;try{if(typeof document<"u"){var e=document.createElement("style");e.nonce=(t=document.head.querySelector("meta[property=csp-nonce]"))==null?void 0:t.content,e.appendChild(document.createTextNode(".ambi-box{display:contents;position:relative;transition:box-shadow .4s}.ambi-box__back{position:absolute;left:0;top:0;width:100%;height:100%}")),document.head.appendChild(e)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})(); var A = Object.defineProperty; var C = (i, t, e) => t in i ? A(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e; var o = (i, t, e) => C(i, typeof t != "symbol" ? t + "" : t, e); class b { constructor(t, e) { o(this, "el"); o(this, "options"); o(this, "canvas"); try { if (!(t instanceof HTMLElement)) throw new Error("Ambilighter -> constructor :: invalid element"); if (typeof e != "object") throw new Error("Ambilighter -> constructor :: invalid options"); this.el = t, this.options = e, this.canvas = document.createElement("canvas"); } catch (r) { console.error(r); } } static validate(t) { return t instanceof HTMLElement; } updateCanvas(...t) { } /** * Hooks */ onLoad() { } onUpdate(...t) { } onDestroy() { } } function s(i) { if (i === null) return { r: 0, g: 0, b: 0, a: 1 }; if (!(i instanceof ImageData)) throw new Error("Ambilightify -> averageColor :: invalid image. Expected ImageData"); const t = i.data, e = { r: 0, g: 0, b: 0, a: 1 }; for (let a = 0; a < t.length; a += 4) { const n = t[a], u = t[a + 1], l = t[a + 2], g = t[a + 3]; e.r = e.r + n, e.g = e.g + u, e.b = e.b + l, e.a = e.a + g; } const r = t.length / 4; return e.r = Math.ceil(e.r / r), e.g = Math.ceil(e.g / r), e.b = Math.ceil(e.b / r), e.a = Math.ceil(e.a / r), e; } function c(i, t) { if (!(i instanceof HTMLCanvasElement)) throw new Error("getBorderImage :: invalid canvas"); const e = i.getContext("2d"), r = i.width, a = i.height; return e ? e.getImageData( (r - 1) * t[0], (a - 1) * t[1], r * t[2] || 1, a * t[3] || 1 ) : null; } const h = { top: [0, 0, 1, 0], bottom: [0, 1, 1, 0], left: [0, 0, 0, 1], right: [1, 0, 0, 1], bottomRight: [0, 0, 1, 1] }; class v extends b { constructor(t, e) { super(t, e); try { if (!(t instanceof HTMLImageElement)) throw new Error("AmbilighterImage -> constructor :: invalid element"); t.onload = () => { this.update(); }, this.update(); } catch (r) { console.error(r); } return new Proxy(this, { set(r, a, n) { return a === "onUpdate" && (r[a] = n, r.update()), !0; } }); } static validate(t) { return t instanceof HTMLImageElement; } update() { this.updateCanvas(this.el); const t = { frame: { top: { color: s(c(this.canvas, h.top)), radius: this.radius }, bottom: { color: s(c(this.canvas, h.bottom)), radius: this.radius }, left: { color: s(c(this.canvas, h.left)), radius: this.radius }, right: { color: s(c(this.canvas, h.right)), radius: this.radius } }, averageColor: s(c(this.canvas, h.bottomRight)) }; this.onUpdate(t); } /** * Updates when picture repainted * @param image { any } */ updateCanvas(t) { try { const e = this.canvas.getContext("2d"); e && (this.canvas.width = t.naturalWidth, this.canvas.height = t.naturalHeight, e.drawImage(t, 0, 0, t.naturalWidth, t.naturalHeight)); } catch (e) { console.error(e); } } /** * getters / setters */ get radius() { var t; return ((t = this == null ? void 0 : this.options) == null ? void 0 : t.radius) || 0; } /** * Hooks */ onLoad(...t) { } onUpdate(...t) { } onDestroy() { } } class w extends b { constructor(t, e) { super(t, e); try { if (!(t instanceof HTMLVideoElement)) throw new Error("AmbilighterVideo -> constructor :: invalid element"); this.init(); } catch (r) { console.error(r); } } static validate(t) { return t instanceof HTMLVideoElement; } init() { this.el.addEventListener("loadedmetadata", () => this.update()), this.el.addEventListener("timeupdate", () => this.update()); } deinit() { this.el.removeEventListener("loadedmetadata", () => this.update()), this.el.removeEventListener("timeupdate", () => this.update()); } update() { this.updateCanvas(this.el); const t = { frame: { top: { color: s(c(this.canvas, h.top)), radius: this.radius }, bottom: { color: s(c(this.canvas, h.bottom)), radius: this.radius }, left: { color: s(c(this.canvas, h.left)), radius: this.radius }, right: { color: s(c(this.canvas, h.right)), radius: this.radius } }, averageColor: s(c(this.canvas, h.bottomRight)) }; this.onUpdate(t); } /** * Updates when picture repainted * @param video { any } */ updateCanvas(t) { try { const e = this.canvas.getContext("2d"); e && (this.canvas.width = t.videoWidth, this.canvas.height = t.videoHeight, e.drawImage(t, 0, 0, t.videoWidth, t.videoHeight)); } catch (e) { console.error(e); } } /** * getters / setters */ get radius() { var t; return ((t = this == null ? void 0 : this.options) == null ? void 0 : t.radius) || 0; } /** * Hooks */ onLoad(...t) { } onUpdate(...t) { } onDestroy() { } } class E extends b { constructor(t, e) { super(t, e); try { if (!(t instanceof HTMLCanvasElement)) throw new Error("AmbilighterCanvas -> constructor :: invalid element"); } catch (r) { console.error(r); } } static validate(t) { return t instanceof HTMLCanvasElement; } /** * Updates when picture repainted * @param canvas { any } */ updateCanvas(t) { try { this.canvas = t; } catch (e) { console.error(e); } } /** * getters / setters */ get radius() { var t; return ((t = this == null ? void 0 : this.options) == null ? void 0 : t.radius) || 0; } /** * Hooks */ onLoad(...t) { } onUpdate(...t) { } onDestroy() { } } class $ { static produce(t, e) { return v.validate(t) ? new v(t, e) : w.validate(t) ? new w(t, e) : E.validate(t) ? new E(t, e) : null; } } function f(i = 0) { return typeof i == "number" && i >= 0 && i <= 255; } function L(i = 0) { return typeof i == "number" && i >= 0 && i <= 1; } class m { constructor({ r: t, g: e, b: r, a } = { r: 0, b: 0, g: 0, a: 1 }) { o(this, "r"); o(this, "g"); o(this, "b"); o(this, "a"); this.r = f(t) ? t : 0, this.g = f(e) ? e : 0, this.b = f(r) ? r : 0, this.a = L(a) ? a : 1; } toString() { return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`; } } class S { static parseSize(t = "0px") { const e = /^(-?\d+(\.\d+)?)(px|%|em|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc)$/, r = t.match(e); if (r) { const a = parseFloat(r[1]), n = r[3]; return { value: a, unit: n }; } return null; } } class p { static formatStringAttribute(t, e = "") { if (typeof e != "string") throw new Error(`Formatted -> formatStringAttribute :: invalid valueDefault. Expected "string" type, got "${typeof e}"`); return typeof t != "string" ? e : t; } static formatNumberAttribute(t, e = 0) { if (typeof e != "number") throw new Error(`Formatted -> formatNumberAttribute :: invalid valueDefault. Expected "number" type, got "${typeof e}"`); return t === null || isNaN(+t) ? e : +t; } static formatBooleanAttribute(t, e = !1) { const r = ["true", "false"]; if (typeof e != "boolean") throw new Error(`Formatted -> formatNumberAttribute :: invalid valueDefault. Expected "boolean" type, got "${typeof e}"`); return typeof t == "string" && r.includes(t) ? t === "true" : typeof t == "boolean" ? t : typeof t == "string" || t === null ? t !== null : e; } static formatArrayAttribute(t, e = []) { let r = e; if (t !== "string" || !JSON.parse(t)) throw new Error("Formatted -> formatArrayAttribute :: cannot parse val as JSON"); if (r = JSON.parse(t), !(r instanceof Array)) throw new Error('Formatted -> formatArrayAttribute :: invalid value. Expected "array"'); return r; } static formatValues(t, e = "24px") { let r = t; if (t || (r = e), typeof r == "string") { if (["auto", "inherit"].includes(r)) return r; if (parseInt(r).toString() === r) return `${r}px`; const a = S.parseSize(r); return a ? `${a.value}${a.unit}` : e; } return `${r}px`; } } class H extends HTMLElement { constructor() { super(); o(this, "rendered", !1); o(this, "ambilighter", null); } update() { var e; try { const r = p.formatStringAttribute(this.getAttribute("target"), ""), a = p.formatStringAttribute(this.getAttribute("light-to"), ""), n = p.formatNumberAttribute(this.getAttribute("radius"), 0), u = document.getElementById(r) || this.children[0], l = document.getElementById(a) || this.children[0], g = { radius: n }; u instanceof HTMLElement && (this.ambilighter = $.produce(u, g), (e = this.ambilighter) != null && e.onUpdate && (this.ambilighter.onUpdate = (d) => { const y = new CustomEvent("onupdate", { detail: { data: d }, bubbles: !0, composed: !0 }); if (l instanceof HTMLElement) { const x = [ `0 -${n / 2}px ${n}px ${new m(d.frame.top.color).toString()}`, `${n / 2}px 0 ${n}px ${new m(d.frame.right.color).toString()}`, `0 ${n / 2}px ${n}px ${new m(d.frame.bottom.color).toString()}`, `-${n / 2}px 0 ${n}px ${new m(d.frame.left.color).toString()}` ]; l.style.boxShadow = x.join(","), this.dispatchEvent(y); } })); } catch (r) { console.error(r); } } render() { this.className = "ambi-box", this.update(), this.attachShadow({ mode: "open" }), this.shadowRoot && (this.shadowRoot.innerHTML = ` <link rel="stylesheet" href="./src/css/ambi-box.css"></link> <slot></slot> `), this.rendered = !0; } connectedCallback() { this.render(); } disconnectedCallback() { } static get observedAttributes() { return [ "target", "radius", "light-to" ]; } attributeChangedCallback() { this.rendered && this.update(); } adoptedCallback() { } } customElements.define("ambi-box", H); export { H as AmbiBox, $ as Ambilight, E as AmbilighterCanvas, v as AmbilighterImage, w as AmbilighterVideo, m as Color };