This commit is contained in:
2026-08-19 17:17:46 +02:00
parent a40f10ab22
commit 2ae236cc67
5 changed files with 64 additions and 25 deletions
+12 -7
View File
@@ -1,4 +1,4 @@
import { Point, lerpPoint, drawCircle, drawLine, drawDashedLine, resizeCanvas } from "./common.js"
import { Point, lerpPoint, drawCircle, drawLine, drawDashedLine, resizeCanvas, drawCurve } from "./common.js"
let points: Point[] = [];
@@ -17,7 +17,8 @@ function update(ctx: CanvasRenderingContext2D, time: number) {
drawDashedLine(ctx, ab, bc, 0xFF0000);
drawCircle(ctx, ab, 1, 0xFF0000);
drawCircle(ctx, bc, 1, 0xFF0000);
drawCircle(ctx, abc, 1, 0xFFFFFF);
drawCircle(ctx, abc, 1, 0xFFFFFF); // Point on the bezier curve
} else if (points.length === 4) {
const [a, b, c, d] = points;
const ab = lerpPoint(a, b, p);
@@ -35,7 +36,8 @@ function update(ctx: CanvasRenderingContext2D, time: number) {
drawCircle(ctx, cd, 1, 0xFF0000);
drawCircle(ctx, abc, 1, 0xFFFF00);
drawCircle(ctx, bcd, 1, 0xFFFF00);
drawCircle(ctx, abcd, 1, 0xFFFFFF);
drawCircle(ctx, abcd, 1, 0xFFFFFF); // Point on the bezier curve
}
for (let i = 0; i < points.length - 1; ++i) {
@@ -60,10 +62,13 @@ function init() {
resizeCanvas(ctx); // Init canvas to full window size
canvas.addEventListener("click", (evt) => {
const {clientX, clientY} = evt;
if (points.length < 4) {
points.push({x: clientX, y: clientY});
}
const rect = canvas.getBoundingClientRect();
if (points.length < 4) {
points.push({
x: evt.clientX - rect.left,
y: evt.clientY - rect.top,
});
}
});
window.addEventListener('resize', () => resizeCanvas(ctx));
+20 -2
View File
@@ -3,9 +3,27 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello typescript</title>
<title>Metaballs</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #000000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body style="margin: 0; padding 0; overflow: hidden; background-color: #000000">
<body>
<canvas id="canvas" tabindex="0"></canvas>
<script type="module" src="./metaballs.js"></script>
</body>
+15 -9
View File
@@ -1,3 +1,5 @@
import { resizeCanvas } from "./common.js";
let pos = { x: 0, y: 0 }
function init() {
@@ -5,20 +7,25 @@ function init() {
if (!canvas) throw new Error("unable to get canvas HTML element");
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D | null;
if (!ctx) throw new Error("unable to get canvas 2D context");
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
resizeCanvas(ctx);
canvas.onmousemove = (evt) => {
const { clientX, clientY } = evt;
const rect = canvas.getBoundingClientRect();
//const index = (clientY * canvas.width + clientX) * 4;
/*console.log("color",
pixels[index],
pixels[index+1],
pixels[index+2]);*/
pos = { x: clientX, y: clientY };
pos = {
x: (evt.clientX - rect.left) * canvas.width / rect.width,
y: (evt.clientY - rect.top) * canvas.height / rect.height,
};
}
// A canvas does not normally receive resize events. Use a resize observer
const observer = new ResizeObserver(() => resizeCanvas(ctx));
observer.observe(canvas);
window.requestAnimationFrame(t => update(ctx, t));
}
@@ -34,15 +41,15 @@ function update(ctx: CanvasRenderingContext2D, timestamp: number) {
const d1 = 6000 / ((x - width/2)*(x - width/2) + (y - height/2)*(y - height/2));
const d2 = 8000 / ((x - pos.x)*(x - pos.x) + (y - pos.y)*(y - pos.y));
if (d1 + d2 >= 0.90 && d1 + d2 <= 1.00) {
pixels[index] = 255*d1;
pixels[index] = 255 * d1;
pixels[index+1] = 0;
pixels[index+2] = 255*d2;
pixels[index+2] = 255 * d2;
pixels[index+3] = 255;
} else {
pixels[index] = 0;
pixels[index+1] = 0;
pixels[index+2] = 0;
pixels[index+3] = 255;
pixels[index+3] = 0;
}
}
}
@@ -52,4 +59,3 @@ function update(ctx: CanvasRenderingContext2D, timestamp: number) {
}
init();
+2 -1
View File
@@ -99,7 +99,8 @@ function init() {
canvas.onmousemove = (evt) => {
const {clientX, clientY} = evt;
target = new Vec2d(clientX, clientY);
const rect = canvas.getBoundingClientRect();
target = new Vec2d(clientX - rect.left, clientY - rect.top);
}
canvas.onclick = () => {
+9
View File
@@ -38,9 +38,14 @@ export default class Vec2d implements Point {
normalize(): Vec2d {
const len = this.length();
if (len === 0) return new Vec2d(0, 0); // avoid division by zero
return new Vec2d(this.x / len, this.y / len);
}
neg(): Vec2d {
return new Vec2d(-this.x, -this.y);
}
scale(scalar: number): Vec2d {
return new Vec2d(this.x * scalar, this.y * scalar);
}
@@ -61,6 +66,10 @@ export default class Vec2d implements Point {
return this.sub(other).length();
}
dot(other: Vec2d): number {
return this.x * other.x + this.y * other.y;
}
lerp(other: Vec2d, t: number): Vec2d {
// (1-t)*A + B*t
return this.scale(1 - t).add(other.scale(t));