import { IEditableAttribute } from "../../core/EditableAttributes.js"; import HtmlControl from "../../core/HtmlControl.js"; import { isInsideHtmlEditor } from "../../core/HtmlEditor.js"; import { updateIntrinsicSize } from "../../core/updateIntrinsicSize.js"; import { XNode } from "../../core/XNode.js"; export default class ImageSlices extends HtmlControl { static observedAttributes = [ "src", "width", "height", "rows", "columns", "delay", "delayFunc" ]; root: ShadowRoot; img: HTMLImageElement; get editableAttributes(): IEditableAttribute[] { return [ { name: "src", type: "image" }, { name: "render-size", type: "number" }, { name: "height", type: "size" }, { name: "width", type: "size" }, { name: "rows", type: "number" }, { name: "columns", type: "number" }, { name: "delay", type: "number" }, { name: "delayFunc", type: "string", value: "", suggestions: [ { label: "(x, y) => 10*(x + 10*y)", value: "(x, y) => 10*(x + 10*y)"}, { label: "(x, y) => 100*x + 100*y", value: "(x, y) => 100*x + 100*y"}, { label: "(x, y) => 150*x + 300*y", value: "(x, y) => 150*x + 300*y"}, ] }, { name: "mode", type: "enum", values: [ "shadow", "dom" ] } ]; } prepare() { updateIntrinsicSize(this, "width"); updateIntrinsicSize(this, "height"); if (isInsideHtmlEditor(this)) { const root = this.attachShadow({ mode: "open"}) this.root = root; XNode.render(root,
); this.img = this.root.querySelector("img") as HTMLImageElement; } this.updateImage().catch(console.error); } attributeChangedCallback(name, oldValue, newValue) { switch(name) { case "width": updateIntrinsicSize(this, "width"); break; case "height": updateIntrinsicSize(this, "height"); break; case "src": case "rows": case "delay": case "delayFunc": case "columns": this.updateImage().catch(console.error); break; } } async updateImage() { // prepare slices... this.innerHTML = ""; let src = this.getAttribute("src"); if (!src) { return; } const originalSrc = src; src = CSS.escape(src); if (isInsideHtmlEditor(this)) { // addAdoptedStyleRule(this, `[src="${src}"]`, `background-image: url("${src}");`); this.img?.setAttribute("src", originalSrc); return; } const rows = this.getAttributeAsNumber("rows", 10); const columns = this.getAttributeAsNumber("columns", 10); const delay = this.getAttributeAsTime("delay",15); const df = this.getDelayFunc(delay); const diffRow = 100 / rows; const diffCol = 100 / columns; for (let row = 0; row < rows; row++) { for(let col = 0; col < columns; col++) { const d = df(row, col); const slice = document.createElement("img"); const { style } = slice; style.animationDelay = d + "ms"; const left = `${col * diffCol}%`; const top = `${row * diffRow}%`; const right = `${(col + 1) * diffCol}%`; const bottom = `${(row + 1) * diffRow}%`; slice.setAttribute("src", originalSrc); style.clipPath = `polygon(${left} ${top}, ${right} ${top}, ${right} ${bottom}, ${left} ${bottom})` this.appendChild(slice); } } } getDelayFunc(delay) { let start = 0; const df = this.getAttribute("delayFunc"); if (!df) { return () => start += delay; } return new Function("row", "col", `return (${df})(row, col);`) } } customElements.define("image-slices", ImageSlices);