@thi.ng/hdom-canvas
Version:
@thi.ng/hdom component wrapper for declarative canvas scenegraphs
132 lines (131 loc) • 3.52 kB
JavaScript
import { NO_OP } from "@thi.ng/api/api";
import { isArray } from "@thi.ng/checks/is-array";
import { isNotStringAndIterable } from "@thi.ng/checks/is-not-string-iterable";
import { diffArray } from "@thi.ng/diff/array";
import { assert } from "@thi.ng/errors/assert";
import { equiv, releaseTree } from "@thi.ng/hdom/diff";
import { draw } from "@thi.ng/hiccup-canvas/draw";
const FN = "function";
const STR = "string";
const canvas = {
render(_, attribs, ...body) {
const cattribs = { ...attribs };
delete cattribs.__diff;
delete cattribs.__normalize;
const dpr = window.devicePixelRatio || 1;
if (dpr !== 1) {
!cattribs.style && (cattribs.style = {});
cattribs.style.width = `${cattribs.width}px`;
cattribs.style.height = `${cattribs.height}px`;
cattribs.width *= dpr;
cattribs.height *= dpr;
}
return [
"canvas",
cattribs,
[
"g",
{
__impl: IMPL,
__diff: attribs.__diff !== false,
__normalize: attribs.__normalize !== false,
__release: attribs.__release === true,
__serialize: false,
__clear: attribs.__clear,
scale: dpr !== 1 ? dpr : null
},
...body
]
];
}
};
const createTree = (_, canvas2, tree) => {
const ctx = canvas2.getContext("2d");
assert(!!ctx, "canvas ctx unavailable");
const attribs = tree[1];
if (attribs) {
if (attribs.__skip) return;
if (attribs.__clear !== false) {
ctx.clearRect(0, 0, canvas2.width, canvas2.height);
}
}
draw(ctx, tree);
};
const normalizeTree = (opts, tree) => {
if (tree == null) {
return tree;
}
if (isArray(tree)) {
const tag = tree[0];
if (typeof tag === FN) {
return normalizeTree(
opts,
tag.apply(null, [opts.ctx, ...tree.slice(1)])
);
}
if (typeof tag === STR) {
const attribs = tree[1];
if (attribs?.__normalize === false) {
return tree;
}
const res = [tree[0], attribs];
for (let i = 2, n = tree.length; i < n; i++) {
const n2 = normalizeTree(opts, tree[i]);
n2 != null && res.push(n2);
}
return res;
}
} else if (typeof tree === FN) {
return normalizeTree(opts, tree(opts.ctx));
} else if (typeof tree.toHiccup === FN) {
return normalizeTree(opts, tree.toHiccup(opts.ctx));
} else if (typeof tree.deref === FN) {
return normalizeTree(opts, tree.deref());
} else if (isNotStringAndIterable(tree)) {
const res = [];
for (let t of tree) {
const n = normalizeTree(opts, t);
n != null && res.push(n);
}
return res;
}
return tree;
};
const diffTree = (opts, parent, prev, curr, child) => {
const attribs = curr[1];
if (attribs.__skip) return;
if (attribs.__diff === false) {
releaseTree(prev);
return createTree(opts, parent, curr);
}
let impl = attribs.__impl;
if (impl && impl !== IMPL) {
return impl.diffTree(opts, parent, prev, curr, child);
}
const delta = diffArray(prev, curr, "only-distance", equiv);
if (delta.distance > 0) {
return createTree(opts, parent, curr);
}
};
const IMPL = {
createTree,
normalizeTree,
diffTree,
hydrateTree: NO_OP,
getElementById: NO_OP,
createElement: NO_OP,
createTextElement: NO_OP,
replaceChild: NO_OP,
getChild: NO_OP,
removeAttribs: NO_OP,
removeChild: NO_OP,
setAttrib: NO_OP,
setContent: NO_OP
};
export {
IMPL,
canvas,
createTree,
diffTree,
normalizeTree
};