ambilighter
Version:
Ambilight around the video, image or canvas HTML elements
400 lines (396 loc) • 11.1 kB
JavaScript
(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
};