I am creating a piano using p5.js. I need help with the color change. When a user presses a key, I want the key to flash a quick color change to let them know that they pressed the key.
In my code, the color does change when you click on the first key, however, when I click a little bit outside the first key, the first key still changes color.
Is my distance a little off? Or is there a more effective way to do this?
function setup() {
createCanvas(990, 600);
}
function draw() {
background(220);
fill(255);
rect(0, 300, 70, 400);
rect(70, 300, 70, 400);
rect(140, 300, 70, 400);
rect(210, 300, 70, 400);
rect(280, 300, 70, 400);
rect(350, 300, 70, 400);
rect(420, 300, 70, 400);
rect(490, 300, 70, 400);
rect(560, 300, 70, 400);
rect(630, 300, 70, 400);
rect(700, 300, 70, 400);
rect(770, 300, 70, 400);
rect(840, 300, 70, 400);
rect(910, 300, 70, 400);
fill(0);
rect(50, 300, 38, 180);
rect(120, 300, 38, 180);
rect(260, 300, 38, 180);
rect(330, 300, 38, 180);
rect(400, 300, 38, 180);
rect(540, 300, 38, 180);
rect(610, 300, 38, 180);
rect(750, 300, 38, 180);
rect(820, 300, 38, 180);
rect(890, 300, 38, 180);
text("mouse x: "+mouseX+" mouse y:"+mouseY, width/2,height-30);
}
function mousePressed() {
cursor(HAND);
}
function mouseReleased() {
cursor(ARROW);
let d = dist(mouseX, mouseY, 0, 300);
if (d < 300) {
fill(0);
rect(0, 300, 70, 400);
}
}
You're checking whether the mouse position is withing 300 pixels of the point 0,300. This sets up a circle with a center of 0,300 and a radius of 300. Try drawing that in your scene to see where your clickable area is.
Your keys are rectangles, so you should be using point-rectangle intersection to check whether the mouse is inside a particular key. Google is your friend here, but basically you want to check whether mouseX is between the left and right edges, and mouseY is between the top and bottom edges.
Finally, note that you're only ever showing the "flash" for a single frame. I personally can't even see it. You probably want to show the flash for longer, using the millis() function or the frameCount variable for timing. (Again, Google is your friend!)
Shameless self-promotion: here is a tutorial on collision detection, including point-rectangle intersection. It's for Processing, but the ideas are the same in P5.js.
Related
I have been trying to use built-in functions in p5.js but I can't figure out what I am doing wrong, the col variable won't assign to moon.brightness rather the moon is always white when I preview it, I assigned my variables col and dor before the setup function and called them in the setup function before I initialised the Moon variable, at a total loss here, new to using p5.js so any help about where I am going wrong would be appreciated
var groundHeight;
var mountain1;
var mountain2;
var tree;
var moon;
var sun;
var darkness;
var brightness;
var col;
var dor;
function setup() {
cnv = createCanvas(800, 600)
//set the groundHeight proportional to the canvas size
groundHeight = (height / 3) * 2;
//initalise the mountain objects with properties to help draw them to the canvas
mountain1 = {
x: 400,
y: groundHeight,
height: 320,
width: 230
};
mountain2 = {
x: 550,
y: groundHeight,
height: 200,
width: 130
};
//initalise the tree object
tree = {
x: 150,
y: groundHeight + 20,
trunkWidth: 40,
trunkHeight: 150,
canopyWidth: 120,
canopyHeight: 100
};
//initalise the sun
sun = {
x: 150,
y: 70,
diameter: 80
};
col = (150, 200, 255)
dor = (255, 255, 255)
//TASK: intialise a moon object with an extra property for brightness
moon = {
brightness: col,
x: 700,
y: 70,
diameter: 80
};
//set the initial darkness
darkness = {
x: 800,
y: 600,
light: col
};
}
function draw() {
//TASK: update the values for the moons brightness, the sun's position and the darkness.
//You can either map this to the mouse's location (i.e. the futher left the mouse is the more daylight) or you can just change the values gradually over time.
//draw the sky
background(150, 200, 255);
noStroke();
//draw the sun
fill(255, 255, 0);
ellipse(sun.x, sun.y, sun.diameter);
sun.y = map(mouseX, 0, 800, 70, 630)
fill(moon.brightness)
ellipse(moon.x, moon.y, moon.diameter);
moon.brightness = map(mouseX, 0, 800, col, dor);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
There are two issues here:
In JavaScript the expression (1, 2, 3) does not create a an array, or a vector, or a tuple, or anything that could be used to represent a color. The comma operator evaluates the expression on the left, and the regardless of the value of that expression, it evaluates an expression on the right.
The p5.js map() function does not work with colors, it only works with numbers. If you're working with colors you need to use lerpColor()
var groundHeight;
var mountain1;
var mountain2;
var tree;
var moon;
var sun;
var col;
var dor;
function setup() {
cnv = createCanvas(800, 600)
//set the groundHeight proportional to the canvas size
groundHeight = (height / 3) * 2;
//initalise the mountain objects with properties to help draw them to the canvas
mountain1 = {
x: 400,
y: groundHeight,
height: 320,
width: 230
};
mountain2 = {
x: 550,
y: groundHeight,
height: 200,
width: 130
};
//initalise the tree object
tree = {
x: 150,
y: groundHeight + 20,
trunkWidth: 40,
trunkHeight: 150,
canopyWidth: 120,
canopyHeight: 100
};
//initalise the sun
sun = {
x: 150,
y: 70,
diameter: 80
};
// FIXED: Use the color function to create a p5.Color object from R G and B components
col = color(150, 200, 255)
dor = color(255, 255, 255)
//TASK: intialise a moon object with an extra property for brightness
moon = {
brightness: col,
x: 700,
y: 70,
diameter: 80
};
}
function draw() {
//TASK: update the values for the moons brightness, the sun's position and the darkness.
//You can either map this to the mouse's location (i.e. the futher left the mouse is the more daylight) or you can just change the values gradually over time.
scale
//draw the sky
background(150, 200, 255);
noStroke();
//draw the sun
fill(255, 255, 0);
ellipse(sun.x, sun.y, sun.diameter);
sun.y = map(mouseX, 0, 800, 70, 630)
fill(moon.brightness)
ellipse(moon.x, moon.y, moon.diameter);
// FIXED: when interpolating between two colors, use lerpColor instead of map
moon.brightness = lerpColor(col, dor, mouseX / width);
}
html, body {
margin: 0;
padding: 0;
}
canvas {
transform: scale(0.25);
transform-origin: top left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I want to make a simple project in which the user has to select which text color looks better on a particular background. After clicking on the right background color, I should log the color and corresponding text color (0 for black, 1 for white) to a file.
This is what I have so far:
function setup(){
createCanvas(1200, 600);
}
function draw(){
background(51);
fill(0);
wBol = ellipse(300, 300, 350, 350);
fill(0);
bBol = ellipse(900, 300, 350, 350);
textSize(64);
fill(0);
text('text', 250, 320);
fill(255);
text('text', 850, 320);
}
function mousePressed(){
var d1 = dist(300, 300, mouseX, mouseY);
var d2 = dist(900, 300, mouseX, mouseY);
if(d1 <= 350/2){
console.log(wBol.fill());
wBol.fill(random(0,255), random(0,255), random(0,255));
bBol.fill(random(0,255), random(0,255), random(0,255));
}
if(d2 <= 350/2){
console.log(wBol.fill());
wBol.fill(random(0,255), random(0,255), random(0,255));
bBol.fill(random(0,255), random(0,255), random(0,255));
}
}
This doesn't work and I know 'fill' isn't supposed to work like this, but I don't have a single clue on how I should go about this.
Thanks in advance.
In p5.js, ellipses (and other shapes) aren't objects. The ellipse() function has no output, it simply draws an ellipse on the screen. Similarly with fill(), the function has no output, it simply sets the fill color for the next things you draw. So if you want to save the fill color, you should do it in another variable. You could do something like this:
let wBol = { xpos: 300, ypos: 300, xwid: 350, ywid: 350, fill: 0};
let bBol = { xpos: 900, ypos: 300, xwid: 350, ywid: 350, fill: 0};
function draw() {
background(51);
fill(wBol.fill);
ellipse(wBol.xpos, wBol.ypos, wBol.xwid, wBol.ywid);
fill(bBol.fill);
ellipse(bBol.xpos, bBol.ypos, bBol.xwid, bBol.ywid);
//... the rest
}
I'm try to show the entirety of the green circle, and only the intersecting point of the pink circle.
context
Anyone have any idea if this is possible in Javascript?
function setup() {
createCanvas(500,500);
}
function draw() {
noStroke();
fill(3, 252, 102);
ellipse(100, 100, 100, 100);
fill(255, 46, 206, 100);
ellipse(130,130, 100, 100);
}
I`m trying to add an edge to a toolbar in mxGraph. Vertex following the examples i can do it perfectly. But edges i can't do it, someone can help me? I declare a link style, but it doesn't work.
var addVertex = function(icon, w, h, style)
{
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function()
{
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('images/rectangle.gif', 100, 40, '');
addVertex('images/rounded.gif', 100, 40, 'shape=link');
addVertex('images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('images/actor.gif', 30, 55, 'shape=umlActor');
I think you can edit XML file of the toolbar you want to add edge to it, just like adding new cell to the toolbar
Though Edge is a cell object, it cannot be placed in the toolbar for drag and drop. If your intention is to create connection between vertices, you can use the following in your code and check:
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
On mouseover for any vertex (source vertex), one connection icon (With Right Arrow) will appear. Just click and drag that icon to the another vertex (Target Vertex). This will create edge between two verties
var addVertex = function(mylabel, icon, w, h, style)
{
var vertex = new mxCell(mylabel, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
addToolbarItem(graph, toolbar, vertex, icon);
};
addVertex("None",'<c:url value="/resources/js/examples/editors/images/swimlane.gif"/>', 120, 160, 'shape=swimlane;startSize=20;');
addVertex("Catagory 1",'<c:url value="/resources/js/examples/editors/images/rectangle.gif"/>', 100, 40, 'shape=rectangle');
addVertex("Catagory 2",'<c:url value="/resources/js/examples/editors/images/rounded.gif"/>', 100, 40, 'shape=rounded');
addVertex("Catagory 3",'<c:url value="/resources/js/examples/editors/images/ellipse.gif"/>', 40, 40, 'shape=ellipse');
addVertex("Catagory 4",'<c:url value="/resources/js/examples/editors/images/rhombus.gif"/>', 40, 40, 'shape=rhombus');
addVertex("Catagory 5",'<c:url value="/resources/js/examples/editors/images/triangle.gif"/>', 40, 40, 'shape=triangle');
addVertex("Catagory 6",'<c:url value="/resources/js/examples/editors/images/cylinder.gif"/>', 40, 40, 'shape=cylinder');
addVertex("Catagory 7",'<c:url value="/resources/js/examples/editors/images/actor.gif"/>', 30, 40, 'shape=actor');
// toolbar.addLine();
I've got a animated circle which looks like this:
the blue part counts down, so for example from full to nothing in 10 seconds. The orange circle is just a circle. But I want that the circle will be smaller when you click on it. So i made an onclick event for the circle.
circleDraw.node.onclick = function () {
circleDraw.animate({
stroke: "#E0B6B2",
arc: [100, 100, 100, 100, 100]
}, 500);
circleDraw.toFront();
};
That works, i've made it for both of the circles, they both become smaller but, After the 500 mili seconds the blue circle becomes big again because the timer for the blue circle got the parameters that it should be bigger.
circleDraw.animate({
arc: [100, 100, 0, 100, 500]
}, 10000);
Because the blue circle counts for 10 seconds it becomes automatically bigger again. How do I make both circles smaller but keep the timer counting down?
I was thinking of stopping the animation for the blue circle and save the remaining mili seconds of the animation draw it again smaller and start the animation again with the remaining seconds, but I don't know how to do this. But maybe i'm looking in the wrong direction and do I have to make it different.
Thanks.
All my code:
/************************************************************************/
/* Raphael JS magic
*************************************************************************/
var drawTheCircleVector = function(xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
}; /************************************************************************/
/* Make the circles
*************************************************************************/
var timerCircle = Raphael("timer", 320, 320);
var circleBg = Raphael("backgroundCircle", 320, 320);
timerCircle.customAttributes.arc = drawTheCircleVector
circleBg.customAttributes.arc = drawTheCircleVector
/************************************************************************/
/* draw the circles
*************************************************************************/
var drawMe = circleBg.path().attr({
"fill": "#FF7F66",
"stroke": 0,
arc: [160, 160, 100, 100, 140]
});
var clickOnes = true;
drawMe.node.onclick = function() {
if (clickOnes == true) {
circleDraw.animate({
arc: [100, 100, 0, 100, 100]
}, 500);
circleDraw.toFront();
drawMe.animate({
arc: [100, 100, 100, 100, 100]
}, 500);
circleDraw.toFront();
clickOnes = false;
} else {
circleDraw.animate({
arc: [160, 160, 0, 100, 150]
}, 500);
circleDraw.toFront();
drawMe.animate({
arc: [160, 160, 100, 100, 140]
}, 500);
circleDraw.toFront();
clickOnes = true;
}
};
// arc: [Xposition, Yposition, how much 1 = begin 100 = end, ? = 100, 150];
/************************************************************************/
/* Draw the circle
*************************************************************************/
var circleDraw = timerCircle.path().attr({
"stroke": "#2185C5",
"stroke-width": 10,
arc: [160, 160, 100, 100, 150]
});
circleDraw.animate({
arc: [160, 160, 0, 100, 150]
}, 9000);
window.setInterval(function() {
goToNextStopGardensPointBus210()
}, 9000);
Here is my code the timer works and if you click on the circle it will become smaller but if you click on it before the bue cirlce is done it will become big again.
UPDATE
working version of what I got on jsFiddle,
http://jsfiddle.net/hgwd92/2S4Dm/
Here's a fiddle for you..
The issue was you where redrawing the items, with new animation speeds and such. Using the transform function, you can add a scaling transform that acts independent of the draws.
circleDraw.animate({transform: 'S0.5,0.5,160,160', 'stroke-width': 5}, 500);
and then to set it back...
circleDraw.animate({transform: 'S1,1,160,160', 'stroke-width': 10}, 500);
Note you need to set the center for the blue arc (the 160, 160), or once it gets past half way the center of the arc will move and it will scale to a different position.
EDIT: Updated the fiddle and code to scale the blue line to so it looks better.