All circles being drawn in the same colour [duplicate] - javascript

This question already has answers here:
Drawing lines with canvas by using for loop
(2 answers)
Closed 2 years ago.
Shortly summarized the problem is this. I want to draw two circles on the canvas with different colors. For some reason, they are drawn in the same color, even though the console log I have placed in is switching between "green" and "blue". Sorry that some of the variable names are in my native language if that poses a problem just ask and I'll translate it.
var bodyEl = document.querySelector("body");
var canvasEl = document.createElement("canvas");
var height = window.innerHeight;
var width = window.innerWidth;
canvasEl.height = height;
canvasEl.width = width;
bodyEl.appendChild(canvasEl);
var ctx = canvasEl.getContext("2d");
var obj = [];
class ball {
constructor(radius, farge, xPosisjon, yPosisjon) {
this.x = xPosisjon;
this.y = yPosisjon;
this.rad = radius;
this.farge = farge;
}
get areal() {
let areal = "areal: " + (Math.PI * this.rad * this.rad + "px");
return (areal);
}
tegn() {
//console.log(this.farge);
ctx.fillStyle = this.farge;
ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI);
ctx.fill();
}
}
obj.push(new ball(20, "green", 100, 100));
obj.push(new ball(30, "blue", 500, 300));
setInterval(() => {
obj.forEach(x => {
x.tegn();
});
}, 30);

You need to add a ctx.beginPath().
The reason you are seeing the same color is related to the same problem found in this question: Drawing lines with canvas by using for loop. If you don't use beginPath(), you keep pushing draw commands to the same (root) path and then drawing the ever increasingly complex path.
You have to use beginPath to start a sub-path. ctx.fill() will close the sub-path. The closePath is optional.
The third, and an optional step, is to call closePath(). This method
tries to close the shape by drawing a straight line from the current
point to the start. If the shape has already been closed or there's
only one point in the list, this function does nothing.
var bodyEl = document.querySelector("body");
var canvasEl = document.createElement("canvas");
var height = window.innerHeight;
var width = window.innerWidth;
canvasEl.height = height;
canvasEl.width = width;
bodyEl.appendChild(canvasEl);
var ctx = canvasEl.getContext("2d");
var obj = [];
class ball {
constructor(radius, farge, xPosisjon, yPosisjon) {
this.x = xPosisjon;
this.y = yPosisjon;
this.rad = radius;
this.farge = farge;
}
get areal() {
let areal = "areal: " + (Math.PI * this.rad * this.rad + "px");
return (areal);
}
tegn() {
//console.log(this.farge);
ctx.beginPath();
ctx.fillStyle = this.farge;
ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI);
ctx.fill();
}
}
obj.push(new ball(20, "green", 100, 100));
obj.push(new ball(30, "blue", 500, 300));
setInterval(() => {
ctx.clearRect(0,0,500,500);
obj.forEach(x => {
x.tegn();
});
}, 1000);

your paths are being drawn in the correct colors, but your second (blue) arc is drawing on top of your first (green) one. at the top of your tegn method, add a call to ctx.beginPath() to let your canvas know that your paths should be independent.

You missed beginPath.
var bodyEl = document.querySelector("body");
var canvasEl = document.createElement("canvas");
var height = window.innerHeight;
var width = window.innerWidth;
canvasEl.height = height;
canvasEl.width = width;
bodyEl.appendChild(canvasEl);
var ctx = canvasEl.getContext("2d");
var obj = [];
class ball {
constructor(radius, farge, xPosisjon, yPosisjon) {
this.x = xPosisjon;
this.y = yPosisjon;
this.rad = radius;
this.farge = farge;
}
get areal() {
let areal = "areal: " + (Math.PI * this.rad * this.rad + "px");
return (areal);
}
tegn() {
//console.log(this.farge);
ctx.beginPath();
ctx.fillStyle = this.farge;
ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI);
ctx.fill();
}
}
obj.push(new ball(20, "green", 100, 100));
obj.push(new ball(30, "blue", 500, 300));
setInterval(() => {
obj.forEach(x => {
x.tegn();
});
}, 30);

Related

Detecting circle intersection from array

