Public
Edited
May 10, 2023
Insert cell
Insert cell
viewof strokes = {
const context = DOM.context2d(width, 300);
const strokes = (context.canvas.value = []);
const curve = d3.curveBasis(context);
const redo = [];

let tmp = 0;

context.lineJoin = "round";
context.lineCap = "round";

// Render and report the new value.
function render() {
tmp++;
if (tmp == 1) console.log("first render");
context.clearRect(0, 0, width, 300);
for (const stroke of strokes) {
context.beginPath();
curve.lineStart();
for (const point of stroke) {
curve.point(...point);
}
if (stroke.length === 1) curve.point(...stroke[0]);
curve.lineEnd();
context.lineWidth = stroke.lineWidth;
context.strokeStyle = stroke.strokeStyle;
context.stroke();
}
context.canvas.value = strokes;
context.canvas.dispatchEvent(new CustomEvent("input"));
}

d3.select(context.canvas).call(
d3
.drag()
.container(context.canvas)
.subject(dragsubject)
// .on("load", render)
.on("start drag", dragged)
.on("start.render drag.render", render)
);

context.canvas.undo = () => {
if (strokes.length === 0) return;
redo.push(strokes.pop());
render();
};

context.canvas.redo = (stroke) => {
if (redo.length === 0) return;
strokes.push(redo.pop());
render();
};

// Create a new empty stroke at the start of a drag gesture.
function dragsubject() {
strokes.splice(0);
const stroke = [];
stroke.lineWidth = 1;
stroke.strokeStyle = "#000000";
strokes.push(stroke);
redo.length = 0;
return stroke;
}

// Add to the stroke when dragging.
function dragged({ subject, x, y }) {
subject.push([x, y]);
}

return context.canvas;
}
Insert cell
strokes
Insert cell
defaultStrokes = [
[
[45, 259.8999938964844],
[45, 259.8999938964844],
[48, 258.8999938964844],
[55, 254.89999389648438],
[64, 249.89999389648438],
[75, 244.89999389648438],
[90, 236.89999389648438],
[108, 226.89999389648438],
[127, 215.89999389648438],
[148, 203.89999389648438],
[165, 193.89999389648438],
[180, 185.89999389648438],
[203, 172.89999389648438],
[231, 158.89999389648438],
[251, 148.89999389648438],
[270, 140.89999389648438],
[296, 131.89999389648438],
[315, 125.89999389648438],
[331, 121.89999389648438],
[348, 119.89999389648438],
[367, 116.89999389648438],
[384, 114.89999389648438],
[395, 113.89999389648438],
[403, 113.89999389648438],
[420, 113.89999389648438],
[444, 113.89999389648438],
[450, 113.89999389648438],
[463, 113.89999389648438],
[476, 114.89999389648438],
[489, 115.89999389648438],
[502, 116.89999389648438],
[516, 118.89999389648438],
[531, 119.89999389648438],
[546, 120.89999389648438],
[562, 120.89999389648438],
[590, 119.89999389648438],
[620, 115.89999389648438],
[644, 110.89999389648438],
[670, 104.89999389648438],
[697, 98.89999389648438],
[725, 92.89999389648438],
[754, 86.89999389648438],
[779, 80.89999389648438],
[799, 75.89999389648438],
[821, 68.89999389648438],
[843, 62.899993896484375],
[863, 56.899993896484375],
[881, 51.899993896484375],
[898, 48.899993896484375],
[913, 44.899993896484375],
[928, 42.899993896484375],
[942, 39.899993896484375],
[957, 38.899993896484375],
[969, 37.899993896484375],
[979, 35.899993896484375],
[989, 34.899993896484375],
[999, 33.899993896484375],
[1011, 31.899993896484375],
[1012, 30.899993896484375],
[1017, 29.899993896484375],
[1021, 28.899993896484375],
[1024, 27.899993896484375],
[1027, 27.899993896484375],
[1029, 26.899993896484375],
[1031, 26.899993896484375],
[1033, 25.899993896484375],
[1034, 24.899993896484375],
[1035, 24.899993896484375],
[1035, 23.899993896484375],
[1036, 23.899993896484375],
[1036, 23.899993896484375],
[1037, 23.899993896484375],
[1037, 23.899993896484375],
[1037, 23.899993896484375],
[1037, 23.899993896484375],
[1037, 23.899993896484375],
[1037, 23.899993896484375],
[1037, 23.899993896484375],
[1038, 22.899993896484375]
]
]
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more