Rendering
Once your simulation is running, you need to draw it on the screen. Both verlet-engine and verlet-react use the HTML5 Canvas API for rendering, but they approach it differently.
Default Rendering
For many cases, the default, built-in rendering is all you need.
- React
- JavaScript
The <VerletCanvas> component handles rendering automatically. It finds all particles and constraints in your simulation and draws them with a default style. This is the easiest way to get started.
// The points and constraints are drawn automatically.
<VerletCanvas width={600} height={400}>
<Point id="p1" pos={new Vec2(200, 100)} pinned />
<Point id="p2" pos={new Vec2(300, 100)} />
<DistanceConstraint from="p1" to="p2" />
</VerletCanvas>
You can customize the appearance by passing a style prop to <Point> and constraint components.
With verlet-engine, there is no default renderer. You are always in complete control of the drawing process. You need to get a canvas 2D context and write your own rendering logic inside the animation loop.
const canvas = document.getElementById('my-canvas');
const ctx = canvas.getContext('2d');
function animate() {
sim.frame(); // Update physics
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const c of sim.composites) {
// Draw constraints that have a `draw` method
for (const constraint of c.constraints) {
if (typeof constraint.draw === 'function') {
constraint.draw(ctx);
}
}
// Draw particles
for (const p of c.particles) {
ctx.beginPath();
ctx.arc(p.pos.x, p.pos.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
}
requestAnimationFrame(animate);
}
Custom Rendering
If you need more control over the appearance of your simulation, you can provide a custom rendering function.
- React
- JavaScript
The <VerletCanvas> component accepts a customRenderer prop. This function gives you direct access to the canvas ctx and the array of composites on every frame. When you provide this prop, the default renderer is disabled.
import React from 'react';
import { VerletCanvas, Point } from 'verlet-react';
import { Vec2 } from 'verlet-engine';
const myCustomRenderer = (ctx, composites) => {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillStyle = 'red';
for (const c of composites) {
for (const p of c.particles) {
// Draw all particles as red squares
ctx.fillRect(p.pos.x - 5, p.pos.y - 5, 10, 10);
}
}
};
const App = () => {
return (
<VerletCanvas width={600} height={400} customRenderer={myCustomRenderer}>
<Point id="p1" pos={new Vec2(300, 200)} />
</VerletCanvas>
);
};
As mentioned before, you are always in control of rendering. To create a "custom" renderer, you simply change the drawing logic inside your animate function. This gives you unlimited flexibility to draw your simulation however you like, for example using WebGL or another rendering library.
// Example of custom logic inside the animate function:
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
for (const c of sim.composites) {
// Custom: only draw the constraints, not the particles
for (const constraint of c.constraints) {
if (constraint instanceof DistanceConstraint) {
ctx.beginPath();
ctx.moveTo(constraint.a.pos.x, constraint.a.pos.y);
ctx.lineTo(constraint.b.pos.x, constraint.b.pos.y);
ctx.stroke();
}
}
}