ittech/w3/nanvas.js
2022-02-14 13:22:41 +07:00

318 lines
No EOL
7.6 KiB
JavaScript

"use strict";
/**
* @author nuark (github: NuarkNoir, tg: nuark)
*/
/**
* Double PI, yeah
*/
const DOUBLE_PI = Math.PI * 2;
/**
* Base class for dynamic objects
*/
class Dynamic {
/** @type {Number} */ x;
/** @type {Number} */ y;
constructor(x, y) {
this.x = x;
this.y = y;
}
/**
* Updates some data
* @param {Nanvas} nanvas
*/
update(nanvas) {
throw new Error("[Dynamic] Not implemented yet!");
}
/**
* Draws dynamic and all
* @param {Nanvas} nanvas
*/
draw(nanvas) {
throw new Error("[Dynamic] Not implemented yet!");
}
}
/**
* Particle class
*/
class Particle extends Dynamic {
/** @type {Number} */ id;
/** @type {Number} */ x;
/** @type {Number} */ y;
/** @type {Boolean} */ alive = false;
/** @type {Number} */ aliveFor = 0.0;
}
/**
* Particle emitter class
*/
class ParticleEmmiter extends Dynamic {
/** @type {Number} */ emitterSize = 20;
/** @type {Number} */ poolSize = 10;
/** @type {Number} */ particleLifeTime = 1.0 * 1000;
/** @type {Number} */ spawnRate = 200.0;
/** @type {Number} */ timeSinceLastSpawn = 0.0;
/** @type {Array.<Particle>} */ pool = new Array(this.poolSize);
/**
* Constructs object
* @param {Number} x position on x
* @param {Number} y Position on y
* @param {Number} dx Speed over x
* @param {Number} dy Speed over y
*/
constructor(x, y, dx, dy) {
super(x, y);
this.dx = dx;
this.dy = dy;
this.lastFrameTime = Date.now();
for (let i = 0; i < this.poolSize; i++) {
this.pool[i] = new Particle(this.x, this.y);
this.pool[i].id = i;
}
}
/**
* Updates particles data
* @param {Nanvas} nanvas
*/
update(nanvas) {
const now = Date.now();
const dt = now - this.lastFrameTime;
this.lastFrameTime = now;
this.timeSinceLastSpawn += dt;
if (this.timeSinceLastSpawn >= this.spawnRate) {
this.timeSinceLastSpawn = 0.0;
for (let i = 0; i < this.poolSize; i++) {
if (!this.pool[i].alive) {
this.pool[i].x = this.x + this.emitterSize/2;
this.pool[i].y = this.y + this.emitterSize/2;
this.pool[i].alive = true;
this.pool[i].aliveFor = 0.0;
console.log(`Spawned ${this.pool[i].id}`);
break;
}
}
}
for (let i = 0; i < this.poolSize; i++) {
if (this.pool[i].alive) {
this.pool[i].x += this.dx*dt;
this.pool[i].y += this.dy*dt;
this.pool[i].aliveFor += dt;
if (this.pool[i].aliveFor >= this.particleLifeTime) {
this.pool[i].alive = false;
console.log(`Killed ${this.pool[i].id}`);
}
}
}
}
/**
* Draws emitter and particles
* @param {Nanvas} nanvas
*/
draw(nanvas) {
nanvas.square(this.x, this.y, this.emitterSize);
for (let i = 0; i < this.poolSize; i++) {
if (this.pool[i].alive) {
nanvas.circle(this.pool[i].x, this.pool[i].y, 2);
}
}
}
}
/**
* Class for working with canvas
*/
class Nanvas {
/** @type {CanvasRenderingContext2D} */
ctx = null;
/** @type {Number} */
width;
/** @type {Number} */
height;
/** @type {Array.<Array.<String, ...Number>>} */
commands = [];
/** @type {Array.<Dynamic>} */
dynamics = [];
/**
* Pushes drawing style on stack
*/
push() {
this.ctx.save();
}
/**
* Pops drawing style from stack
*/
pop() {
this.ctx.restore();
}
/**
* Fill background with colour
* @param {String} color Fill color
*/
background(color) {
this.push();
this.ctx.fillStyle = color;
this.ctx.fillRect(0, 0, this.width, this.height);
this.pop();
}
/**
* Clears canvas background
*/
clear() {
this.background("white");
}
/**
* Draws nifty grid
*/
grid() {
this.push();
this.ctx.beginPath();
for (var x = 0; x < this.width; x += 10) {
this.ctx.moveTo(x, 0);
this.ctx.lineTo(x, this.height);
}
for (var y = 0; y < this.height; y += 10) {
this.ctx.moveTo(0, y);
this.ctx.lineTo(this.width, y);
}
this.ctx.strokeStyle = "#eee";
this.ctx.stroke();
this.pop();
}
/**
* Pushes commands into canvas
* @param {Array.<Array.<String, ...Number>>} commands Commands array
*/
pushCommands(commands) {
this.commands = commands;
}
/**
* Clears canvas commands
*/
clearCommands() {
this.commands = [];
}
/**
* Draws static objects (commands)
*/
drawStatic() {
this.commands.forEach(command => {
let [cmd, ...args] = command;
if (typeof (this[cmd]) !== typeof (() => { })) {
throw new Error(`No such function: ${cmd}`);
}
this[cmd](...args);
});
}
/**
* Draws circle at postion `(x,y)` with radius `r`
* @param {Number} x Position on x
* @param {Number} y Position on y
* @param {Number} r Radius
*/
circle(x, y, r) {
this.ctx.beginPath();
this.ctx.arc(x, y, r, 0, DOUBLE_PI);
this.ctx.stroke();
}
/**
* Draws square at postion `(x,y)` with width `w`
* @param {Number} x Position on x
* @param {Number} y Position on y
* @param {Number} r Width
*/
square(x, y, w) {
this.ctx.beginPath();
this.ctx.rect(x, y, w, w);
this.ctx.stroke();
}
/**
* Draws Ellipse at postion `(x,y)` with radiais as `r1` and `r2`
* @param {Number} x Position on x
* @param {Number} y Position on y
* @param {Number} r1 Radius 1
* @param {Number} r2 Radius 2
*/
ellipse(x, y, r1, r2) {
this.ctx.beginPath();
this.ctx.ellipse(x, y, r1, r2, 0, 0, DOUBLE_PI);
this.ctx.stroke();
}
/**
* Draws bezier curve with following parameters
* @param {Number} h1x Position of first handle on x
* @param {Number} h1y Position of first handle on y
* @param {Number} h2x Position of second handle on x
* @param {Number} h2y Position of second handle on y
* @param {Number} x Position of end point on x
* @param {Number} y Position of end point on y
*/
bezier(h1x, h1y, h2x, h2y, x, y) {
this.ctx.beginPath();
this.ctx.bezierCurveTo(h1x, h1y, h2x, h2y, x, y);
this.ctx.stroke();
}
/**
* Add dynamic object to canvas
* @param {Dynamic} dynamic Dynamic object
*/
addDynamic(dynamic) {
this.dynamics.push(dynamic);
}
/**
* Updates and draws dynamics
*/
updateDynamics() {
this.dynamics.forEach(dyn => dyn.update(this));
this.dynamics.forEach(dyn => dyn.draw(this));
}
/**
* Mounts Nanvas on existing canvas element and fixes some things
* @param {HTMLElement} canvasElement
* @returns {Nanvas}
*/
static mount(canvasElement, width = null, height = null) {
const n = new Nanvas();
n.ctx = canvasElement.getContext("2d");
// fixing blurrynessssssssssssssss
n.ctx.translate(0.5, 0.5);
n.width = width === null ? canvasElement.width : width;
n.height = height === null ? canvasElement.height : height;
return n;
}
}