JSFiddle - React, Tailwind, and code Playground
by Li Sen
HTML
<canvas id="canvas"></canvas>
CSS
* {
padding: 0;
margin: 0;
}
JavaScript
class Ball {
constructor(radius = 40, 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();
}
}
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;
function drawScreen() {
drawPlane(W / 4, floor);
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) {
let 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);
}
function drawPlane(w=0, h=0) {
const points = [
{ x: -w / 2, y: h, z: h / 4 },
{ x: w / 2, y: h, z: h / 4 },
{ x: w / 2, y: h, z: - h / 4 },
{ x: -w / 2, y: h, z: - h / 4 },
];
const len = points.length - 1;
let pt1, pt2;
for (let i = 0; i < len; i++) {
pt1 = transform(points[i]);
pt2 = transform(points[i + 1]);
drawLine(pt1, pt2);
}
pt1 = pt2;
pt2 = transform(points[0]);
drawLine(pt1, pt2);
}
function drawLine(pt1, pt2) {
ctx.strokeStyle = '#fff';
...