1
0
mirror of https://gitlab.com/Anson-Projects/projects.git synced 2025-06-15 14:36:47 +00:00

make a cute little canvas to draw on and hook it up to some ojs

This commit is contained in:
Anson 2025-05-25 20:16:35 +00:00
parent 51c03d9213
commit 7cd96f1997
2 changed files with 115 additions and 2 deletions

View File

@ -2,8 +2,7 @@ const kProgressiveAttr = "data-src";
let categoriesLoaded = false;
window.quartoListingCategory = (category) => {
// category is URI encoded in EJS template for UTF-8 support
category = decodeURIComponent(atob(category));
category = atob(category);
if (categoriesLoaded) {
activateCategory(category);
setCategoryHash(category);

View File

@ -0,0 +1,114 @@
---
title: "Brachistochrone"
description: |
Lets create a double pendulum in Observable JS!
date: 2025-05-09
categories:
- Observable JS
- Code
- Math
---
This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text
:::{.column-screen}
```{ojs}
//| echo: false
viewof canvas = {
const height = 500;
const canvas = html`<canvas width=${width} height="${height}" style="border: 1px solid #ccc; cursor: crosshair;"></canvas>`;
const ctx = canvas.getContext('2d');
let isDrawing = false;
let lastX = 0;
let lastY = 0;
let pathLength = 0;
let pathHistory = [{time: Date.now(), length: 0}];
// Drawing settings
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
canvas.addEventListener('mousedown', (e) => {
if (!isDrawing) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.arc(300, 100, 5, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.arc(600, 300, 5, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
pathLength = 0;
pathHistory = [{time: Date.now(), length: 0}];
canvas.value = {length: pathLength, history: pathHistory};
canvas.dispatchEvent(new CustomEvent("input"));
}
isDrawing = true;
[lastX, lastY] = [e.offsetX, e.offsetY];
});
canvas.addEventListener('mousemove', (e) => {
if (!isDrawing) return;
// Calculate distance
const distance = Math.sqrt(
Math.pow(e.offsetX - lastX, 2) +
Math.pow(e.offsetY - lastY, 2)
);
const angle = Math.atan2(e.offsetY - lastY, e.offsetX - lastX)
pathLength += distance;
// Draw
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
// Update value and emit event
pathHistory.push({time: Date.now(), length: pathLength});
canvas.value = {length: pathLength, history: pathHistory};
canvas.dispatchEvent(new CustomEvent("input"));
});
canvas.addEventListener('mouseup', () => isDrawing = false);
canvas.addEventListener('mouseout', () => isDrawing = false);
// Initial value
canvas.value = {length: 0, history: pathHistory};
return canvas;
}
```
```{ojs}
//| echo: false
md`Current path length: **${canvas.length?.toFixed(0) || 0} pixels**`
```
```{ojs}
//| echo: false
import {Plot} from "@observablehq/plot"
pathChart = Plot.plot({
width: width,
height: 200,
y: {label: "Path Length (pixels)"},
x: {label: "Time", type: "time"},
marks: [
Plot.line(canvas.history || [], {
x: "time",
y: "length",
stroke: "steelblue",
strokeWidth: 2
})
]
})
```
This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text