Related
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
I am making this random little sketch where you can click and drop coins and there is a count of how many coins you have in the top left.
The problem I am running into is, whenever you run this you will notice that the amount label just continuously stacks on top of itself whenever it updates. I have read other posts that say that we need to redraw the background to clear the text but when I do this, it also removes any coins that have been generated on the canvas. How can I update this to keep the coins visible but remove and redraw the amount?
var moneyCount = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
background(100);
}
function draw() {
if (mouseIsPressed){
dropCoins();
displayCount();
}
}
function displayCount() {
textSize(80);
text('$' + moneyCount, 80, 80);
}
function dropCoins() {
var maxSize = 40;
var xLoc = mouseX;
var yLoc = mouseY;
makeStacks(xLoc, yLoc, maxSize);
}
function makeStacks(x, y, size){
fill(255,215,0);
ellipse(x, y, size);
for (i = 0; i < size; i++){
let r1 = random(100);
let r2 = random(100);
if (r1 < 50){
x = x + 2
} else {
x = x - 2;
}
if (r2 < 50){
y = y + 2;
} else {
y = y - 2;
}
moneyCount++;
ellipse(x, y, size);
}
}
You have a couple of options available:
You store where the coins are so you can redraw them after you clear the background
You use multiple "layers" using createGraphics() so you can clear the background, but not the coins PGraphics
For option 1 you can do something like this:
var moneyCount = 0;
var coins = [];
function setup() {
createCanvas(windowWidth, windowHeight);
textSize(80);
}
function draw() {
background(100);
if (mouseIsPressed){
dropCoins();
}
displayCoins();
displayCount();
}
function displayCount() {
text('$' + moneyCount, 80, 80);
}
function dropCoins() {
var maxSize = 40;
var xLoc = mouseX;
var yLoc = mouseY;
makeStacks(xLoc, yLoc, maxSize);
}
function makeStacks(x, y, size){
for (i = 0; i < size; i++){
let r1 = random(100);
let r2 = random(100);
if (r1 < 50){
x = x + 2
} else {
x = x - 2;
}
if (r2 < 50){
y = y + 2;
} else {
y = y - 2;
}
moneyCount++;
coins.push({x:x,y:y,size:size});
}
}
function displayCoins(){
fill(255,215,0);
for(var i = 0 ; i < coins.length; i++){
ellipse(coins[i].x,coins[i].y,coins[i].size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
The idea is you would initialise an array to store the data you would need to redraw the coins (x,y, and size (if it varies)).
Bare in mind the more coins you'd add, the more memory you'd use.
If you simply need the rendered image and don't need the coin position data, option 2 will be more efficient.
For option 2 the main idea, as you can see in the documentation, is that you can have another graphics layer to draw into. Once initialised simply use dot notion on the instance and use the typical p5 drawing calls on it. To render it use image()
Here's a demo for the PGraphics option:
var moneyCount = 0;
var coinsLayer;
function setup() {
createCanvas(windowWidth, windowHeight);
textSize(80);
coinsLayer = createGraphics(windowWidth, windowHeight);
}
function draw() {
background(100);
// render coins layer
image(coinsLayer,0,0);
if (mouseIsPressed){
dropCoins(coinsLayer);
}
displayCount();
}
function displayCount() {
text('$' + moneyCount, 80, 80);
}
function dropCoins(coinsLayer) {
var maxSize = 40;
var xLoc = mouseX;
var yLoc = mouseY;
makeStacks(coinsLayer, xLoc, yLoc, maxSize);
}
function makeStacks(layer, x, y, size){
layer.fill(255,215,0);
layer.ellipse(x, y, size);
for (i = 0; i < size; i++){
let r1 = random(100);
let r2 = random(100);
if (r1 < 50){
x = x + 2
} else {
x = x - 2;
}
if (r2 < 50){
y = y + 2;
} else {
y = y - 2;
}
moneyCount++;
layer.ellipse(x, y, size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
I would like to do the following in p5.js. Say I have a canvas roughly like this:
Let's say I have given those red squares some interactivity using the function mouseClicked() and their respective coordinates in the canvas (as in, if I click on a square, change its color).
Now I'd like to use that blue "i"-button to display some sort of info box, and it should look approximately like this:
I want that "info dialog" to go away if the user clicks on that "OK-button" (whcih is not really a button, but also just a square in a p5 canvas).
Question: Is there an elegant way of deactivating the "square interactivity" and activating that "OK button interactivity" and to have the interactivity the other way around whenever the info box is not being displayed?
The only way I can think of to achieve this goes like this:
function mouseClicked(){
if(infoBoxIsBeingDisplayed){
do something
}else{
do something else
}
}
However, this seems a little convoluted.
I'd appreciate any suggestions on how to do this better.
Your solution seems fine, and it also seems much less "convoluted" than any of the other options. Keep it simple.
You might be able to clean up your code by splitting up your logic into smaller utility functions. Something like this:
function mouseClicked(){
if(infoBoxIsBeingDisplayed){
mouseClickedInfoBox();
}else{
mouseClickedSquaresCanvas();
}
}
Then your logic would be in those utility functions, specific to each screen. You could further split it up to generalize your bounds-checking, but the idea is the same.
I've run into a similar problem to this before, the best way I came up with to solve this (which is a fairly patchy way to do it) is to move the squares outside of the canvas. sorry my code is probably really messy but look at the function game and you will see what i did
If you run the game make it full screen.
example:(to play the game use A and D or use the arrow keys but i suggest A and D because the way the code snippet is set up the game doesn't fit on the page and the page moves around when you press the arrow keys)
var bs = [];
var speed;
var ship1;
var num = 40;
var d;
var gscore = 0;
var highscore = 0;
var cap = 0;
function setup() {
createCanvas(windowWidth,windowHeight- 4);
for(var i = 0; i < num; i++) {
bs[i] = new Box(random(0, width), random(-600,-30));
}
ship1 = new ship();
button1 = new button();
}
function draw() {
background(0);
if(cap == 0){
gscore = gscore + 0.1;
}
if(highscore < gscore){
highscore = gscore;
}
speed = map(gscore,4,100,4,5);
ship1.show();
ship1.update();
for(var i = 0; i < num; i++) {
bs[i].update();
bs[i].show();
if(bs[i].y >= height){
bs[i].x = random(0, width);
bs[i].y = random(-600,-30);
}
for(var j = 0; j < num; j++) {
if(bs[i].touch(bs[j])){
if(bs[i] != bs[j]){
bs[j].x = random(0, width);
bs[j].y = random(-600,-30);
}
}
}
if(bs[i].touch(ship1)){
game();
}
}
push();
fill(255,0,0);
textSize(36);
text("score: "+ floor(gscore),0,36);
text("highscore: "+floor(highscore),0,72);
pop();
}
function Box(x, y) {
this.x = x;
this.y = y;
this.show = function() {
fill(255);
noStroke();
rect(this.x, this.y, 30, 30);
}
this.update = function() {
this.y = this.y + speed;
}
this.touch = function(other){
d = dist(this.x, this.y, other.x, other.y);
if(d < 15/*half of the squares*/+15/*the total area of the ship*/){
return true;
}else {
return false;
}
}
}
function game(){//look here, game is the end game screen
for(var i = 0; i < num; i++) {
bs[i].x = -200;//making all squares x value -200
bs[i].y = -200;//making all squares y value -200
}
ship1.x = -200;//making ship x value -200
ship1.y = -200;//making ship y value -200
cap = 1;//cap is a variable made to stop the score from increasing when the end game screen is shown its "capping" the score
push();
fill(255,0,0);
textAlign(CENTER);
textSize(64);
text("You lose", width/2, height/2);
fill(255);
text("Try again?", width/2,height/2+64);
button1.touch();//touch checks if the mouse is over the button(the button sucks ass btw the hitbox for it is a square not a rectangle)
button1.show();//showing the button
button1.update();//updates the text and button color when highlighted
fill(texthover);
textSize(48);
text("Yes",width/2, height/2+145);
pop();
}
function button(){
this.x = width/2;
this.y = height/2+128;
this.d;
this.update = function() {
this.x = width/2;
this.y = height/2+128;
}
this.show = function(){
push();
rectMode(CENTER);
fill(hover);
rect(this.x, this.y, 128, 64, 50);
pop();
}
this.touch = function(){
this.d = dist(this.x, this.y, mouseX, mouseY);
if(this.d <32){
hover = 51;
texthover = 255;
if(mouseIsPressed){
for(var i = 0; i < num; i++) {
bs[i].x = random(0, width);
bs[i].y = random(-600,-30);
}
ship1.x = width/2;
ship1.y = 450;
gscore = 0;
cap = 0;
}
}else {
hover = 200;
texthover = 0;
}
}
}
function ship() {
this.x = width/2;
this.y = 450;
this.update = function() {
if(keyIsDown(LEFT_ARROW) || keyIsDown(65)) {
if(this.x>14){
this.x = this.x - map(gscore,2,100,2,3);
}
}
if(keyIsDown(RIGHT_ARROW) || keyIsDown(68)) {
if(this.x<width- 15){
this.x = this.x + map(gscore,2,100,2,3);
}
}
}
this.show = function() {
push();
rectMode(CENTER);
fill(200,200,0);
rect(this.x+15, this.y+5, 5, 30);
fill(150,100,200);
rect(this.x+15, this.y + 15,30, 15)
pop();
}
}
function windowResized() {
createCanvas(windowWidth,windowHeight- 4);
button1.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/addons/p5.dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/p5.js"></script>
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
So I'm trying to have the down arrow make the background of my canvas change. I'm having trouble just getting the button press to work itself.
Also I was told that I would have to have a function redraw all the shapes that are already there at the beginning as well, which I am also stuck on what to change.
Here is a JSFiddle of what I have going so far, any suggestions are appreciated!
https://jsfiddle.net/u8avnky2/
var mainCanvas = document.getElementById("canvas");
var mainContext = mainCanvas.getContext('2d');
//rotate canvas
function buttonClick() {
mainContext.rotate(20*Math.PI/180);
}
//key down event
window.addEventListener('keydown', function(event) {
if (event.keyCode === 40) {
fillBackgroundColor();
}
});
function fillBackgroundColor() {
var colors = ["red", "green", "blue", "orange", "purple", "yellow"];
var color = colors[Math.floor(Math.random()*colors.length)];
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
mainContext.fillStyle = color;
mainContext.fillRect(0, 0, canvas.width, canvas.height);
}
function check() {
mainContext.clearRect(square.x,square.y,square.w,square.h);
}
var circles = new Array();
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function Circle(radius, speed, width, xPos, yPos) {
this.radius = radius;
this.speed = speed;
this.width = width;
this.xPos = xPos;
this.yPos = yPos;
this.opacity = .1 + Math.random() * .5;
this.counter = 0;
var signHelper = Math.floor(Math.random() * 2);
if (signHelper == 1) {
this.sign = -1;
} else {
this.sign = 1;
}
}
//drawing circle
Circle.prototype.update = function () {
this.counter += this.sign * this.speed;
mainContext.beginPath();
mainContext.arc(this.xPos + Math.cos(this.counter / 100) * this.radius,
this.yPos + Math.sin(this.counter / 100) * this.radius,
this.width,
0,
Math.PI * 2,
false);
mainContext.closePath();
mainContext.fillStyle = 'rgba(255, 255, 51,' + this.opacity + ')';
mainContext.fill();
};
function setupCircles() {
for (var i = 0; i < 25; i++) {
var randomX = Math.round(-200 + Math.random() * 700);
var randomY = Math.round(-200 + Math.random() * 700);
var speed = .2 + Math.random() * 3;
var size = 5 + Math.random() * 100;
var radius = 5 + Math.random() * 100;
var circle = new Circle(radius, speed, size, randomX, randomY);
circles.push(circle);
}
drawAndUpdate();
}
setupCircles();
function drawAndUpdate() {
mainContext.clearRect(0, 0, 1000, 1000);
for (var i = 0; i < circles.length; i++) {
var myCircle = circles[i];
myCircle.update();
}
requestAnimationFrame(drawAndUpdate);
}
jsFiddle : https://jsfiddle.net/CanvasCode/u8avnky2/1/
I added a variable known as color globally, so the fillBackgroundColor can access that instead.
var color = "white";
Then in your drawAndUpdate function we just do a fillRect with the color variable using the canvas width and height and it works.
function drawAndUpdate() {
mainContext.fillStyle = color;
mainContext.fillRect(0, 0, mainCanvas.width, mainCanvas.height);
for (var i = 0; i < circles.length; i++) {
var myCircle = circles[i];
myCircle.update();
}
requestAnimationFrame(drawAndUpdate);
}