p5.js: Random movement of drawing tool - p5.js

I coded this drawing tool:
var a = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
background(0, 0, 255);
}
function draw() {
fill(0, 255, 255, random(255));
translate(mouseX, mouseY);
rotate(a);
textSize(120);
textAlign(CENTER, CENTER);
text('*', 0, 0);
rotate(a);
a = a + 0.08;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
Now I would like to have a movement, without doing it with the cursor. I have thought about something like this:
var a;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
a.position(x, y);
}
function setup() {
a = createCanvas(windowHeight, windowHeight);
centerCanvas();
}
function draw() {
fill(0, 255, 255, random(255));
var x =
windowHeight / 2 +
sin(frameCount * 0.01) * cos(frameCount * 0.04) * windowHeight / 3;
var y =
windowHeight / 2 +
cos(frameCount * 0.01) * sin(frameCount * 0.04) * windowHeight / 3;
rotate(a);
textSize(120);
textAlign(CENTER, CENTER);
text('*', 0, 0);
rotate(a);
a = a + 0.08;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
Unfortunately, it doesn't work. It also would be cool if the movement would be random inside the canvas. Does anyone know how to do it?

This (almost) worked for me:
let wander;
function setup() {
createCanvas(windowWidth, windowHeight);
background(0, 0, 0);
wander = new Vehicle(0, 0);
}
function draw() {
wander.wander();
wander.update();
wander.edges();
wander.show();
}
class Vehicle {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = createVector(1, 0);
this.acc = createVector(0, 0);
this.maxSpeed = 4;
this.maxForce = 0.2;
this.r = 16;
this.wanderTheta = PI / 2;
this.xoff = 0;
}
wander() {
let angle = noise(this.xoff) * TWO_PI * 2;
let steer = p5.Vector.fromAngle(angle);
steer.setMag(this.maxForce);
this.applyForce(steer);
this.xoff += 0.01;
}
applyForce(force) {
this.acc.add(force);
}
update() {
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.set(0, 0);
}
show() {
stroke(255);
strokeWeight(2);
fill(255);
push();
translate(this.pos.x, this.pos.y);
rotate(this.vel.heading());
textAlign(CENTER, CENTER);
textSize(120);
fill(0, 255, 255, random(255));
text("*", 0, 0);
pop();
}
edges() {
let hitEdge = false;
if (this.pos.x > width + this.r) {
this.pos.x = -this.r;
hitEdge = true;
} else if (this.pos.x < -this.r) {
this.pos.x = width + this.r;
hitEdge = true;
}
if (this.pos.y > height + this.r) {
this.pos.y = -this.r;
hitEdge = true;
} else if (this.pos.y < -this.r) {
this.pos.y = height + this.r;
hitEdge = true;
}
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
Using P5.js's noise function is a really accurate way to represent wandering. With a little tinkering you can get the effect you want.
EDIT
This is your (debugged) code:
let a;
let r;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
a.position(x, y);
}
function setup() {
a = createCanvas(windowHeight, windowHeight);
centerCanvas();
}
function draw() {
fill(0, 255, 255, random(255));
const x = windowHeight / 2 +
sin(frameCount * 0.01) * cos(frameCount * 0.04) * windowHeight / 3;
const y = windowHeight / 2 +
cos(frameCount * 0.01) * sin(frameCount * 0.04) * windowHeight / 3;
translate(x, y);
rotate(a * 0.08);
textSize(50);
textAlign(CENTER, CENTER);
text('*', 0, 0);
a += 1;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>

How about this?
var a = 0;
var x = 0;
var y = 0;
var delta = 1;
function setup() {
createCanvas(windowWidth, windowHeight);
background(0, 0, 255);
}
function draw() {
fill(0, 255, 255, random(255));
x = x + 2 + Math.random();
y = y + 0.5 + Math.random();
x = x * delta;
y = y * delta;
if (x < 0) delta = -1;
translate((x) % windowWidth,(y) % windowHeight);
rotate(a);
textSize(120);
textAlign(CENTER, CENTER);
text('*', 0, 0);
rotate(a);
a = a + 0.08;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>

Related

p5.js change object colour after X frames

I would like to change the fill colour of an object over time. Is there a way to change the fill colour of an object after X frames?
I am learning about constructors, and I am thinking that in the code's updateParticle function, after this.age counts to 'X', the fill colour of the ellipse could change.
'''
function Particle(x, y, xSpeed, ySpeed, size, colour) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.size = size;
this.colour = colour;
this.age = 0;
this.drawParticle = function() {
fill(this.colour);
noStroke();
ellipse(this.x, this.y, this.size);
}
this.updateParticle = function() {
this.x += this.xSpeed;
this.y += this.ySpeed;
this.age++;
}
}
function Emitter(x, y, xSpeed, ySpeed, size, colour) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.size = size;
this.colour = colour;
this.particles = [];
this.startParticles = [];
this.lifetime = 0;
this.addParticle = function() {
var p = new Particle(random(this.x - 10, this.x + 10),
random(this.y - 10, this.y + 10),
random(this.xSpeed - 0.4, this.xSpeed + 0.4),
random(this.ySpeed - 5, this.ySpeed - 1),
random(this.size - 4, this.size + 40),
this.colour);
return p;
}
this.startEmitter = function(startParticles, lifetime) {
this.startParticles = startParticles;
this.lifetime = lifetime;
for (var i = 0; i < startParticles; i++) {
this.particles.push(this.addParticle());
}
}
this.updateParticles = function() {
var deadParticles = 0
for (var i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].drawParticle();
this.particles[i].updateParticle();
if (this.particles[i].age > random(0, this.lifetime)) {
this.particles.splice(i, 1);
deadParticles++;
}
}
if (deadParticles > 0) {
for (var i = 0; i < deadParticles; i++) {
this.particles.push(this.addParticle());
}
}
}
}
var emit;
function setup() {
createCanvas(800, 600);
emit = new Emitter(width / 2, height - 100, 0, -1, 10, color(200, 0, 200, 50));
emit.startEmitter(600, 4000);
}
function draw() {
background(200);
emit.updateParticles();
}
'''
Well you could just:
if(frameCount % 30 == 0){ // % 30 is the remainder of num / 30, so 4 % 3 = 1, since 3 / 3 = 0 And 4 / 3 = 3.33
fill("lime") // these are just preset colors in p5.js AND css lime == rgb(0,255,0)
} else {
fill('darkred')
}
or you could also do it with for example a switch statement: using background for no reason
switch(MY_frameCount){
case 1:
background('blue')
break
case 2:
background("darkgreen")
break
case 376:
background(crimson)
// break
}
MY_frameCount ++
or:
if(Math.random() < 0.1){
fill(Math.random() * 255, Math.random() * 255, Math.random() * 255)
} // this should on average fill with random color every 10 frames

How can I stop canvas from adding onto the rotation?

I'm trying to get a rectangle to rotate 10 degrees counterclockwise, in a game loop. I want the box to only rotate 10 degrees. Not add another 10 degrees on the next loop, because that's what it's doing:
First iteration of Game Loop
Second Iteration of Game Loop
Here is my current draw function:
...
class Bok {
static width = 17;
static height = 12;
constructor(position) {
this.position = position;
}
draw() {
...
ctx.beginPath();
ctx.rect(this.position.x, this.position.y, 17, 12);
ctx.fillStyle = "red";
ctx.strokeStyle = "black";
ctx.lineWidth = 10;
ctx.translate(this.position.x + Bok.width / 2, this.position.y + Bok.height / 2);
// this keeps adding 10 degrees to the box every update,
// how can i rotate it 10 degrees without adding on to the
// previous rotation?
ctx.rotate(10);
ctx.translate(-(this.position.x + Bok.width / 2), -(this.position.y + Bok.height / 2));
ctx.stroke();
ctx.fill();
...
}
...
First, looking at your code it seems you got confused on how the API works.
You need to transform the context before you draw (define) the path.
Now, there are many ways to not accumulate transformations:
The long way: perform the inverse transformation.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const duration = 5000;
const cx = 150;
const cy = 80;
const start = performance.now();
function draw( time ) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const delta = ((start - time) % duration) / duration;
ctx.translate( cx, cy );
ctx.rotate( Math.PI * 2 * delta );
ctx.translate( -cx, -cy );
ctx.strokeRect( cx - 20, cy - 40, 40, 80 );
// inverse
ctx.translate( cx, cy );
ctx.rotate( Math.PI * 2 * delta * -1);
ctx.translate( -cx, -cy );
// an horizontal line
ctx.fillRect(0, 80, canvas.width, 5);
requestAnimationFrame( draw );
}
requestAnimationFrame( draw );
<canvas></canvas>
The short but slow way, save() before drawing and restore() after. But this will save and restore every properties of your canvas context, which may be an overkill.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const duration = 5000;
const cx = 150;
const cy = 80;
const start = performance.now();
function draw( time ) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const delta = ((start - time) % duration) / duration;
ctx.save();
ctx.translate( cx, cy );
ctx.rotate( Math.PI * 2 * delta );
ctx.translate( -cx, -cy );
ctx.strokeRect( cx - 20, cy - 40, 40, 80 );
ctx.restore();
// an horizontal line
ctx.fillRect(0, 80, canvas.width, 5);
requestAnimationFrame( draw );
}
requestAnimationFrame( draw );
<canvas></canvas>
The clean way: reset your context transform to an identity matrix.
This may look a bit more complicated, but when your code will grow and you'll have a lot objects to manage, having them all relative to the identity matrix will save you from a lot of troubles.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const duration = 5000;
const cx = 150;
const cy = 80;
const start = performance.now();
function draw( time ) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const delta = ((start - time) % duration) / duration;
ctx.translate( cx, cy );
ctx.rotate( Math.PI * 2 * delta );
ctx.translate( -cx, -cy );
ctx.strokeRect( cx - 20, cy - 40, 40, 80 );
// clear to identity transform
ctx.setTransform(1, 0, 0, 1, 0 ,0);
// an horizontal line
ctx.fillRect(0, 80, canvas.width, 5);
requestAnimationFrame( draw );
}
requestAnimationFrame( draw );
<canvas></canvas>
The simplest solution would be to make a trigger for it.
class Bok {
static width = 17;
static height = 12;
constructor(position) {
this.position = position;
this.rotated = false;
}
draw() {
...
ctx.beginPath();
ctx.rect(this.position.x, this.position.y, 17, 12);
ctx.fillStyle = "red";
ctx.strokeStyle = "black";
ctx.lineWidth = 10;
ctx.translate(this.position.x + Bok.width / 2, this.position.y + Bok.height / 2);
if (!this.rotated) {
ctx.rotate(10);
this.rotated = true;
}
ctx.translate(-(this.position.x + Bok.width / 2), -(this.position.y + Bok.height / 2));
ctx.stroke();
ctx.fill();
...
}
...
adding updated code to rotate based on velocity
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = 500;
let friction = 0.8;
let gravity = 1.5;
class Player {
constructor() {
this.x = 128;
this.y = 260;
this.w = 32;
this.h = 32;
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.color = "green";
this.a = 0;
this.r = this.a * (Math.PI/180);
}
draw() {
ctx.save();
ctx.fillStyle = this.color;
ctx.translate(this.x+this.w/2, this.y+this.h/2)
ctx.rotate(-this.r);
ctx.fillRect(0, 0, this.w, this.h);
ctx.restore();
}
update() {
if (controller.space) {
this.vy < 10 ? this.vy -= this.speed : this.vy = 10;
this.a < 10 ? this.a += 0.5 : this.a = 10;
} else {
this.a > 0 ? this.a -= 0.5 : this.a = 0;
}
this.r = this.a * (Math.PI/180);
this.vy += gravity;
this.y += this.vy;
this.vy *= friction;
this.draw()
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {
this.y = canvas.height - this.h;
this.vy = 0;
}
}
}
let player = new Player();
function handlePlayer() {
player.draw();
player.update();
}
class Controller {
constructor() {
this.space = false;
let keyPress = (e) => {
if (e.code === "Space") this.space = e.type === "keydown";
};
window.addEventListener("keydown", keyPress);
window.addEventListener("keyup", keyPress);
}
}
let controller = new Controller();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = 'bold 48px serif';
ctx.fillText("Spacebar", 100, 50);
player.update();
player.canvasCollision();
requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>

P5.js Creating a fading curve on a grid

I've made a square grid on top of the canvas and also a cruve (that is meant to have a fading trail). I made them seperately and tried combining them so the curve would appear on top of the grid. However, it doesn't show the curve.
I've commented out the grid so it's easier to see the curve.
How do I get this to work?
var cols = 10;
var rows = 10;
var t = 0;
var particleArray = [];
function setup() {
createCanvas(600, 600);
background(0);
fill(100);
rect(0, 0, 550, 550, 25);
}
// blue grid
function draw() {
/*for (var c = 0; c < cols; c++) {
for (var r = 0; r < rows; r++) {
var XO = 25 + c * 50;
var YO = 25 + r * 50;
stroke(0);
fill(100,149,237);
rect(XO, YO, 50, 50);
noLoop();
// :(
}
}
*/
//curve
y = width / 2 + 270 * sin(3 * t + PI / 2) - 25;
x = height / 2 + 270 * sin(1 * t) - 25;
particleArray.push(new Particle(x, y, t));
for (i=0; i<particleArray.length; i++) {
particleArray[i].show(t);
}
if (particleArray.length > 700) {
particleArray.shift();
}
t += .01;
}
function Particle(x, y, t) {
this.x = x;
this.y = y;
this.t = t;
this.show = function(currentT) {
var _ratio = t / currentT;
_alpha = map(_ratio, 0, 1, 0, 255); //points will fade out as time elaps
fill(255, 255, 255, _alpha);
ellipse(x, y, 5, 5);
}
}
I don't know if this was intentional but you called noLoop() function where you're drawing the grid. If you comment that out it works.

Setting background image in canvas animation

I need to set background image for this canvas animation without affecting the animation style.
This CodePen is shown below.
var c = document.getElementById('canv');
var $ = c.getContext('2d');
var w = c.width = window.innerWidth;
var h = c.height = window.innerHeight;
var grav = 0.00095;
var s = [20, 15, 10, 5];
var gravX = w / 2;
var gravY = h / 2;
var nodes;
var num = 55;
var minDist = 155;
var spr = 0.0000009;
part();
run();
//random size function
function S() {
var curr = s.length;
var cur_ = Math.floor(Math.random() * curr);
return s[cur_];
}
function part() {
nodes = [];
for (var i = 0; i < num; i++) {
var node = {
hue: Math.random()*360,
rad: S(),
x: Math.random() * w,
y: Math.random() * h,
vx: Math.random() * 8 - 4,
vy: Math.random() * 8 - 4,
upd: function() {
this.x += this.vx;
this.y += this.vy;
if (this.x > w) this.x = 0;
else if (this.x < 0) this.x = w;
if (this.y > h) this.y = 0;
else if (this.y < 0) this.y = h;
},
draw: function() {
//outer ring
var g = $.createRadialGradient(this.x, this.y, this.rad * 2, this.x, this.y, this.rad);
g.addColorStop(0,'hsla(242, 55%, 15%,.7)');
g.addColorStop(.5, 'hsla(242, 50%, 10%,.5)');
g.addColorStop(1,'hsla(242, 30%, 5%,.5)');
$.fillStyle = g;
$.beginPath();
$.arc(this.x, this.y, this.rad * 2, 0, Math.PI * 2, true);
$.fill();
$.closePath();
//inner particle
var g2 = $.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.rad);
g2.addColorStop(0, 'hsla('+this.hue+', 85%, 40%, 1)');
g2.addColorStop(.5, 'hsla('+this.hue+',95%, 50%,1)');
g2.addColorStop(1,'hsla(0,0%,0%,0)');
$.fillStyle = g2;
$.beginPath();
$.arc(this.x, this.y, this.rad, 0, Math.PI * 2, true);
$.fill();
$.closePath();
}
};
nodes.push(node);
}
}
function run() {
$.globalCompositeOperation = 'source-over';
$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.fillRect(0, 0, w, h);
$.globalCompositeOperation = 'lighter';
for (i = 0; i < num; i++) {
nodes[i].upd();
nodes[i].draw();
}
for (i = 0; i < num - 1; i++) {
var n1 = nodes[i];
for (var j = i + 1; j < num; j++) {
var n2 = nodes[j];
Spr(n1, n2);
}
Grav(n1);
}
window.requestAnimationFrame(run);
}
function Spr(na, nb) {
var dx = nb.x - na.x;
var dy = nb.y - na.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist) {
$.lineWidth = 1;
$.beginPath();
$.strokeStyle = "hsla(217, 95%, 55%, .15)";
$.moveTo(na.x, na.y);
$.lineTo(nb.x, nb.y);
$.stroke();
$.closePath();
var ax = dx * spr;
var ay = dy * spr;
na.vx += ax;
na.vy += ay;
nb.vx -= ax;
nb.vy -= ay;
}
}
function Grav(n) {
n.vx += (gravX - n.x) * grav;
n.vy += (gravY - n.y) * grav;
};
window.addEventListener('resize', function() {
c.width = w = window.innerWidth;
c.height = h = window.innerHeight;
});
body{
width:100%;
margin:0;
overflow:hidden;
}
<canvas id='canv' ></canvas>
CSS
Just replace the beginning of the run() code to:
function run() {
...
//$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.clearRect(0, 0, w, h);
...
Then move the color settings to CSS together with an image reference:
#canv {
background: hsla(242, 40%, 5%, .85) url(path/to/image.jpg);
}
Add background-size to the CSS rule if needed. Note that since you're using different blending modes such as lighter which depends on existing content, you may not get desired result as it will blend with an empty canvas and not a solid - the approach below should solve that in this case.
CodePen
JavaScript
As before, replace the first lines in run() but after you made sure the image you want to use has loaded, simply draw it in:
function run() {
...
//$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.drawImage(img, 0, 0, w, h); // img must be loaded (use onload)
...
If your image contains transparency you also need to clear the canvas first:
function run() {
...
//$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.clearRect(0, 0, w, h);
$.drawImage(img, 0, 0, w, h); // img must be loaded (use onload)
...
CodePen

Changing shapes using addEventListener in HTML5

I'm trying to change the shape of the particles in this script so that every time you click, the shapes change to a random shape. The shapes I want to do are circles (which they already are), squares, triangles, pentagons, and a four-leaf clover shape without the stem. I want to use the addEventListener method, but i have no idea where to even start with that. Thanks in advance, and here's the code I have so far:
http://jsfiddle.net/eampkcrr/
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = window.innerWidth;
var height = window.innerHeight;
var xCirc;
var yCirc;
var rCirc;
var animate = true;
canvas.width = width;
canvas.height = height;
makeParticles();
makeShapes();
function makeParticles() {
xCenter = canvas.width/2;
yCenter = canvas.height/2;
particles = [];
for (var i = 0; i < 3000; i++){
particles.push(new Particle());
}
}
function makeShapes() {
xCenter = canvas.width/2;
yCenter = canvas.height/2;
shapes = [];
shapes.push(new Circle());
}
function Circle() {
var r1 = 150;
var r2 = 1000;
var gradient1 = context.createRadialGradient(width/2, height/2, r1, width/2, height/2, r2);
gradient1.addColorStop(0.2, "yellow");
gradient1.addColorStop(0.8, "purple");
context.fillStyle = gradient1;
context.fillRect(0, 0, canvas.width, canvas.height);
var gradient2 = context.createRadialGradient(width/2, height/2, 120, width/2, height/2, 150);
gradient2.addColorStop(0, "black");
gradient2.addColorStop(.75, "black");
gradient2.addColorStop(1, "orange");
context.beginPath();
context.arc(width/2, height/2, 150, 0, 2 * Math.PI, true);
context.fillStyle = gradient2;
context.fill();
}
function start() {
if(animate){
window.requestAnimationFrame(start);
}
draw();
moveParticles();
}
function Particle() {
this.x = Math.floor((Math.random() * canvas.width) + 1);
this.y = Math.floor((Math.random() * canvas.height) + 1);
this.z = Math.floor((Math.random() * canvas.width));
var grad = context.createRadialGradient(this.x, this.y, Math.floor((Math.random() * 10) + 1), this.x, this.y, Math.floor((Math.random() * 10) + 1));
var colors = ["red", "green", "blue", "orange", "purple", "yellow", "white"];
grad.addColorStop(0, colors[Math.floor(Math.random()*colors.length)]);
grad.addColorStop(1, colors[Math.floor(Math.random()*colors.length)]);
this.color = grad;
this.radius = 1;
}
function draw() {
Circle();
for (var i = 0; i < particles.length; i++){
var p = particles[i];
xP = (xCenter - p.x) * (canvas.width/p.z);
xP += xCenter;
yP = (yCenter - p.y) * (canvas.width/p.z);
yP += yCenter;
rP = (canvas.width/p.z);
context.beginPath();
context.arc(xP, yP, rP, 0, 2 * Math.PI, true);
context.fillStyle = p.color;
context.fill();
xCirc -= p.x;
yCirc -= p.y;
}
}
function moveParticles() {
for (var j = 0; j < particles.length; j++){
var p = particles[j];
p.z -= 2;
if (p.z <= 0){
p.z = canvas.width;
}
}
}
start();
You can listen for click events on the canvas like this:
canvas.onclick=function(){ ... }
To change the particle shape, you can add a draw method to Particle that draws the appropriate shape based on a particle's this.particleType.
function Particle(particleType) {
this.particleType=particleType;
this.x = Math.floor((Math.random() * canvas.width) + 1);
this.y = Math.floor((Math.random() * canvas.height) + 1);
this.z = Math.floor((Math.random() * canvas.width));
var grad = context.createRadialGradient(this.x, this.y, Math.floor((Math.random() * 10) + 1), this.x, this.y, Math.floor((Math.random() * 10) + 1));
var colors = ["red", "green", "blue", "orange", "purple", "yellow", "white"];
grad.addColorStop(0, colors[Math.floor(Math.random()*colors.length)]);
grad.addColorStop(1, colors[Math.floor(Math.random()*colors.length)]);
this.color = grad;
this.radius = 1;
this.draw=function(){
// update position
var xP = (xCenter - this.x) * (canvas.width/this.z);
xP += xCenter;
var yP = (yCenter - this.y) * (canvas.width/this.z);
yP += yCenter;
var rP = (canvas.width/this.z);
// set fillStyle
context.fillStyle = this.color;
// draw on context based on the particle's current shape
switch (this.particleType){
case 'circle':
context.beginPath();
context.arc(xP, yP, rP, 0, 2 * Math.PI, true);
context.fill();
break;
case 'square':
context.fillRect(xP-rP, yP-rP, rP*2, rP*2);
break;
}
// update
xCirc -= this.x;
yCirc -= this.y;
}
}
Then your external function draw simply requests each particle to draw itself:
function draw() {
Circle();
for (var i = 0; i < particles.length; i++){
// request each particle to draw itself
var p = particles[i].draw();
}
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = window.innerWidth;
var height = window.innerHeight;
var xCirc;
var yCirc;
var rCirc;
var xCenter,yCenter;
var animate = true;
var pTypes=['circle','square'];
var pTypeIndex=0;
canvas.width = width;
canvas.height = height;
makeParticles();
makeShapes();
canvas.onclick=function(){
pTypeIndex++;
if(pTypeIndex>pTypes.length-1){pTypeIndex=0;}
var pType=pTypes[pTypeIndex];
for(var i=0;i<particles.length;i++){
particles[i].particleType=pType;
}
};
start();
function makeParticles() {
xCenter = canvas.width/2;
yCenter = canvas.height/2;
particles = [];
for (var i = 0; i < 500; i++){
particles.push(new Particle(pTypes[pTypeIndex]));
}
}
function makeShapes() {
xCenter = canvas.width/2;
yCenter = canvas.height/2;
shapes = [];
shapes.push(new Circle());
}
function Circle() {
var r1 = 150;
var r2 = 1000;
var gradient1 = context.createRadialGradient(width/2, height/2, r1, width/2, height/2, r2);
gradient1.addColorStop(0.2, "yellow");
gradient1.addColorStop(0.8, "purple");
context.fillStyle = gradient1;
context.fillRect(0, 0, canvas.width, canvas.height);
var gradient2 = context.createRadialGradient(width/2, height/2, 120, width/2, height/2, 150);
gradient2.addColorStop(0, "black");
gradient2.addColorStop(.75, "black");
gradient2.addColorStop(1, "orange");
context.beginPath();
context.arc(width/2, height/2, 150, 0, 2 * Math.PI, true);
context.fillStyle = gradient2;
context.fill();
}
function start() {
if(animate){
window.requestAnimationFrame(start);
}
draw();
moveParticles();
}
function Particle(particleType) {
this.particleType=particleType;
this.x = Math.floor((Math.random() * canvas.width) + 1);
this.y = Math.floor((Math.random() * canvas.height) + 1);
this.z = Math.floor((Math.random() * canvas.width));
var grad = context.createRadialGradient(this.x, this.y, Math.floor((Math.random() * 10) + 1), this.x, this.y, Math.floor((Math.random() * 10) + 1));
var colors = ["red", "green", "blue", "orange", "purple", "yellow", "white"];
grad.addColorStop(0, colors[Math.floor(Math.random()*colors.length)]);
grad.addColorStop(1, colors[Math.floor(Math.random()*colors.length)]);
this.color = grad;
this.radius = 1;
this.draw=function(){
// update position
var xP = (xCenter - this.x) * (canvas.width/this.z);
xP += xCenter;
var yP = (yCenter - this.y) * (canvas.width/this.z);
yP += yCenter;
var rP = (canvas.width/this.z);
// set fillStyle
context.fillStyle = this.color;
// draw on context
switch (this.particleType){
case 'circle':
context.beginPath();
context.arc(xP, yP, rP, 0, 2 * Math.PI, true);
context.fill();
break;
case 'square':
context.fillRect(xP-rP, yP-rP, rP*2, rP*2);
break;
}
// update
xCirc -= this.x;
yCirc -= this.y;
}
}
function draw() {
Circle();
for (var i = 0; i < particles.length; i++){
var p = particles[i].draw();
}
}
function moveParticles() {
for (var j = 0; j < particles.length; j++){
var p = particles[j];
p.z -= 2;
if (p.z <= 0){
p.z = canvas.width;
}
}
}
<h4>Click to change shapes.</h4>
<canvas id="canvas" width=300 height=300></canvas>
[ Fixed "yip" in circle <--> square conversion -- Thanks #Kaiido ]

Resources