I have a canvas on which I can place circles wherever I click. I want to detect when any two circle intersect, so i am storing my coordinates in an array.
The radius of every circle is 30, so that is just hardcoded into my formula. That said, even when I place two on top of each other, it's not triggering my little filltext to let me know that it's working. I've tried many things. If someone could tell me why this isn't working, that would be appreciable. The parts where I place the dots works just fine; I just need to detect overlap.
window.onload = init;
function init() {
var canvas = document.getElementById("testCanvas");
var context = canvas.getContext("2d");
var newPath = false;
var circles = [];
canvas.onmousedown = function(e) {
newPath = true;
x = e.clientX - e.target.offsetLeft;
y = e.clientY - e.target.offsetTop;
context.moveTo(x, y);
context.beginPath();
context.arc(x, y, 30, 0, 2 * Math.PI, true);
var nextColor = randomColor();
context.fillStyle = nextColor;
context.fill();
var aCircle = [x, y];
function isIntersect(aCircle, circle) {
return Math.sqrt((aCircle[0]-circle.x) ** 2 + (aCircle[1] - circle.y) ** 2) < 30;
};
circles.forEach(circle => {
if (isIntersect(aCircle, circle)) {
context.fillText('INTERSECTED', 60, 160);
}
});
circles.push(aCircle);
context.closePath();
}
}
Multiply the radius by 2 since each circle has one...
window.onload = init;
function init() {
var canvas = document.getElementById("testCanvas");
var context = canvas.getContext("2d");
var newPath = false;
var circles = [];
canvas.onmousedown = function(e) {
newPath = true;
x = e.clientX - e.target.offsetLeft;
y = e.clientY - e.target.offsetTop;
context.moveTo(x, y);
context.beginPath();
context.arc(x, y, 30, 0, 2 * Math.PI, true);
var nextColor = '#123123' //randomColor();
context.fillStyle = nextColor;
context.fill();
var aCircle = [x, y];
function isIntersect(aCircle, circle) {
var radius = 30;
var dist = Math.hypot(aCircle[0]-circle[0], aCircle[1]-circle[1]);
return dist <= (radius * 2)
};
circles.forEach(circle => {
if (isIntersect(aCircle, circle)) {
console.log("intresected");
//context.fillText('INTERSECTED', 0, 0);
}
});
circles.push(aCircle);
context.closePath();
}
}
<canvas id='testCanvas'></canvas>

Javascript canvas path not rendering

I'm new to canvas and I'm trying to create a small game. I'm trying to render some shapes on the screen but for some reason the shapes don't appear. The methods are being called and shapes get added to the array but nothing renders on the screen.
const canvas = document.getElementsByClassName('gameContainer');
const context = canvas[0].getContext('2d');
let canvasWidth = window.innerWidth;
let canvasHeight = window.innerHeight;
let shapes = [];
class Shape {
constructor(x, y, rad) {
this.x = x;
this.y = y;
this.rad = rad;
}
createShape() {
context.beginPath();
context.arc(this.x, this.y, this.rad, 0, 2 * Math.PI, false);
context.fillStyle = '#000';
context.fill();
}
}
class CanvasState {
constructor() {
this.canvas = canvas[0];
this.canvas.height = canvasHeight;
this.canvas.width = canvasWidth;
this.shapes = shapes;
}
addShape() {
setInterval(function() {
const randomRad = randomNum(10, 100);
const shape = new Shape(randomNum(randomRad, canvasWidth - randomRad), -randomRad, randomRad);
shape.createShape(this.context);
shapes.push(shape);
}, 1000);
}
}
function randomNum(min, max) {
Math.round(Math.random() * (max - min) + min);
}
function init() {
const cvs = new CanvasState();
cvs.addShape();
}
window.onload = function() {
init();
}
Any help given is greatly appreciated, thanks!
Assuming you have a working implementation of randomNum, the following lines result in the y-coordinate always being negative:
const randomRad = randomNum(10, 100);
const shape = new Shape(randomNum(randomRad, canvasWidth - randomRad), -randomRad, randomRad);
So all the shapes get drawn off screen.

JS target function comes to constructor not working

