How to make an image appear cyclically on each click with p5js - javascript

I am new to the amazing world of creative coding and p5js. I want to create a website for my design studio with a p5js effect that I can't find the solution for. I searched everywhere and I can't find anyone with the same problem as me, that's why I'm posting my very first message here. Here's the idea: in a canvas, I would like that with each mouse click, a different image can appear. Currently, my code allows to display images randomly but I would like to be able to set a cyclic order of appearance : work0.png, work1.png, work2.png... and it starts again.
If someone has seen this problem somewhere or could explain it to me I would be very grateful. Thanks !
let works = []
function preload() {
for (let i = 0; i < 5; i++) {
works[i] = loadImage("work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
function mouseClicked() {
imageMode(CENTER);
let r = floor(random(0, 6));
image(works[r], mouseX, mouseY, 500, 600);
}

instead of using random, you can use a counter
let works = []
function preload() {
for (let i = 0; i < 5; i++) {
works[i] = loadImage("work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++ //add one to the counter
image(works[counter%5], mouseX, mouseY, 500, 600); // the % symbols is modulo, which is a fancy word for remainder when divided by that number, so it would cycle from 0 to 4
}
So the counter starts at zero, and every time you click we increase the counter by one. to stop it from increasing constantly, we can use the modulo operator %(in programming this isn't the percent sign). examples: 2%5 is 2, 16%5 is 1, 25%5 is 0.
this essentially makes the value cycle from 0 to 4. the counter is constantly increasing, but the remainder when divided by five will always cycle.
also btw p5.js is a javascript library, and you are loading in code made by other people, which means without the p5.js library the code doesn't run in this code snippet on StackOverflow

Related

Is there a way to continuously move an object in P5 via user input?

I'm pretty sure after looking through the documentation that this isn't what the framework is intended to do, but I've got a student I'm tutoring who really wants to move an object drawn on a P5 canvas using input, like using the arrow keys.
What I was able to figure out is the following:
let value = 0;
function setup() {
// Create a canvas with a specified width and height
createCanvas(400, 400);
// Fill in background color
background("blue");
}
function draw() {
background(200);
rectMode(CENTER);
translate(value, value, value);
translate(150, 150, 150)
rect(0, 0, 20, 20);
}
function keyPressed() {
while(true) {
if (keyCode == LEFT_ARROW) {
value = 20;
}
}
}
But of course, this uses an infinite loop and is therefore less than ideal. Does anyone know of a better way to achieve this?
On my system (Chrome, MacOS), keyPressed is called repeatedly if I hold the left-arrow key. Below is a really simple example. Focus into the snippet and hold down your left arrow key - observe that the xPos decreases every frame.
You need to actually do something in your keyPressed method. Right now, the value is just being assigned repeatedly, and definitely, you don't want to have an infinite loop.
For the record, according to the docs, keyPressed isn't guaranteed to work this way on all systems, so you may need more complex logic depending on what system(s) you want to run this on.
function setup() {
createCanvas(640, 360);
}
let xPos = 100;
function draw() {
background(0,0,255);
fill(255,0,0);
ellipse(xPos,50,10,10);
}
function keyPressed(event) {
if (keyCode == LEFT_ARROW) {
xPos -= 1;
} else if (keyCode == RIGHT_ARROW) {
xPos += 1;
}
// this prevents default browser behavior
event.preventDefault();
return false;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.3.3/p5.min.js"></script>
let acl;
let pos;
let value = 0;
function setup() {
acl = createVector(0,0);
pos = createVector(0,0);
// Create a canvas with a specified width and height
createCanvas(400, 400);
// Fill in background color
background("blue");
}
function draw() {
background(200);
rectMode(CENTER);
rect(pos.x, pos.y, 20, 20);
pos.add(acl);
}
function keyPressed() {
if (keyCode == LEFT_ARROW) {
acl.set(-1,0)
}
if(keyCode == RIGHT_ARROW){
acl.set(1,0)
}
}

Why are custom fonts slowing down my program?

So I’m making a project in p5.js which aim to reproduce the Matrix rain code effect.
It ran pretty smooth until I decided to load a custom font.
When I did, my framerate dropped considerably. And it’s the case for every custom font that I try to load (only when I use the loadFont function; if I use textFont and some default font it doesn’t seem to slow down)
Of course, I use the loadFont function in the preload function, and textFont in setup, so I don’t know if this is a known thing ? How could I avoid this ?
Here is a Minimum Viable Example of the problem :
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$+-*/÷=%\"'#&_(),.;:?!\\|{}<>[]^~ "
var font;
var spacing_width = 10;
var spacing_height = 20;
function preload() {
font = loadFont('matrix_font.otf');
}
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
frameRate(60);
background(0);
textFont(font);
fill(255)
}
var lastUpdate;
function draw()
{
background(0);
var timeSinceLastUpdate = new Date().getTime() - lastUpdate;
print(timeSinceLastUpdate + "ms") // prints time elapsed between each frame
lastUpdate = new Date().getTime();
for(var i = 0; i < 40; i++)
for(var j = 0; j < 40; j++)
text(characters[int(random(0, characters.length))], j * spacing_width, i * spacing_height)
}
This code prints in the console values around 40-50ms, but when I comment out the line textFont(font); the values are around 10-20ms
I'm using this font but I have the problem with every font that I try to load
See https://github.com/processing/p5.js/issues/3435 for a discussion of this issue

Why does the red blocks skip a block in p5.js?

Ok so the red blocks dangers[] should fall down smoothly down the canvas, but they skip and act weirdly. Does it have anything to do with the for loop that show(); them?
Here's the code:
var dangers = [];
function setup() {
createCanvas(1060, 480);
createDanger();
}
var x = 0;
function createDanger() {
var randomWidth = (floor(random(980)) * 80) % 980;
dangers[x] = new Danger(randomWidth, -80);
dangers.forEach(function(obj) {
setInterval(function() {
obj.fall();
}, 1000);
});
x++;
console.log(dangers);
}
function draw() {
background(0);
for (danger of dangers) {
danger.show();
}
}
setInterval(createDanger, 3000)
//Danger
function Danger (x, y) {
this.x = x;
this.y = y;
var size = 80;
this.fall = function () {
this.y += size;
}
this.update = function () {
}
this.show = function () {
fill(255,0,0);
noStroke();
rect(this.x, this.y, size, size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>
<meta charset="utf-8" />
And if you have any other suggestions to my code, feel free to help. Thanks.
Here's an example of smoother performance.
var dangers = [];
function setup() {
background(0);
createCanvas(1060, 480);
createDanger();
}
function createDanger() {
var randomWidth = (floor(random(980)) * 80) % 980;
dangers.push(new Danger(randomWidth, -80));
}
var lastRenderTime;
function update() {
var now = Date.now();
if (lastRenderTime) {
var elapsed = (now - lastRenderTime) / 1000;
dangers.forEach(function(obj) {
obj.fall(elapsed);
});
}
dangers = dangers.filter(d => d.y < 400);
lastRenderTime = now;
}
function draw() {
update();
background(0);
for (danger of dangers) {
danger.show();
}
}
setInterval(function () {
createDanger();
}, 500)
//Danger
function Danger (x, y) {
this.x = x;
this.y = y;
this.speed = 40 + random(50); // pixels per second
var size = 80;
this.fall = function (time) {
this.y += this.speed * time;
}
this.update = function () {
}
this.show = function () {
fill(255,0,0);
noStroke();
rect(this.x, this.y, size, size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>
<meta charset="utf-8" />
This is close to the original, except that I did a couple of things differently:
I got rid of the idea of setting an update function for each object when you create a new one. That just doesn't make sense.
I updated the position of each object on every frame. I did this by adding an update function that is called from the draw function. That guarantees that the animation is up to date before each frame is rendered.
I used an absolute real-time clock to adjust the amount of time that passes per frame. I keep track of this with a lastRenderTime variable and subtract now minus lastRenderTime to compute the elapsed time. The elapsed time is passed through to the update function as a parameter.
I used a more physics-based approach to updating the position of the object.
I defined the speed of each object in pixels per second. The position is updated, not by a constant amount each frame, but instead by a variable amount, according to the actual amount of time that has elapsed since the last frame of animation. This ensures that the object moves consistently in real-time regardless of the performance of the machine rendering it. So this.y += size becomes this.y += this.speed * time.
I changed the array access to use push which is a more language-agnostic description of the operation you are trying to perform rather than relying on JavaScript's quirky "extend the length of the array when you write off the end of it" behaviour.
I added a filter function to remove expired objects after they hit the bottom of the window to ensure the array doesn't grow without bound and consume resources over time.
I increased the frequency with which the objects are created from once every 3000 milliseconds to once every 500 milliseconds. Perhaps only because I'm a little impatient. :)
I also decided to choose a random speed for each object, just for a little visual variety and to make it clear that each object has its own speed.

JavaScript Canvas -- Is it possible to draw depending upon Math.random?

I am creating a very basic sprite for a game (it is drawn in Canvas/Context/LineTo). I want its expression to change randomly based on two different draw methods. This is my attempt at this:
drawFace = function () {
if (Math.random() < 0.05) {
Player.drawhappyface(context);
}
else if (Math.random() < 0.1) {
Player.drawsadface(context);
}
}
drawFace();
I can confirm that the drawhappyface and drawsadface draw methods work independent of this function (respectively drawing a smile and a frown). But using this function and its logic, they're simply never drawn (the player entirely lacks a face). So, have I written this wrong? I'm inspired by the following simulation which has constantly animating expressions also using Math.random: http://www.blobsallad.se/
If I instead write the function like this, then absolutely nothing is drawn on the canvas (all other sprites, etc. also not drawn):
drawFace = function (context) {
if (Math.random() < 0.05) {
Player.drawhappyface(context);
}
else if (Math.random() < 0.1) {
Player.drawsadface(context);
}
}
drawFace();
The problem might be that you call Math.random() in both if statements and then they will have different values. Call Math.random() only once and save the value for every time you need/call drawFace Try like so:
drawFace = function () {
var randomNumber = Math.random();
console.log(randomNumber);
if (randomNumber < 0.05) {
console.log('randomNumber < 0.05');
}
else if (randomNumber < 0.1) {
console.log('randomNumber < 0.1');
}
}
drawFace();
if you just want to draw happy or sad face you can do even simpler if else
drawFace = function () {
var randomNumber = Math.random();
console.log(randomNumber);
if (randomNumber < 0.5) {
console.log('draw happy face');
} else {
console.log('draw sad face');
}
}
drawFace();

problems with translating a p5js sketch into processing

I translate a p5js scetch to processing because I want to generate a mov file. The scetch is based on a tutorial from daniel shiffman on kadenze but I´ve got a problem now with a boolean output. How can I code this piece of p5js code in processing?
function draw() {
for( var i = 0; i < particles.length; i++) {
if(particles[i].isDead()) {
//code
}
}
}
function Particles() {
this.isDead = function() {
var distance = p5.Vector.sub(attractor.pos, this.pos);
var d = distance.mag();
if(d < 5) {
return true;
} else {
return false;
}
}
}
First I tried it with void, but void hasn´t got an output. Then I tried something like this with boolean but it also doesn´t work.
void setup() {
//code
}
void draw () {
for (int i = 0; i < particles.length; i++) {
if(particles[i].isDead()) {
//code
}
}
}
Class Particle {
Particle() {
//code
}
boolean isDead() {
PVector distance = PVector.sub(a.location, location);
float d = distance.mag();
if(d < 5) {
return true;
} else {
return false;
}
}
}
It will be great if somebody can help me.
regards mattias
The first problem is that you aren't ever actually doing anything if isDead() returns true. You need to actually put code inside that if statement, or maybe move that if statement to somewhere that makes logical sense.
For example, you could modify your display() function inside your Mover class to draw in green when the Mover is dead:
void display() {
if (isDead()) {
fill(0, 255, 0);
} else {
fill(255, 0, 0);
}
stroke(0);
strokeWeight(2);
ellipse(location.x, location.y, 10, 10);
}
That's just an example, and what you actually do depends on what you want to happen when a Mover is dead.
But even if you make that change, you'll notice that a Mover only dies when it reaches the middle of the gray circle. That's because of this if statement inside your isDead() function:
PVector distance = PVector.sub(a.location, location);
float d = distance.mag();
if (d < 5) {
return true;
} else {
return false;
}
You're measuring the distance between the center of the gray circle and the center of each little red circle. But you're only returning true if the d < 5. The problem with that is the gray circle has a diameter of 50, so if you want the little red circles to die when they enter the gray circle, you have to factor that diameter into your calculations. Try using if(d < 30) instead. I got 30 by dividing the diameter 50 by 2 and then adding 5 for the small circle size. You might have to play with it to get the effect you're looking for.
By the way, you might be interested in the dist() function, which returns the distance between two points. More info can be found in the reference.

Categories