p5 resize window without clearing the background - p5.js

The following code makes use of the p5dom add-on to position the canvas in the centre of the window. To dynamically resize the canvas I'm using the windowResized() function. I want to keep the background function in setup. How do I prevent the background colour from clearing when I resize the window? Many thanks.
var cnv;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
cnv.position(x, y);
}
function setup() {
cnv = createCanvas(windowWidth,windowHeight);
centerCanvas();
background(255, 0, 200);
}
function draw(){
}
function windowResized() {
centerCanvas();
resizeCanvas(windowWidth,windowHeight)
}

One thing you might do is draw everything to a buffer instead of directly to the screen. The createGraphics() function is your friend here. From the P5.js reference:
var pg;
function setup() {
createCanvas(100, 100);
pg = createGraphics(100, 100);
}
function draw() {
background(200);
pg.background(100);
pg.noStroke();
pg.ellipse(pg.width/2, pg.height/2, 50, 50);
image(pg, 50, 50);
image(pg, 0, 0, 50, 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js"></script>
You would want to draw everything to a buffer, and then when the screen is resized, redraw that buffer.

Simply by adding the background function to the draw one too.
Try this:
var cnv;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
cnv.position(x, y);
}
function setup() {
cnv = createCanvas(windowWidth,windowHeight);
centerCanvas();
background(255, 0, 200);
}
function draw(){
//HERE:
background(255, 0, 200);
}
function windowResized() {
//(you can add it here too...)
centerCanvas();
resizeCanvas(windowWidth,windowHeight);
}

I would simply set the background in the windowResized function.
function windowResized() {
centerCanvas();
resizeCanvas(windowWidth,windowHeight)
background(255, 0, 200);
}

For an alternative method, you could use CSS to edit the dimensions using something similar to the following:
<style>
canvas {
width: 100%;
height: 100%;
}
</style>

Related

Hide and show background image when keyboard pressed, preserving overlay elements

I would like to paint ellipses over an image when clicking the mouse, and when I press the keyboard, hide and show the underneath image alternatively, without cleaning the ellipses.
I'm using createGraphics() to store the image data, and remove() so when the keyboard is pressed I spect the image disappear but it doesn't work
Here is a sketch of what I trying to do:
let isMouseBeeingPressed = false;
let img;
let isShow = true
let bufferImg;
function preload() {
img = loadImage(
"https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&dpr=1&w=500"
);
}
function setup() {
createCanvas(500, 500);
background(255);
loadImageBuffer();
}
function loadImageBuffer() {
bufferImg = createGraphics(400, 400);
bufferImg.image(img, 0, 0);
image(bufferImg, 0, 0);
}
function draw() {
if(isMouseBeeingPressed) {
stroke(0, 0, 0, 50);
fill(255);
ellipse(mouseX, mouseY, 20);
}
}
function keyPressed() {
if(isShow) {
bufferImg.remove();
} else {
image(bufferImg, 0, 0);
}
console.log('isShow:', isShow);
return isShow = !isShow;
}
function mousePressed() {
isMouseBeeingPressed = true;
}
function mouseReleased() {
isMouseBeeingPressed = false;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Any idea of how to achieve this?
The best approach is probably to draw the ellipses to a separate buffer (p5.Graphics), and then draw that on top of the image or blank background as needed. An alternative approach would be to record the position of each ellipse in an array so that they can be redrawn. However, the latter approach will use more memory and switching the image on and off will have a noticeable delay after many ellipses have been drawn.
Approach #1 (render via p5.Graphics)
let img;
let graphics;
let isShow = true;
function preload() {
img = loadImage(
"https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&dpr=1&w=500"
);
}
function setup() {
createCanvas(500, 500);
background(255);
image(img, 0, 0);
graphics = createGraphics(width, height);
}
function draw() {
if (mouseIsPressed) {
graphics.stroke(0, 0, 0, 50);
graphics.fill(255);
graphics.ellipse(mouseX, mouseY, 20);
background(255);
if (isShow) {
image(img, 0, 0);
}
image(graphics, 0, 0);
}
}
function keyPressed(e) {
isShow = !isShow;
background(255);
if (isShow) {
image(img, 0, 0);
}
image(graphics, 0, 0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Update: one thing to note about this approach is that whenever the mouse is pressed, the entire scene must be redrawn (white background, image if applicable, and foreground). The reason for this is antialiasing and transparency. When you draw an ellipse on the graphics buffer the edges will have some partially transparent pixels due to antialiasing. If you repeatedly draw the buffer as an overlay without redrawing what is behind it then the partially transparent pixels will become less and less transparent until they are sold black. This will cause your ellipses to have a slightly thicker and more pixelated outer edge.
Approach #2 (Array of ellipse positions)
let img;
let isShow = true
let positions = [];
function preload() {
img = loadImage(
"https://images.pexels.com/photos/20787/pexels-photo.jpg?auto=compress&cs=tinysrgb&dpr=1&w=500"
);
}
function setup() {
createCanvas(500, 500);
background(255);
image(img, 0, 0);
}
function draw() {
if (mouseIsPressed) {
stroke(0, 0, 0, 50);
fill(255);
ellipse(mouseX, mouseY, 20);
positions.push([mouseX, mouseY]);
}
}
function keyPressed() {
isShow = !isShow;
background(255);
if (isShow) {
image(img, 0, 0);
}
stroke(0, 0, 0, 50);
fill(255);
for (const [x, y] of positions) {
ellipse(x, y, 20);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

P5.JS - Filling a shape with a preloaded image, using an object, its properties and its functions

I am trying to create an object and for it to have a shape (an ellipse), an image, and a function that somehow fills the shape with a pre-loaded image.
I already found this post, but I can't get it to work if I try to make it neat and fit it all into an object.
To be clear, this is what it would look like:
let image;
let hypotheticalObject
function preload()
{
image = loadImage('Assets/randomimage.jpeg');
}
function setup()
{
hypotheticalObject = {
objectImage: image,
graphicsBuffer: createGraphics(100, 100),
xPos: width / 2,
yPos: height / 2,
size: 50,
colour: color(255, 255, 255),
shape: function()
{
fill(this.colour),
ellipse(this.xPos, this.yPos, this.size);
},
mask: function()
{
this.graphicsBuffer.shape();
},
render: function()
{
something something
}
}
function draw()
{
hypotheticalObject.render();
}
That's kind of how far I can get, as I can't figure out how to proceed.
There are a couple of issues with your code, namely this:
shape: function() {
// ...
},
mask: function() {
this.graphicsBuffer.shape();
},
You declare shape() on hypotheticalObject but then you try to call it on graphicsBuffer? This isn't how javascript works. You could do something like this:
hypotheticalObject.graphicsBuffer.shape = function() {
// When graphicsBuffer.shape() is called, the this keyword will be bound to graphicsBuffer
// Unfortunately we need a to access members from hypotheticalObject as well, so that is inconvenient:
this.fill(hypotheticalObject.colour),
this.ellipse(hypotheticalObject.xPos, hypotheticalObject.yPos, hypotheticalObject.size);
}
However I don't think that is the most idiomatic approach either. Here's a working solution that tries to stay close to your code:
let img;
let hypotheticalObject;
function preload() {
img = loadImage('https://www.paulwheeler.us/files/windows-95-desktop-background.jpg');
}
function setup() {
createCanvas(windowWidth, windowHeight);
hypotheticalObject = {
objectImage: img,
// The graphics you're using for the mask need to be the size of the image
graphicsBuffer: createGraphics(img.width, img.height),
xPos: width / 2,
yPos: height / 2,
size: 50,
init: function() {
this.maskedImage = createImage(this.objectImage.width, this.objectImage.height);
},
updateShape: function() {
this.graphicsBuffer.clear();
// The fill color is irrelevant so long as it is opaque
this.graphicsBuffer.fill(0);
// These drawing instructions need to happen on the p5.Graphics object
this.graphicsBuffer.ellipse(this.xPos, this.yPos, this.size);
},
render: function() {
this.updateShape();
// When you call mask on an image it changes the image so we need to make a copy
this.maskedImage.copy(
this.objectImage,
0, 0, this.objectImage.width, this.objectImage.height,
0, 0, this.objectImage.width, this.objectImage.height
);
this.maskedImage.mask(this.graphicsBuffer);
// Always draw the image at 0, 0 since the masked portion is already offset
image(this.maskedImage, 0, 0);
}
}
hypotheticalObject.init();
}
let xv = 2;
let yv = 2;
function draw() {
background(255);
hypotheticalObject.xPos += xv;
hypotheticalObject.yPos += yv;
if (hypotheticalObject.xPos > width) {
hypotheticalObject.xPos = width;
xv *= -1;
} else if (hypotheticalObject.xPos < 0) {
hypotheticalObject.xPos = 0;
xv *= -1;
}
if (hypotheticalObject.yPos > height) {
hypotheticalObject.yPos = height;
yv *= -1;
} else if (hypotheticalObject.yPos < 0) {
hypotheticalObject.yPos = 0;
yv *= -1;
}
hypotheticalObject.render();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Update: Animated Gifs
If you want this to work with animated gifs, you have to deal with an idiosyncrasy of p5.js: the fact that it updates gif images only when they are rendered with either the image() function or the texture() function. And that rendering code depends on the drawing context having timing information, and therefor does not work with off screen p5.Graphics buffers. Here's an example:
let img;
let hypotheticalObject;
function preload() {
img = loadImage('https://www.paulwheeler.us/files/Clouds.gif');
}
function setup() {
createCanvas(img.width, img.height);
hypotheticalObject = {
objectImage: img,
// The graphics you're using for the mask need to be the size of the image
graphicsBuffer: createGraphics(img.width, img.height),
xPos: width / 2,
yPos: height / 2,
size: 100,
init: function() {
this.maskedImage = createImage(this.objectImage.width, this.objectImage.height);
},
updateShape: function() {
this.graphicsBuffer.clear();
// The fill color is irrelevant so long as it is opaque
this.graphicsBuffer.fill(0);
// These drawing instructions need to happen on the p5.Graphics object
this.graphicsBuffer.ellipse(this.xPos, this.yPos, this.size);
},
render: function() {
this.updateShape();
// When you call mask on an image it changes the image so we need to make a copy
this.maskedImage.copy(
this.objectImage,
0, 0, this.objectImage.width, this.objectImage.height,
0, 0, this.objectImage.width, this.objectImage.height
);
this.maskedImage.mask(this.graphicsBuffer);
// Always draw the image at 0, 0 since the masked portion is already offset
image(this.maskedImage, 0, 0);
}
}
hypotheticalObject.init();
}
let xv = 2;
let yv = 2;
function draw() {
// In order to get a gif to animate it needs to be passed to either the image() function or the texture() function. And unfortunately it must be the global one, not the version on an off-screen p5.Graphics
// However we don't actually want to display this
image(img, width, height);
background(255);
hypotheticalObject.xPos += xv;
hypotheticalObject.yPos += yv;
if (hypotheticalObject.xPos > width) {
hypotheticalObject.xPos = width;
xv *= -1;
} else if (hypotheticalObject.xPos < 0) {
hypotheticalObject.xPos = 0;
xv *= -1;
}
if (hypotheticalObject.yPos > height) {
hypotheticalObject.yPos = height;
yv *= -1;
} else if (hypotheticalObject.yPos < 0) {
hypotheticalObject.yPos = 0;
yv *= -1;
}
hypotheticalObject.render();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Call function draw on mouse click

Is it possible to call the function draw(){} in a p5 js sketch when clicking on the canvas?
I would like everything under the draw function to be called when clicking anywhere on the canvas, and not before.
function setup() {
createCanvas(500, 500);
frameRate(65);
background('#ff0a0a');
textSize(60);
text("ART", 370, 250);
};
function draw() {
noFill();
var red = random(100);
var green = random(200);
var blue =random(230);
var h = random(height);
stroke(red,green,blue);
strokeWeight(8);
rect(frameCount,h,300,20+(frameCount));
ellipse(frameCount,h ,300,20+(frameCount));
triangle(frameCount,h ,300,20+(frameCount));
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/p5.js"></script>
This works fine for me:
function setup() {
createCanvas(500, 500);
frameRate(65);
background('#ff0a0a');
textSize(60);
text("ART", 370, 250);
};
function mousePressed() {
noFill();
var red = random(100);
var green = random(200);
var blue =random(230);
var h = random(height);
stroke(red,green,blue);
strokeWeight(8);
rect(frameCount,h,300,20+(frameCount));
ellipse(frameCount,h ,300,20+(frameCount));
triangle(frameCount,h ,300,20+(frameCount));
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/p5.js"></script>
Just to mention another way and to address your question in Kevin's answer above, you can do something like this to draw continuously when the mouse is clicked once and stop when it is clicked again. This would work like a toggle switch for drawing :-
var drawThings;
function setup() {
createCanvas(500, 500);
frameRate(65);
background('#ff0a0a');
textSize(60);
text("ART", 370, 250);
}
function draw() {
if (drawThings) {
noFill();
var red = random(100);
var green = random(200);
var blue = random(230);
var h = random(height);
stroke(red, green, blue);
strokeWeight(8);
rect(frameCount, h, 300, 20 + (frameCount));
ellipse(frameCount, h, 300, 20 + (frameCount));
triangle(frameCount, h, 300, 20 + (frameCount));
}
}
function mouseClicked() {
drawThings = !drawThings;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/p5.js"></script>

Moving an objects while others stay static in a click controlled sketch

So I'm trying to build an animation that I can step through with mouse clicks. Adding individual objects click by click is easy. Sequence I want is as follows:
One object(a) drawn initially.
First mouse click adds an object(b).
Second mouse click adds an object(c).
Third mouse click, object(c) should move across the screen and disappear.
I'm having a problem on the last part of the sequence. I can't figure out how to make the object move and still maintain the static part of the sketch. The normal way of doing movement is to change the coordinates of the object with each loop through the draw() function, and use the background to cover up the previous objects. Can't do that in this case because I need object(a) and object(b) to be persistent.
Code below. Thanks for your help!
var count = 0;
function setup() {
createCanvas(200, 200);
a = new Object1(20, 40);
b = new Object1(20, 85);
c = new Object1(20, 130);
}
function draw() {
background(200);
a.display();
if (count == 1) {
b.display();
}
if (count == 2) {
b.display();
c.display();
}
if (count == 3) { //this is where I have a problem
}
if (count > 3) {
count = 0;
}
}
function Object1(ix, iy, itext) {
this.x = ix;
this.y = iy;
this.text = itext;
this.display = function() {
fill(160);
rect(this.x, this.y, 40, 40);
}
}
function mousePressed() {
count++;
}
Generally how you'd do this is by drawing the static part of your scene to an off-screen buffer, which you can create using the createGraphics() function. From the reference:
var pg;
function setup() {
createCanvas(100, 100);
pg = createGraphics(100, 100);
}
function draw() {
background(200);
pg.background(100);
pg.noStroke();
pg.ellipse(pg.width/2, pg.height/2, 50, 50);
image(pg, 50, 50);
image(pg, 0, 0, 50, 50);
}
You'd draw the static part to the buffer, then draw the buffer to the screen, then draw the dynamic stuff on top of it each frame.
This has been discussed before, so I'd recommend doing a search for stuff like "processing pgraphics" and "p5.js buffer" to find a bunch more information.

p5.js this._renderer.image is not a function issue

var adobe1bar1;
function preload() {
adobe1bar1 = loadImage("1-1.png");
}
function setup() {
createCanvas(500, 500, WEBGL);
center = createVector(width / 2, height / 2, height / 2);
cameraZ = -500;
}
function draw() {
background(40); // backgraound white
var cameraX = map(mouseX, 0, width, -500, 500); // map
var cameraY = map(mouseY, 0, height, -500, 500);
console.log(cameraY, cameraX);
if (cameraZ < -500) {
cameraZ = -500;
}
if (cameraZ > 0) {
cameraZ = 0;
}
camera(center.x + cameraX, center.y + cameraY, center.z + cameraZ, center.x, center.y, center.z, 0, 1, 0);
translate(center.x, center.y, center.z);
translate(0, 0, 0);
image(adobe1bar1, -250, -250, 500, 500);
}
Here is my p5.js code.
When I use the image() function
The following error message keeps appearing.
Uncaught TypeError: this._renderer.image is not a function
Is there a solution?
When not using 'WEBGL',
it uploads images without error,
but the camera() function does not work.
image is not a WEBGL function.
Try to apply an image texture to a plane instead.
texture(adobe1bar1);
translate(-250, -250, 0);
plane(500);
https://github.com/processing/p5.js/wiki/Getting-started-with-WebGL-in-p5
EDIT:
To use transparent texture you need to enable blend mode which can be done by using:
fill(0,0,0,0);
In setup

Resources