I am working on drawing moving rectangles on my canvas. I made a template function for the test purpose and it works, but since i want to draw more of the rectangles with same animation effect I have to make this template function comes to the constructor, getContextand now the problem occurs:
the template function:
ctx = getContext('2d');
var boxHeight = canvas.height/40;
var boxWidth = canvas.width/20;
function drawBox(){
var x = 20;
var y = canvas.height;
var w = boxWidth;
var h = boxHeight;
var timer = 0;
var ladder = Math.floor(Math.random()*((canvas.height*0.5)/boxHeight)) + 1;
for(var i = 0; i < ladder; i++){
ctx.fillStyle = 'hsl('+Math.abs(Math.sin(timer) * 255)+', 40%, 50%)';
ctx.fillRect(x,y,w,h);
ctx.strokeRect(x,y,w,h);
ctx.lineWidth = 2;
ctx.stroke();
y -= boxHeight;
timer += Math.random()*0.3;
}
}
function animate(){
ctx.clearRect(0,0,canvas.width,canvas.height);
window.requestAnimationFrame(animate);
drawBox();
}
animate();
this template function drawBox()just working fine, then i tried to enclose its properties into a Box()constructor object:
function Box(x, width) {
this.postion = {
x: x,
y: canvas.height
};
this.width = width;
this.height = canvas.height / 40;
this.colorTimer = 0;
this.draw = function() {
this.colorTimer += Math.random() * 0.3;
var ladder = Math.floor(Math.random() * ((canvas.height * 0.5) / boxHeight)) + 1;
for (var i = 0; i < ladder; i++) {
ctx.fillStyle = 'hsl(' + Math.abs(Math.sin(this.colorTimer) * 255) + ', 40%, 50%)';
ctx.fillRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.lineWidth = 2;
ctx.stroke();
this.postion.y -= this.height;
}
}
}
var myBox = new Box(20, boxWidth);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
myBox.draw();
}
animate();
this is not working, i have been stuck with this about 2 hours and i don't think there is any method or properties difference between my Boxconstructor and my drawBoxobject. When it comes to myBoxobject to calling its draw()method, there is nothing pop out on the screen.
I am wondering did i just miss something important when creating Boxconstructor object? Could someone give me a hint please?
As #Todesengel mentioned, the real issue here is, you are re-initializing all the variables each time the template function (drawBox) is called. But you are not doing the same for the constructor. To resolve this, put this.colorTimer = 0 and this.postion.y = canvas.height insde the draw method (as these are the variables that need to be re-initialized).
However, there are other issues :
you are increasing the timer variable inside the for loop, in template function, but not doing the same for constructor
as #Barmar mentioned, you should define draw method as Box.prototype.draw, for efficiency (not mandatory though)
Here is the revised version of your code :
ctx = canvas.getContext('2d');
var boxHeight = canvas.height / 40;
var boxWidth = canvas.width / 20;
function Box(x, width) {
this.postion = {
x: x,
y: canvas.height
};
this.width = width;
this.height = canvas.height / 40;
}
Box.prototype.draw = function() {
this.colorTimer = 0;
this.postion.y = canvas.height;
var ladder = Math.floor(Math.random() * ((canvas.height * 0.5) / this.height)) + 1;
for (var i = 0; i < ladder; i++) {
ctx.fillStyle = 'hsl(' + Math.abs(Math.sin(this.colorTimer) * 255) + ', 40%, 50%)';
ctx.fillRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y, this.width, this.height);
ctx.lineWidth = 2;
ctx.stroke();
this.postion.y -= this.height;
this.colorTimer += Math.random() * 0.3;
}
}
var myBox = new Box(20, boxWidth);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
myBox.draw();
window.requestAnimationFrame(animate);
}
animate();
<canvas id="canvas" width="300" height="300"></canvas>
I believe the important thing to note here is that in your first case, there is a new drawBox function being called every time, with the variables being instantiated and initialized, or "reset", each time. In your second case, the myBox object is not being recreated each time, so you have left over variables. These will not behave the same way. It should work as expected if you move var myBox = new Box(20, boxWidth); into the animate function.
Another fix, if you don't want to do recreate the myBox object for each call, is to reset the left over variables after each animate call. It would be more efficient, and probably more desirable, to do it this way.
You should not modify this.position.y in the draw method.
So remove this assignment from the loop:
this.postion.y -= this.height;
... and change the following lines to dynamically add -i*this.height:
ctx.fillRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
As others have said, you should better define the method on the prototype. And colorTimer should change in the for loop. I think you could do with a local variable though.
Demo:
var ctx = canvas.getContext('2d');
var boxHeight = canvas.height/40;
var boxWidth = canvas.width/20;
function Box(x, width) {
this.postion = {
x: x,
y: canvas.height
};
this.width = width;
this.height = canvas.height / 40;
}
Box.prototype.draw = function() {
var ladder = Math.floor(Math.random() * ((canvas.height * 0.5) / boxHeight)) + 1;
var colorTimer = 0;
for (var i = 0; i < ladder; i++) {
ctx.fillStyle = 'hsl(' + Math.abs(Math.sin(colorTimer) * 255) + ', 40%, 50%)';
ctx.fillRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
ctx.strokeRect(this.postion.x, this.postion.y-i*this.height, this.width, this.height);
ctx.lineWidth = 2;
ctx.stroke();
colorTimer += Math.random() * 0.3;
}
};
var myBox = new Box(20, boxWidth);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
myBox.draw();
}
animate();
<canvas id="canvas"></canvas>

