I am really new to p5.js, and I'm trying to find a solution to this for a few days with no luck.
I have a button and an ellipse. When the button is clicked a rectangle appears on the canvas and when the ellipse is clicked its color changes.The problem is that I can't have both at the same time and the reason is this part of the code:
stroke(rgb);
strokeWeight(2);
If I have it on my code, when I click the button the rectangle appears, but when I click the ellipse nothing happens. If I leave it out of my code, I can change the ellipse's color, but when I click the button nothing happens.
I have no idea why these two lines of code make it impossible to interact with both the button and the shape, and I'd really like to find out. Am I missing something important?
My code is the following:
function setup(){
createCanvas(800, 600);
bubble = new new_ellipse(500,300);
}
function draw() {
background(51);
button = createButton("clickme");
button.position(100, 65);
button.mousePressed(show_rect);
bubble.display();
stroke(rgb);
strokeWeight(2);
}
function mousePressed(){
bubble.clicked();
}
function new_ellipse(x,y){
this.x = x;
this.y = y;
this.col = color(255,100);
this.display = function(){
stroke(255);
fill(this.col);
ellipse(this.x, this.y, 100, 100);
}
this.clicked = function(){
var d = dist(mouseX, mouseY, this.x, this.y);
if(d < 50){
this.col = color(233,11,75);
}
}
}
function show_rect(){
fill(255,24,23);
rect(200,200,100,100);
}
Two related issues:
Don't call createButton inside draw. draw is called about 60 times a second, so this spams buttons. draw is your animation loop. Use setup to set things up.
Given that draw() runs 60 times a second, any calls to show_rect triggered by a button press will be almost instantly wiped clean by background(51) a split second later.
One option is to introduce a boolean to keep track of whether the button has been pressed yet, then re-draw it every frame if it's supposed to be visible.
Another approach is to drop the draw() function and re-paint only when state changes. This prevents background(51) from blasting everything and is efficient, but also isn't suitable if you have animations or a good deal of dynamism in your app.
Here's a sample of the first approach, using a boolean:
var bubble, button;
var shouldShowRect = false;
function setup() {
createCanvas(800, 600);
bubble = new Ellipse(500, 300);
button = createButton("clickme");
button.mousePressed(function () {
// or set to true if you don't want to toggle
shouldShowRect = !shouldShowRect;
});
button.position(100, 65);
}
function draw() {
background(51);
bubble.display();
if (shouldShowRect) {
showRect();
}
strokeWeight(2);
}
function mousePressed() {
bubble.clicked();
}
function Ellipse(x, y) {
this.x = x;
this.y = y;
this.col = color(255, 100);
this.display = function() {
stroke(255);
fill(this.col);
ellipse(this.x, this.y, 100, 100);
}
this.clicked = function() {
var d = dist(mouseX, mouseY, this.x, this.y);
if (d < 50) {
this.col = color(233, 11, 75);
}
}
}
function showRect() {
fill(255, 24, 23);
rect(200, 200, 100, 100);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
Related
This question already has answers here:
how to skew image like this
(5 answers)
Closed 10 months ago.
i am trying to make a simple canvas game in jquery,
basically, i have an infinite loop that constantly draws the player and background:
loop = () => {
$('#player').css("transform", "rotateX("+xr+"deg) rotateY("+yr+"deg)");
ctx.fillStyle = 'gray';
ctx.fillRect(0,0,400,400);
ctx.drawImage(player, x, y, 50, 50);
window.setTimeout(loop, 1);
}
And then i have a switch statement that alters the player's XR and YR when they push the A and D buttons:
$(document).ready(()=>{
loop();
$(document).bind('keydown',(e)=>{
switch(e.keyCode){
case 65:
//moving left (altering the xr and yr values)
break;
case 87:
//moving up (altering the y and x values based on SIN and COS and player's XR and YR)
break;
etc...
}
});
});
i can tell it's detecting the key pushes, because when i run CONSOLE.LOG() in the switch statement, it prints something to the console,
so clearly this is a problem with images drawn to a canvas not keeping their css styling.
if anybody can figure out how to keep an image's css when drawing it to a canvas, or alter an image draw in a canvas's css, that would be great.
and no, i didn't find any answers anywhere else. this question doesn't seem to have been asked yet.
EDIT: xr and yr are XROTATION and YROTATION variables.
apparently, images don't keep their css in canvases, and there is no way to add this. never mind, guess i'll try something else.
You don't need CSS, you can do everything with the Canvas, you have translate and rotate methods. Here a very basic example:
const ctx = canvas.getContext("2d");
const { width: w, height: h } = canvas;
let x = (w - 50) >> 1, y = (h - 50) >> 1;
let angle = 0;
const Keys = {
pressed: {},
handleEvent({type, code}) {
this.pressed[code] = type === "keydown"
}
}
document.addEventListener("keydown", Keys);
document.addEventListener("keyup", Keys);
function update() {
if (Keys.pressed["ArrowLeft"]) {
--angle;
}
if (Keys.pressed["ArrowRight"]) {
++angle;
}
}
function draw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w, h);
ctx.translate(x + 25, y + 25);
ctx.rotate(angle * Math.PI / 180 );
ctx.drawImage(player, -25, -25, 50, 50);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
let player = new Image();
player.src = "https://1.bp.blogspot.com/-ddkcnhuU07g/Vllq4DNohkI/AAAAAAAACHo/p8d9SZxOy-w/s1600/mship1.png";
requestAnimationFrame(function render() {
update();
draw();
requestAnimationFrame(render);
})
<canvas id="canvas" width="320" height="240"></canvas>
This is my code:
var drawGlob = function () {
function keyPressed() {
if (keyTyped === 32) {
var size = (random(100, 150));
fill(random(0, 255), 0, 0);
ellipse(mouseX, mouseY, size, size);
return false;
}
}
};
var draw = function () {
noStroke();
drawGlob();
};
After some testing, I discovered that the problem lies within:
var draw = function () {
noStroke();
drawGlob();
};
Not sure where and how you are using it, more information would be useful. Anyway, in drawGlob you are declaring a function in a function, and where is it taking the variable keyTyped since it is not passed as arguments?
Are you referring to keyTyped the function? maybe take a look to this example
Maybe you meant keyCode here another example
Edit: try this:
function keyPressed() {
if (keyCode === 32) {
var size = (random(100, 150));
fill(random(0, 255), 0, 0);
ellipse(mouseX, mouseY, size, size);
return false;
}
};
var draw = function() {
noStroke();
};
So, when you press a Spacebar(32) it will trigger a random circle where the mouse is pointed.
I am working on a sketch tool with html canvas.
I am using a common algorithm for this, that uses the mousedown, mousemove, mouseup events.
mousedown
I beginPath(), and moveTo(// the mouse coordinates).
mousemove
I draw lineTo(// the mouse coordinates), and then stoke(// the line to render it)
mouseup
I do nothing, // closePath()
And I noticed that, calling the stroke method without first calling closePath or beginPath, will redraw or restroke all previous paths or lines, which makes them appear thicker than the define color.
without a transparent color its is barely noticeable, but the colors do appear thicker than they should be.
but with color with transparency|alpha e.g. rgba(). The most recent path or line respects the color's transparency, but all previous line due to the redraw, the transparent colored line overlap and that causes previous lines to get thicker in sequence or succession.
is there a way to avoid|prevent this behavior. thank in advance.
sample is below, try drawing really fast!
var cvs = document.querySelector("canvas");
cvs.width = cvs.parentElement.clientWidth;
var colorInput = document.querySelector("input");
var ctx = cvs.getContext("2d");
ctx.strokeStyle = "rgba(0, 0, 0, 0.4)"
ctx.lineWidth = 20;
onDraw(cvs, {
penDown: function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
ctx.strokeStyle = colorInput.value;
ctx.beginPath();
ctx.moveTo(x, y);
},
penMove: function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
ctx.lineTo(x, y);
ctx.stroke();
},
penUp: function() {
// ctx.closePath;
}
});
function onDraw(node, drawHandler, beginHandler, endHandler, outOfBoundHandler, sticky) {
var mouseDown = false, mouseOut = false;
if( typeof drawHandler === "object" ) {
var drawEvents = drawHandler;
drawHandler = get(drawEvents.penMove);
beginHandler = get(drawEvents.penDown);
endHandler = get(drawEvents.penUp);
outOfBoundHandler = get(drawEvents.penOff);
sticky = drawEvents.sticky;
}
function get(name) {
return typeof name === "string" ? drawEvents[ name ] : name;
}
node.addEventListener('mousedown', function(e) {
mouseDown = true;
beginHandler&&beginHandler.call(this, e);
});
node.addEventListener('mousemove', function(e) {
mouseDown&&drawHandler&&drawHandler.call(this, e);
});
node.addEventListener('mouseup', function(e) {
mouseDown = false;
endHandler&&endHandler.call(this, e);
});
node.addEventListener('mouseout', function(e) {
mouseDown&&outOfBoundHandler&&outOfBoundHandler.call(this, e);
if( !sticky ) {
mouseDown = false;
}
});
}
.wrapper { border: 1px solid #aaa }
<div class="wrapper">
<canvas border="1" width="600" hieght="400">Canvas is not supported</canvas>
<input type="text" value="rgba(0, 0, 0, 0.3)" placeholder="rgba(#, #, #, #)">
</div>
If no Path argument is passed to stroke and fill methods they will use the path currently being declared with the context's drawing methods.
const ctx = c.getContext('2d');
// starts Path declaration
ctx.moveTo(20, 20);
ctx.lineTo(30, 80);
ctx.stroke(); // first rendering
setTimeout(() => {
ctx.clearRect(0, 0, 300, 150); // even if we clear the canvas
ctx.lineTo(70, 20); // this will continue path declaration
setTimeout(() => {
ctx.stroke(); // and this will draw everything
}, 1000);
}, 1000);
<canvas id="c"></canvas>
The only ways to start a new path declaration (except for the first one) are to either reset the whole context (not good), or to use beginPath method.
const ctx = c.getContext('2d');
// starts Path declaration
ctx.moveTo(20, 20);
ctx.lineTo(30, 80);
ctx.stroke(); // first rendering
setTimeout(() => {
ctx.clearRect(0, 0, 300, 150);
ctx.beginPath(); // start a new Path declaration
ctx.moveTo(30, 80); // we need to move to the previous coords
ctx.lineTo(70, 20); // this will be alone
ctx.stroke(); // and this will draw only the new path
}, 1000);
<canvas id="c"></canvas>
About closePath, it's just a lineTo(last_point_in_current_path_declaration), and doesn't ends a path declaration in no way.
So for your problem, there are two strategies you can adopt :
keep only the last coordinates, and at every mousemove,
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(nextX, nextY);
keep all your coordinates in an array and redraw everything every time
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
coords.forEach(pt => ctx.lineTo(pt.x, pt.y));
ctx.stroke();
Personally, I prefer the second one, which allows some undo - redo, and to e.g change your pen's style.
I am working with Processing.js (version 1.4.8).
I have 5 white points, which coordinates I chose specifically. The black dot marks the center of the sketch! I want to be able to translate and scale my sketch. ALSO, I want it to occupy the whole window.
var mapWidth, mapHeight, canvas, pjs, centerX, centerY;
var points = [[100, 100], [300, 100], [100, 300], [300, 300], [200, 200]];
var setSize = function() {
mapWidth = $(window).outerWidth();
mapHeight = $(window).outerHeight();
if (pjs) {
pjs.size(mapWidth, mapHeight);
}
};
var clear = function() {
pjs.background(200);
};
var drawPoint = function(coordinates) {
var radius = 30;
pjs.ellipse(coordinates[0], coordinates[1], radius, radius);
};
var drawPoints = function() {
pjs.fill(255);
points.map(function(point) {
drawPoint(point);
});
};
var calculateCenter = function() {
centerX = Math.floor(mapWidth / 2);
centerY = Math.floor(mapHeight / 2);
};
var drawCenter = function() {
calculateCenter();
var radius = 10;
pjs.fill(0);
pjs.ellipse(centerX, centerY, radius, radius);
console.log("center", centerX, centerY);
};
var move = function() {
pjs.translate(200, 300);
redraw();
};
var zoomIn = function() {
pjs.scale(2, 2);
redraw();
};
var draw = function() {
clear();
drawPoints();
drawCenter();
};
var redraw = function() {
clear();
draw();
};
var addEvent = function(object, type, callback) {
if (object == null || typeof object == "undefined") return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
} else if (object.attachEvent) {
object.attachEvent("on" + type, callback);
} else {
object["on" + type] = callback;
}
};
$(function() {
canvas = document.getElementById("map");
setSize();
var pjsRun = function(processingjs) {
pjs = processingjs;
pjs.setup = function() {
pjs.size(mapWidth, mapHeight);
draw();
};
};
var p = new Processing(canvas, pjsRun);
addEvent(window, "resize", function(event) {
setSize();
redraw();
});
});
Until here, everything is fine, as you can see in this CodePen.
I want to be able to resize the window AND keep the transformations (translations, scales, ...) that I had already performed.
Please, open the CodePen and try to reproduce this weird behaviour:
1) Perform one (or two) transformation(s) using the top-right buttons
The map is translated by 200 to the right and 300 downwards.
Everything OK by now...
But the problem arises now.
2) Resize the window
The five points are again where they were before the "translate" operation.
So... Again... Is there a way to resize without losing all the transformations that had been performed?
Thanks
Like you've discovered, it appears as though calling the size() function resets the transformation matrix. The short answer to your question is that you need to keep track of the transformations, and then apply them whenever you draw something.
The longer answer to your question is that you're using Processing.js a little bit differently than people typically use it. You've left out the draw() function (note that your draw() function is not the draw() function that's automatically called 60 times per second) and are trying to code event handlers yourself. This disconnect is why you're having issues.
If I were you, I'd start with a more basic sketch that starts out using Processing's built-in draw() function. Write code that draws the scene every frame. Make sure you set the translation and scale every frame. Here's an example:
var draw = function() {
scale(scaleX, scaleY);
translate(translateX, translateY);
background(200);
fill(255);
points.map(function(point) {
ellipse(coordinates[0], coordinates[1], 30, 30);
});
fill(0);
ellipse(width/2, height/2, 10, 10);
};
Then setup event listeners that change the values of scaleX and scaleY as well as translateX and translateY. Let Processing handle the rest.
I added a gif as an element and now I want to lay it behind a hollowed out brain image I loaded on my canvas. The gif element lays on top of the brain image and I want to lay it behind the brain image. Is there a way to do this? Here is the link to the thimble https://thimbleprojects.org/ejonescc16/63728/.
var brains;
var coolpup;
var canvas;
function preload(){
brains = loadImage('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/kevin%20final/assets/brains.png');
coolpup = createImg('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/Brain/gifs/pup.gif');
}
function setup() {
createCanvas(windowWidth,windowHeight);
strokeWeight(2);
rSlider = createSlider(0, 255, 100);
rSlider.position(50, windowHeight-80);
gSlider = createSlider(0, 255, 0);
gSlider.position(50, windowHeight-60);
bSlider = createSlider(0, 255, 255);
bSlider.position(50, windowHeight-40);
}
function draw() {
background(0);
r = rSlider.value();
g = gSlider.value();
b = bSlider.value();
imageMode(CENTER);
coolpup.position(windowWidth/2-350, windowHeight/2-150);
coolpup.size(130,200);
image(brains, windowWidth/2, windowHeight/2);//DISPLAYS BRAINS
fill(255);
textSize(30);
text("This is my brain", windowWidth/2-375, windowHeight-100);
text("This is my brain while coding", windowWidth/2+65, windowHeight-100);
}
I noticed you used createImg to load your gif image.
You can instead use this library to display gifs.
https://github.com/antiboredom/p5.gif.js
This said, you have to first draw the gif the add the mask on top.
var brains, coolpup, coding;
var canvas;
function preload() {
brains = loadImage('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/kevin%20final/assets/brains.png');
coolpup = loadGif('https://raw.githubusercontent.com/Er-Jones/EJonesCCS16/master/code/Brain/gifs/pup.gif');
coding = loadGif('https://media.giphy.com/media/11Yfb7wNfpIEfK/giphy.gif');
}
function setup() {
createCanvas(windowWidth, windowHeight);
strokeWeight(2);
imageMode(CENTER);
}
function draw() {
background(0);
if (mouseX < width * 0.5) {
image(coolpup, mouseX, mouseY);
} else {
image(coding, mouseX, mouseY);
}
image(brains, windowWidth / 2, windowHeight / 2); //DISPLAYS BRAINS
}
My example displays the image attached to the center of the mouse and draws either one or the other image depending on which half of the canvas you hover.