JSFiddle - React, Tailwind, and code Playground

by Li Sen

HTML

<canvas id="canvas"></canvas>

Babel + JSX

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const W = canvas.width = window.innerWidth;
const H = canvas.height = window.innerHeight;
const balls = [];
const ballsNum = 200;
const floor = 100;
const fl = 200;
const g = 0.2;
const bounce = 0.5;
const vpX = W / 2;
const vpY = H / 2;

class Ball {
  constructor(radius=10, color) {
    this.x = 0;
    this.y = 0;
    this.xp = 0;
    this.yp = 0;
    this.zp = 0;
    this.vx = 0;
    this.vy = 0;
    this.vz = 0;
    this.r = radius;
    this.color = color;
    this.scaleX = 1;
    this.scaleY = 1;
    this.visible = true;
  }

  draw(ctx) {
    ctx.save();
    ctx.translate(this.x, this.y);
    ctx.scale(this.scaleX, this.scaleY);
    ctx.fillStyle = this.color;
    ctx.beginPath();
    ctx.arc(0, 0, this.r, 0, (Math.PI / 180) * 360, false);
    ctx.closePath();
    ctx.fill();
    ctx.restore();
  }
}

function drawScreen() {
	balls.forEach(move);
  balls.forEach(draw);
}

function move(ball) {
	ball.vy += g;
  ball.xp += ball.vx;
  ball.yp += ball.vy;
  ball.zp += ball.vz;
  if (ball.yp > floor) {
  	ball.yp = floor;
    ball.vy *= -bounce;
  }
  if (ball.zp > -fl) {
  	const scale = fl / (fl + ball.zp);
    ball.scaleX = ball.scaleY = scale;
    ball.x = vpX + ball.xp * scale;
    ball.y = vpY + ball.yp * scale;
    ball.visible = true;
  } else {
  	ball.visible = false;
  }
  
}

function draw(ball) {
	ball.visible && ball.draw(ctx);
}

for (let i = 0; i < ballsNum; i++) {
	const ball = new Ball(5, '#000');
  ball.vx = Math.random() * 10 - 5;
  ball.vy = Math.random() * 10 - 5;
  ball.vz = Math.random() * 10 - 5;
  balls.push(ball);
}
!function drawFrame() {
	window.requestAnimationFrame(drawFrame);
  ctx.clearRect(0, 0, W, H);
  drawScreen();
}();