Canvas line drawing animation

I am new learner of animation using HTML5 Canvas. I am struggling to create line drawing animation in a canvas with desired length of a line.
Here is the code
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight;
var x = 200;
var y = 200;
draw();
update();
function draw() {
context.beginPath();
context.moveTo(100, 100);
context.lineTo(x, y);
context.stroke();
}
function update() {
context.clearRect(0, 0, width, height);
x = x + 1;
y = y + 1;
draw();
requestAnimationFrame(update);
}
html,
body {
margin: 0px;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
The line is growing on Canvas in the above code. But how to achieve that the 200px wide line and animate the movement in x and y direction. And the same animation with multiple lines using for loop and move them in different direction.
Check the reference image ....
Need to move each line in a different direction
Thanks in advance
Find a new reference image which i want to achieve
You need to either use transforms or a bit of trigonometry.
Transforms
For each frame:
Reset transforms and translate to center
Clear canvas
Draw line from center to the right
Rotate x angle
Repeat from step 2 until all lines are drawn
var ctx = c.getContext("2d");
var centerX = c.width>>1;
var centerY = c.height>>1;
var maxLength = Math.min(centerX, centerY); // use the shortest direction for demo
var currentLength = 0; // current length, for animation
var lenStep = 1; // "speed" of animation
function render() {
ctx.setTransform(1,0,0,1, centerX, centerY);
ctx.clearRect(-centerX, -centerY, c.width, c.height);
ctx.beginPath();
for(var angle = 0, step = 0.1; angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
ctx.lineTo(currentLength, 0);
ctx.rotate(step);
}
ctx.stroke(); // stroke all at once
}
(function loop() {
render();
currentLength += lenStep;
if (currentLength < maxLength) requestAnimationFrame(loop);
})();
<canvas id=c></canvas>
You can use transformation different ways, but since you're learning I kept it simple in the above code.
Trigonometry
You can also calculate the line angles manually using trigonometry. Also here you can use different approaches, ie. if you want to use delta values, vectors or brute force using the math implicit.
For each frame:
Reset transforms and translate to center
Clear canvas
Calculate angle and direction for each line
Draw line
var ctx = c.getContext("2d");
var centerX = c.width>>1;
var centerY = c.height>>1;
var maxLength = Math.min(centerX, centerY); // use the shortest direction for demo
var currentLength = 0; // current length, for animation
var lenStep = 1; // "speed" of animation
ctx.setTransform(1,0,0,1, centerX, centerY);
function render() {
ctx.clearRect(-centerX, -centerY, c.width, c.height);
ctx.beginPath();
for(var angle = 0, step = 0.1; angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
ctx.lineTo(currentLength * Math.cos(angle), currentLength * Math.sin(angle));
}
ctx.stroke(); // stroke all at once
}
(function loop() {
render();
currentLength += lenStep;
if (currentLength < maxLength) requestAnimationFrame(loop);
})();
<canvas id=c></canvas>
Bonus animation to play around with (using the same basis as above):
var ctx = c.getContext("2d", {alpha: false});
var centerX = c.width>>1;
var centerY = c.height>>1;
ctx.setTransform(1,0,0,1, centerX, centerY);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(0,0,0,0.8)";
ctx.shadowBlur = 16;
function render(time) {
ctx.globalAlpha=0.77;
ctx.fillRect(-500, -500, 1000, 1000);
ctx.globalAlpha=1;
ctx.beginPath();
ctx.rotate(0.025);
ctx.shadowColor = "hsl(" + time*0.1 + ",100%,75%)";
ctx.shadowBlur = 16;
for(var angle = 0, step = Math.PI / ((time % 200) + 50); angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
var len = 150 + 150 * Math.cos(time*0.0001618*angle*Math.tan(time*0.00025)) * Math.sin(time*0.01);
ctx.lineTo(len * Math.cos(angle), len * Math.sin(angle));
}
ctx.stroke();
ctx.globalCompositeOperation = "lighter";
ctx.shadowBlur = 0;
ctx.drawImage(ctx.canvas, -centerX, -centerY);
ctx.drawImage(ctx.canvas, -centerX, -centerY);
ctx.globalCompositeOperation = "source-over";
}
function loop(time) {
render(time);
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
body {margin:0;background:#222}
<canvas id=c width=640 height=640></canvas>
Here is what I think you are describing...
window.onload = function() {
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = 400,
height = canvas.height = 220,
xcenter = 200,
ycenter = 110,
radius = 0,
radiusmax = 100,
start_angle1 = 0,
start_angle2 = 0;
function toRadians(angle) {
return angle * (Math.PI / 180);
}
function draw(x1, y1, x2, y2) {
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
}
function drawWheel(xc, yc, start_angle, count, rad) {
var inc = 360 / count;
for (var angle = start_angle; angle < start_angle + 180; angle += inc) {
var x = Math.cos(toRadians(angle)) * rad;
var y = Math.sin(toRadians(angle)) * rad;
draw(xc - x, yc - y, xc + x, yc + y);
}
}
function update() {
start_angle1 += 0.1;
start_angle2 -= 0.1;
if(radius<radiusmax) radius++;
context.clearRect(0, 0, width, height);
drawWheel(xcenter, ycenter, start_angle1, 40, radius);
drawWheel(xcenter, ycenter, start_angle2, 40, radius);
requestAnimationFrame(update);
}
update();
};
html,
body {
margin: 0px;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
This is one that is a variable length emerging pattern. It has a length array element for each spoke in the wheel that grows at a different rate. You can play with the settings to vary the results:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
var xcenter = width/4;
var ycenter = height/2;
var radius;
var time;
if(width>height) {
radius = height*0.4;
}
else {
radius = width*0.4;
}
var start_angle1 = 0;
var start_angle2 = 0;
function toRadians (angle) {
return angle * (Math.PI / 180);
}
function draw(x1,y1,x2,y2) {
context.beginPath();
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.stroke();
}
var radmax=width;
var rads = [];
var radsinc = [];
function drawWheel(xc,yc,start_angle,count,rad) {
var inc = 360/count;
var i=0;
for(var angle=start_angle; angle < start_angle+180; angle +=inc) {
var x = Math.cos(toRadians(angle)) * rads[rad+i];
var y = Math.sin(toRadians(angle)) * rads[rad+i];
draw(xc-x,yc-y,xc+x,yc+y);
rads[rad+i] += radsinc[i];
if(rads[rad+i] > radmax) rads[rad+i] = 1;
i++;
}
}
function update() {
var now = new Date().getTime();
var dt = now - (time || now);
time = now;
start_angle1 += (dt/1000) * 10;
start_angle2 -= (dt/1000) * 10;
context.clearRect(0,0,width,height);
drawWheel(xcenter,ycenter,start_angle1,50,0);
drawWheel(xcenter,ycenter,start_angle2,50,50);
requestAnimationFrame(update);
}
function init() {
for(var i=0;i<100;i++) {
rads[i] = 0;
radsinc[i] = Math.random() * 10;
}
}
window.onload = function() {
init();
update();
};
html, body {
margin: 0px;
}
canvas {
width:100%;
height:200px;
display: block;
}
<canvas id="canvas"></canvas>

Drawing Object Arrays with JS Canvas

I'm new to JS, and I have not gone into jQuery or such yet. All that I'm trying to do is draw 4 simple circles on the canvas. I put the objects into an array because I planned to possibly expand in the future, but that information is irrelevant.
What I need help with, is understanding the basics of how to create objects within an array and then display them on a canvas. Here is my dysfunctionate code:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
var W = 400, H = 500
canvas.width = W, canvas.height = H;
var ball = [3];
for (i===0;i<=3; i++){
ball[i] = {
x: (Math.floor(Math.random() * 350) + 50),
y: (Math.floor(Math.random() * 450) + 50),
color: "red",
radius: (Math.floor(Math.random() * 35) + 15),
draw: balls(x, y, color, radius)
};
}
function balls(x, y, color, radius){
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2*Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
You are making it very complex use this method:
function ball(){
this.x = Math.random() * 100;
this.y = Math.random() * 100;
this.radius = Math.random() * 10;
this.color = "#" + (Math.random() * 16777215).toString(16);
this.draw = function(){
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
}
var balls = [];
for(var i = 0; i < 4; i++) {
balls[i] = new ball();
balls[i].draw();
}
You can simply assign draw to the balls function like so:
ball[i] = {
....
draw: balls
};
Then to draw it, once you defined ball[i] call it's draw() function:
var b = ball[i];
b.draw(b.x, b.y, b.color ,b.radius);
Example Here
Edit: Using no parentheses like ball, is how to reference the function ball() itself. And one last thing, you need to change for (i===0;i<=3; i++) to for (i=0;i<=3; i++). The === is a strict comparison operator, = is assignment.

Categories

Resources