JSFiddle - React, Tailwind, and code Playground

by moritzkobrna

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.5/dat.gui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/victor/1.1.0/victor.js"></script>
<canvas id="canvas" width="800" height="800"></canvas>

<button onClick={window.doStep()}>Step</button>

JavaScript 1.7

const Vector = Victor;
const gui = new dat.GUI();

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');

const options = {
  fieldSize: {w: 800, h: 800},
	forceFieldResolution: 5,
  forceFieldVectorScale: 50,
  gravitationalConstant: 0.01,
  maxStepLength: 5,
  showForceField: true,
};

const planets = [
	{
  	position: [180, 140],
    radius: 30,
  },
  {
  	position: [350, 300],
    radius: 25,
  },
  {
  	position: [220, 250],
    radius: 20,
  },
  {
  	position: [100, 330],
    radius: 40,
  },
];

const emitters = [
	{
  	position: [110, 80],
    angle: -150,
    velocity: 3.35,
  },
];

let bullets = [];
const forceField = [];

const calcForceFieldVector = (x, y, bullet) => {
  let forceVector = new Vector(0, 0);
  planets.forEach(({ position: [x2, y2], radius }) => {
  	const vector = new Vector(x2 - x, y2 - y);
    const dist = vector.length();
    if (dist < radius) {
    	if (bullet) bullet.alive = false;
      return;
    }
    const force = (1 / Math.pow(dist, 1)) * radius * radius * options.gravitationalConstant;
    vector.normalize().multiply(new Vector(force, force));
    forceVector.add(vector);
  });
  return forceVector;
}

const calcForceField = (resolution) => {
	for (let x = 0; x < options.fieldSize.w; x += options.forceFieldResolution) {
  	forceField[x] = [];
  	for (let y = 0; y < options.fieldSize.h; y += options.forceFieldResolution) {
    	forceField[x][y] = calcForceFieldVector(x, y);
    }
  }	
}

const step = (frame, ctx) => {
  bullets.filter((b) => b.alive).forEach((bullet) => {
  	const { path, vector } = bullet;
  	const [lastX, lastY] = path[path.length - 1];
		
    const forceVector = calcForceFieldVector(lastX, lastY, bullet);
    let newBulletVector = bullet.vector.clone().add(forceVector);
    const length = newBulletVector.length();
    if (length < options.maxStepLength) {
	    bullet.vector = newBulletVector;    

      ctx.save();
      ctx.beginPath();
      ctx.moveTo(lastX, lastY);
...