Skip to content

Instantly share code, notes, and snippets.

@nilkun
Created April 4, 2019 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nilkun/f65b5f78b28f0b95c05bc235610c6b09 to your computer and use it in GitHub Desktop.
Save nilkun/f65b5f78b28f0b95c05bc235610c6b09 to your computer and use it in GitHub Desktop.
Explosions and fallout
<div>
<canvas id="canvas" width=800 height=600></canvas>
</div>
<div id="buttons">
<button id="explosion">Explosion</button>
<button id="fallout">Nuclear Fallout</button>
<button id="spin">Galaxy formation</button>
<button id="amoeba">Amoeba</button>
<div id="colors">
<div class="red box"></div>
<div class="green box"></div>
<div class="blue box"></div>
</div>
<button>Number of particles:<br><i id="particles">0</i></button>
<input type="range" min="1" max="10000" value="2000" id="slider">
</div>
class PixelManipulation {
constructor(canvas) {
this.context = canvas.getContext("2d");
this.width = canvas.width;
this.height = canvas.height;
this.image = this.context.getImageData(0, 0, this.width, this.height);
}
getImage() {
this.image = this.context.getImageData(0, 0, this.width, this.height);
}
setImage() {
this.context.putImageData(this.image, 0, 0);
}
setPixel(x, y, red, green, blue) {
const pixelIndex = (y * this.width + x) * 4;
this.image.data[pixelIndex] = red;
this.image.data[pixelIndex + 1] = green;
this.image.data[pixelIndex + 2] = blue;
}
fillColor(red, green, blue, alpha = 255) {
for (let i = 0; i < this.width * this.height * 4; i+=4) {
this.image.data[i] = red;
this.image.data[i + 1] = green;
this.image.data[i + 2] = blue;
this.image.data[i + 3] = alpha;
}
}
}
class Particle {
constructor() {
this.x;
this.y;
this.direction;
this.speed;
this.init;
this.update;
}
}
class Swarm {
constructor(canvas, amount = 10000, color = 1) {
this.size = amount;
this.particles = [];
this.pixels = new PixelManipulation(canvas);
this.color = color;
this.style = this.initSpin;
this.update = this.updateSpin;
}
setPattern(pattern) {
switch(pattern) {
case "fallout": {
this.style = this.initFallout;
this.update = this.updateFallout;
break;
}
case "spin": {
this.style = this.initSpin;
this.update = this.updateSpin;
break;
}
case "amoeba": {
this.style = this.initAmoeba;
this.update = this.updateAmoeba;
break;
}
case "explosion": {
this.style = this.initExplosion;
this.update = this.updateExplosion;
break;
}
}
this.init();
};
setColor(color) {
this.color = color;
};
setParticles(value) {
this.size = value;
this.init();
}
init() {
; // 0 = red, 1 = green, 2 = blue
this.particles = [];
for(let i = 0; i < this.size; i++) {
this.particles.push(new Particle);
// this.particles[i].update = this.particles[i].updateSpin;
this.particles[i].update = this.update;
this.particles[i].init = this.style;
this.particles[i].init();
}
this.pixels.fillColor(0, 0, 0);
this.pixels.setImage();
}
initFallout() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
// this.update = this.updateFallout;
}
updateFallout(delta) {
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
this.x = -1.10;
this.y = -1.10
}
}
initExplosion() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateExplosion;
}
updateExplosion(delta) {
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
initSpin() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateSpin;
}
updateSpin(delta) {
this.direction += 0.01;
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
initAmoeba() {
this.direction = 2 * Math.PI * Math.random();
this.speed = 0.02 * Math.random();
this.x = 0;
this.y = 0;
this.speed *= this.speed/2;
// this.update = this.updateAmoeba;
}
updateAmoeba(delta) {
this.direction += Math.random();;
const xspeed = this.speed * Math.cos(this.direction);
const yspeed = this.speed * Math.sin(this.direction);
this.x += xspeed * delta;
this.y += yspeed * delta;
if(this.x <= -1.0 || this.x >= 1.0 || this.y <= -1.0 || this.y >= 1.0) {
// this.init();
this.x = -1.10;
this.y = -1.10
}
}
render(delta) {
const width = 800;
const height = 600;
const oldPixels = this.pixels.context.getImageData(0, 0, width, height).data;
for(let y=0; y<height; y++) {
for(let x = 0; x<width; x++) {
let redTotal = 0;
for(let row = -1; row <=1; row++) {
for(let col = -1; col <=1; col++) {
let currentX = x + col;
let currentY = y + row;
if(currentX >= 0 && currentX < width && currentY >= 0 && currentY < height) {
let red = oldPixels[4*(currentY * width + currentX) + this.color]
redTotal += red;
}
}
}
this.pixels.image.data[4*(y * width + x) + this.color] = redTotal / 9;
}
}
for(let i = 0; i < this.size; i++) {
const p = this.particles[i];
p.update(delta);
const xPos = Math.floor((1 + p.x) * width/2);
const yPos = Math.floor(p.y * width/2 + height/2);
this.pixels.image.data[4 * (yPos * width + xPos) + this.color] = 255;
}
this.pixels.setImage();
}
}
const fallout = document.querySelector("#fallout");
const explosion = document.querySelector("#explosion");
const spin = document.querySelector("#spin");
const amoeba = document.querySelector("#amoeba");
const red = document.querySelector(".red");
const green = document.querySelector(".green");
const blue = document.querySelector(".blue");
const particles = document.querySelector("#particles");
const slider = document.querySelector("#slider");
fallout.addEventListener("click", () => swarm.setPattern("fallout"));
explosion.addEventListener("click", () => swarm.setPattern("explosion"));
amoeba.addEventListener("click", () => swarm.setPattern("amoeba"));
spin.addEventListener("click", () => swarm.setPattern("spin"));
slider.addEventListener("input", () => {
particles.innerHTML = slider.value;
});
slider.addEventListener("mouseup", () => {
swarm.setParticles(slider.value);
});
red.addEventListener("click", () => swarm.setColor(0));
green.addEventListener("click", () => swarm.setColor(1));
blue.addEventListener("click", () => swarm.setColor(2));
// start
slider.value = 2000;
particles.innerHTML = slider.value;
let currentTime = Date.now();
let previousTime = Date.now();
let deltaTime = 0;
const swarm = new Swarm(canvas, slider.value);
swarm.init();
setInterval(() => {
currentTime = Date.now();
swarm.render(deltaTime);
deltaTime = currentTime - previousTime;
previousTime = currentTime;
}, 1000/48);
body {
display: flex;
flex-direction: row;
}
#buttons {
margin: 10px;
/* padding: 10px; */
display: flex;
flex-direction: column;
}
#colors {
margin: 10px;
/* padding: 10px; */
display: flex;
flex-direction: row;
justify-content: space-evenly;
/* background: grey; */
}
.box {
width: 20px;
height: 20px;
}
.red {
background: red;
}
.green {
background: green;
}
.blue {
background: blue;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment