I want to make green ellipses on left side and red ellipses on right side. I use random function to fill the canvas. I use if statements for my purpose. Maybe switch case would be better for this task?
This syntax only generate pink dots, whats wrong?
var spotPos = {
x:300,
y:200
}
var spotCol = {
r:0,
g:0,
b:0
}
function setup() {
createCanvas(600,400);
background(0);
}
function draw() {
spotPos.x = random(0,width);
spotPos.y = random(0,height);
//spotCol.r = random(60,255);
noStroke()
fill(spotCol.r, spotCol.g, spotCol.b)
ellipse(spotPos.x, spotPos.y, 25, 25);
if(spotPos.x < 300) {
spotCol.b = 255;
} else if(spotPos.x > 300) {
spotCol.r = 255;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/p5.js"></script>
There were a few things preventing you from achieving your desired result.
First you drew the ellipse and then afterwards selected a color. That meant that in the next round of the draw loop, the ellipse will be drawn somewhere else but the color will still be based on the previous position.
The second problem was the assignment of RGB values:
if(spotPos.x < 300) {
spotCol.b = 255;
} else if(spotPos.x > 300) {
spotCol.r = 255;
}
You only ever assigned blue and red a value of 255 and never changed it backed. So after a few iterations you have fill(255, 0, 255) e.g. full red, no green, full blue which resulted in the pink color you were seeing.
Think of draw as a set of instructions that you repeat over and over again. You need to consider the order of your instructions and in what state you end/start each iteration of your loop. If you change some global variables how will they affect your program the next time draw is run?
I've included a working example below but feel free to experiment with your own ideas and solutions.
const spotPos = {
x: 300,
y: 200
}
const spotCol = {
r: 0,
g: 0,
b: 0
}
function setup() {
createCanvas(600, 400);
background(0);
}
function draw() {
spotPos.x = random(0, width);
spotPos.y = random(0, height);
if (spotPos.x < 300) {
spotCol.r = 0;
spotCol.g = 255;
} else if (spotPos.x > 300) {
spotCol.r = 255;
spotCol.g = 0;
}
noStroke();
fill(spotCol.r, spotCol.g, spotCol.b)
ellipse(spotPos.x, spotPos.y, 25, 25);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/p5.js"></script>
If you draw the circle first before coloring it, it won't detect the new color.
Related
I am making a basic drawing application on p5.js.
I have placed my background under the draw function as I have inserted sliders to change the rgb of the background.
Once I do this, I cannot draw however. I have a mousePressed function, which works when I move the background to setup().
Any ideas of why this may be?
let brushSize;
let white;
let redB;
let yellowB;
let blueB;
let blackB;
let greenB;
let pinkB;
let brownB;
let brushColor;
let rSlide, gSlide, bSlide;
let r, g, b;
let imgLego;
let imgBrush;
let fontHead;
let fontMid;
let x;
function preload() {
imgLego = loadImage("images/lego.png");
imgBrush = loadImage("images/paint_brush.png");
fontHead = loadFont("fonts/shizuru.ttf");
fontMid = loadFont("fonts/concertOne.ttf");
}// close preload function
function setup() {
x = 10;
createCanvas(1000, 600);
noStroke();
// create the slider for the brush size
brushSize = createSlider(1, 100, 20);
// create the sliders for red, gree, blue values
rSlide = createSlider(0, 255, 255);
gSlide = createSlider(0, 255, 255);
bSlide = createSlider(0, 255, 255);
// position the sliders
rSlide.position(x, x * 20);
gSlide.position(x, x * 22);
bSlide.position(x, x * 24);
brushSize.position(x, x * 26);
// variables to hold the colour (background)
r = 255;
g = 255;
b = 255;
// color variables for brush
redB = color(255);
whiteB = color(255,0,0);
yellowB = color(246, 236, 54);
blueB = color(0,0,255);
blackB = color(0);
greenB = color(0,170,35);
pinkB = color(255,53,184);
brownB = color(155,103,60);
brushColor = redB;
}
function draw() {
background(r, g, b);
noStroke();
// poisition the text & value of slider
textAlign(LEFT, TOP);
fill(120, 120, 255);
textFont(fontMid);
textSize(15);
text("red : " + rSlide.value(), x*2 + rSlide.width, x*20);
text("green : " + gSlide.value(), x*2 + gSlide.width, x*22);
text("blue : " + bSlide.value(), x*2 + bSlide.width, x*24);
text("brush : ", x*2 + bSlide.width, x*26);
// read the value of the slider and store it in a variable
r = rSlide.value();
g = gSlide.value();
b = bSlide.value();
// create & position the default brush to follow the mouse
//ellipse(mouseX, mouseY, brushSize.value(), brushSize.value());
// customise "background" text
fill(0);
textSize(20);
text("BACKGROUND", 20, 180);
// red "navbar"
fill(255,0,0)
rect(0,0,1000,120,0,0,50,0);
// customse "sketch" text
fill(255)
textFont(fontHead);
textSize(40);
text("SKETCH", 180, 10)
// images
image(imgBrush, 930, 40, 40, 40);
image(imgLego, 20, 15, 160, 90);
//**lego block top right corner**//
stroke(0);
strokeWeight(3)
// yellow block
fill(246, 236, 54);
rect(748,18,164,84,5)
// paint buttons
ellipseMode(CENTER);
// white button
fill(whiteB);
ellipse(770,40,30);
// red button
fill(redB);
ellipse(810,40,30);
// yellow button
fill(yellowB);
ellipse(850,40,30);
// blue button
fill(blueB);
ellipse(890,40,30);
// black button
fill(blackB);
ellipse(770,80,30);
// green button
fill(greenB);
ellipse(810,80,30);
// pink button
fill(pinkB);
ellipse(850,80,30);
// brown button
fill(brownB);
ellipse(890,80,30);
}
// create function to allow the mosue to draw the relevant colours from the lego bloack colour paletter - top right corner
function mouseDragged() {
stroke(brushColor);
strokeWeight(5);
line(mouseX,mouseY,pmouseX,pmouseY);
}
Note that the draw function is continuously executed.
This is what's happening:
mouseDragged draws a line
draw function runs in the next frame and whatever's inside this function gets re-drawn on top
So if there's a background() call inside the draw function, it will re-draw the background on top of whatever was previously drawn. Hence the order of operation is so important in processing.
In your sketch, instead of drawing the line in mouseDragged, you can to draw it inside the draw loop after background() is called. For example:
// Globally accessed variable, declared at the top
// Stores the metadata for all the lines drawn in mouseDragged
let drawnLines = []
function mouseDragged() {
drawnLines.push({
mouseX: mouseX,
mouseY: mouseY,
pmouseX: pmouseX,
pmouseY: pmouseY,
});
}
function draw() {
// Draw the background
background(255, 255, 255);
// Draw the lines drawn by the user
stroke(brushColor);
strokeWeight(5);
drawnLines.forEach(drawnLine => {
line(
drawnLine.mouseX,
drawnLine.mouseY,
drawnLine.pmouseX,
drawnLine.pmouseY);
});
}
I am currently tasked with creating shapes on a canvas of different colors using a DOM. This is my first experience with the DOM. All the HTML and JS works fine if there was 1 color for all shapes, but I am trying to edit the function in question (drawShape(canvasID)) to have an if-else statement that determines the name of the canvasID and have a color associated with it.
Below is my first attempt at differentiating between two canvasId's to display either a red or blue rectangle.
function getElement(elementName) {
var element = document.getElementById(elementName);
return element;
}
function drawShape(canvasID){
var canvas = getElement(canvasID);
alert("Canvas is" + canvas);
var ctx= canvas.getContext('2d');
//ctx.rect(25, 25, 100, 100);
//ctx.fillStyle = "red";
//ctx.fill();
if (canvas == "CANVAS1"){
ctx.rect(25, 25, 100, 100);
ctx.fillStyle = "red";
ctx.fill();
}else if (canvas == "CANVAS2"){
ctx.rect(25, 25, 100, 100);
ctx.fillStyle = "blue";
ctx.fill();
}
}
As I said the rest of the program runs fine, and if I delete the if/else statements and use the lines that are currently comments, then the program will display all the rectangles as red. I just wanted advice on differentiating the colors. Any help is appreciated!
If editing to show all the JS and HTML is necessary just let me know!
Recently had almost the same problem.
Be sure to use "ctx.beginPath();" objects separator;
You can always replace a rect with a line.
See my code for changing color lines:
function lines(color1,color2) {
for (i = 0, j = 0; i <= 8 * 20; i += 20, j++) {
ct3.beginPath();
ct3.moveTo(300, 30 + i);
ct3.lineTo(900, 30 + i);
ct3.lineWidth = 20;
ct3.strokeStyle = (j%2 != 0) ? color1 : color2;
ct3.stroke();
}
}
I am currently taking a course on Javascript at Khan Academy and Im having some trouble with one of the assignments. I have posted the question there, but I have noticed that there is less volunteers willing to help there, so I thought I`d ask here.
I have an Assignment on JS Arrays ( https://www.khanacademy.org/computing/computer-programming/programming/arrays/p/project-make-it-rain ) with the following requirements:
To make an animation of rain, it's best if we use arrays to keep track of the drops and their different properties.
Start with this simple code and build on it to make a cool rain animation. Here are some ideas for what you could do:
Add more drops to the arrays.
Make it so that the drops start back at the top once they've reached the bottom, using a conditional.
Make an array of colors, so that every drop is a different color.
Make other things rain, like snowflakes (using more shape commands) or avatars (using the image commands).
Make it so that when the user clicks, a new drop is added to the array.
Initialize the arrays using a for loop and random() function, at the beginning of the program.
Question 1)
I've got it to use a random colour when the raindrop is called, but it overwrites the previous drop's colour if you call it before the previous drop goes off screen. I've tried moving the fill function outside the loop, and around the loop to no avail. Can anyone give me some insight on this? What am I doing wrong?
Question 2)
I've got a conditional (if/else) to make the raindrop start back at the top, but it drops much slower the second time, and only repeats once. Having trouble figuring out the logic of why this is happening in order to "debug" it.
Current code:
var xPositions = [100];
var yPositions = [0];
var colors = [
color(255, 0, 0),
color(255, 128, 0),
color(255, 255, 0),
color(0, 255, 0),
color(0, 0, 255),
color(128, 0, 255)
];
background(204, 247, 255);
fill(colors[Math.floor(Math.random() * colors.length)]);
// Raindrops (random color)
draw = function() {
background(204, 247, 255);
for (var i = 0; i < xPositions.length; i++) {
if (yPositions[i] < 400) { // if the raindrop hasnt hit the bottom
noStroke();
ellipse(xPositions[i], yPositions[i], 10, 10);
yPositions[i] += 5;
} else { // when it hits the bottom, set the yPositions variable to 0 and restart
ellipse(xPositions[i], yPositions.push(0), 10, 10);
yPositions[i] += 5;
}
}
};
var mouseClicked = function() {
xPositions.push(mouseX);
yPositions.push(mouseY);
fill(colors[Math.floor(Math.random() * colors.length)]);
draw();
};
Question 1)
You need to make a fill call for each raindrop that is drawn, per iteration of the for loop inside draw. For a raindrop to maintain its color as it falls (between draw calls) you need to store its color in an additional array, and initialize the corresponding color when you create new drops.
Question 2)
Simply reset the y value in the y array to make the drop start over. I'm not sure what the ellipse call was doing in your code - see below.
// initial raindrop values
var xPositions = [100];
var yPositions = [0];
var colors = [
color(255, 0, 0),
color(255, 128, 0),
color(255, 255, 0),
color(0, 255, 0),
color(0, 0, 255),
color(128, 0, 255)
];
// initialize the first raindrop to a random color
var dropColors = [colors[Math.floor(Math.random() * colors.length)]];
background(204, 247, 255);
draw = function() {
background(204, 247, 255);
for (var i = 0; i < xPositions.length; i++) {
if (yPositions[i] < 400) { // if the raindrop hasnt hit the bottom
noStroke();
// set the fill color for this drop
fill(dropColors[i]);
ellipse(xPositions[i], yPositions[i], 10, 10);
yPositions[i] += 5;
} else { // when it hits the bottom, set the yPositions variable to 0
yPositions[i] = 5;
}
}
};
var mouseClicked = function() {
xPositions.push(mouseX);
yPositions.push(mouseY);
dropColors.push(colors[Math.floor(Math.random() * colors.length)]);
draw();
};
I want to create a mobile web page where a shape appears on the screen, the user can only traces over the outline of the shape with his/her finger and then a new shape will appear. This library has a few good examples of what I am looking to do, just with more shapes. I have already found a couple of good examples for drawing on the canvas on a touch device here and here. The thing I don't know is how to constrain the line so you are only drawing on the path with a single continuous line. Is there something built in that will let me specify the only path you can draw, or do I have to write that logic by hand?
We can split the issue into two parts :
1) knowing if the user is on the path.
2) knowing if the user went on all path parts.
For 1), we can use the isPointInPath context2D method to know if the mouse/touch point (x,y) is on the curve. The constraint here is that you must build a closed surface, meaning a surface drawn by a fill(), not one built with a stroke(). So in case you are stroking thick lines, you have to do some little math to build the corresponding figures out of moveTo+lineTo+fill.
For 2) : build a list of 'check-points' for your shape. You might have, for instance 8 control points for a circle. Then decide of a distance at which the user will 'activate' the check point. Now the algorithm is, in pseudo-code:
mouseDown => check()
mouseMove => if mouse is down, check()
checkPointList = [ [ 10, 40, false ] , [ centerX, centerY, isChecked], ... ] ;
checked = 0;
function check() {
clear screen
draw the path
if (mouse down and mouse point on path) {
for ( checkPoint in CheckPointList) {
if (checkPoint near enough of mouse) {
checkPoint[3]=true;
checked++;
}
}
if (checked == checkPointList.length) ==>>> User DID draw all the shape.
} else
clear the flags of the checkPointList;
checked=0;
}
I did a moooost simple demo here, which quites work.
The control points are shown in red when disactivated, green when activated :
http://jsbin.com/wekaxiguwiyo/1/edit?js,output
// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
function draw() {
ctx.clearRect(0,0,300,300);
drawShape();
drawCP();
}
// Shape
function drawShape() {
ctx.beginPath();
ctx.moveTo(30,5);
ctx.lineTo(80,5);
ctx.lineTo(80, 300);
ctx.lineTo(30,300);
ctx.closePath();
ctx.lineWidth= 16;
ctx.fillStyle='#000';
ctx.fill();
}
// Control points
var points = [ [50, 50, false], [50,120, false], [50, 190, false],[50,260, false ] ];
var pointsCount = 0;
function drawCP() {
for (var i=0; i<points.length; i++) {
var p = points[i];
ctx.fillStyle=p[2]?'#0F0':'#F00';
ctx.fillRect(p[0]-1, p[1]-1, 2, 2);
}
}
function resetCP() {
for (var i=0; i<points.length; i++) {
points[i][2]=false;
}
pointsCount=0;
}
function testCP(x,y) {
var d=30;
d=sq(d);
for (var i=0; i<points.length; i++) {
if (sq(points[i][0]-x)+sq(points[i][1]-y)<d) {
if (!points[i][2]) pointsCount++;
points[i][2]=true
};
}
}
function sq(x) { return x*x; }
//
draw();
// most simple event handling
addEventListener('mousemove', mouseMove);
var r = cv.getBoundingClientRect();
function mouseMove(e) {
var x = e.pageX-r.left;
var y = e.pageY-r.top;
draw();
ctx.fillStyle='#000';
if (ctx.isPointInPath(x,y)) {
ctx.fillStyle='#F00';
testCP(x,y);
} else {
resetCP();
}
ctx.fillRect(x-3,y-3,6,6);
var pathDrawn = (pointsCount == points.length);
if (pathDrawn) ctx.fillText('Shape drawn!!', 150, 150);
}
So I want a rectangle of my canvas to change the color from black to yellow, to show that this same part have received an information. There is two rectangles from 2 diferent paths, and I want them to change the color in a random way.
If one rectangle is "0" and the other is "1", for example, when I press the button "Begin", math.random() will choose it to be 1 and rectangle "1" will change it's color from black to yellow. The loop will happen again and if math.random() choose it to be 0, now it's the rectangle 0 that will change it's color. And so on, until I press the "End" button.
This is what I tried to write on javascript:
iniciarButton.onclick = function (e) {
iniciarButton.disabled = true;
pararButton.disabled = false;
for (;;) {
var numAleat = Math.floor(Math.random() * 2);
if (numAleat === 1) {
context.fillStyle = "yellow";
context.fillRect(715,140,10,15);
context.fillStyle = "black";
context.fillRect(642,80,15,10);
} else {
context.fillStyle = "yellow";
context.fillRect(642,80,15,10);
context.fillStyle = "black";
context.fillRect(715,140,10,15);
}
if (iniciarButton.disabled === false) break;
}
};
pararButton.onclick = function (e) {
pararButton.disabled = true;
iniciarButton.disabled = false;
};
The problem is that it isn't working the way I expected it. When I press the "begin" button it enters on a loop that ends on one rectangle yellow and the other black and not like blinking in a random way.