# Application Frame
//wiki.ralfbarkow.ch/assets/pages/js-snippet-template/esm.html HEIGHT 256
import { closestPointEllipse } from "https://esm.run/@thi.ng/geom-closest-point"; import { $compile } from "https://esm.run/@thi.ng/rdom"; import { $canvas } from "https://esm.run/@thi.ng/rdom-canvas"; import { merge, reactive } from "https://esm.run/@thi.ng/rstream"; import { gestureStream } from "https://esm.run/@thi.ng/rstream-gestures"; import { mapcat, repeatedly } from "https://esm.run/@thi.ng/transducers"; import { add2, normalCCW, random2 } from "https://esm.run/@thi.ng/vectors";
const W = 600; // define random ellipses ([origin, radius] tuples) const ELLIPSES = [ ...repeatedly(() => [random2([], 50, W - 50), random2([], 10, W / 2)], 5), ];
Compile & mount Reactive Canvas component:
$compile($canvas( // stream merge merge({ src: [ // #1 initial call to action... reactive([ "g", {}, [ "text", { align: "center", fill: "black" }, [W / 2, W / 2], "Move your mouse / finger!", ], ]), // #2 stream of mouse/touch coordinates (main) gestureStream(document.body).map((e) => [ "g", // disable canvas clearing if no mouse buttons pressed { fill: "none", __clear: !!e.buttons }, // semi-transparent white rect to fade previous frame ["rect", { fill: [1, 1, 1, 0.2] }, [0, 0], W, W], // declare ellipses, closest points and tangents ...mapcat(([o, r]) => { const p = closestPointEllipse(e.pos, o, r); return [ ["ellipse", { stroke: "#ccc" }, o, r], ["circle", { stroke: "#f0f" }, p, 5], ["line", { stroke: "#666" }, e.pos, p], [ "line", { stroke: "#6c0" }, p, add2(null, normalCCW([], p, e.pos, 100), p), ], ]; }, ELLIPSES), ]), ], }), [W, W])).mount(document.getElementById("output"));
Note: The id property has been changed from "app" to "output".