Clear canvas after ctx.arc was clicked - javascript

So the objective is to place a circle randomly on the screen, and if the circle was clicked, remove this old circle and make a new circle.
I'm having a problem with removing the old point and making a new one. Instead of removing the old one, it keeps it, makes a new circle and eventually does whatever this is after a bit of clicking:
This is how my code looks like:
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.cursor = "crosshair";
function randSpot() {
var X = Math.floor(Math.random() * canvas.width) + 10;
var Y = Math.floor(Math.random() * canvas.height) + 10;
ctx.arc(X, Y, 5.5, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
document.body.addEventListener('click', function(e) {
if (ctx.isPointInPath(e.clientX, e.clientY)) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
randSpot()
}
});
}
randSpot()
What am I doing wrong and how can I fix it?

You forgot to begin a new path
ctx.beginPath();
I had to modify the code to make it run in the snippet
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.cursor = "crosshair";
function randSpot() {
var X = Math.floor(Math.random() * canvas.width) + 10;
var Y = Math.floor(Math.random() * canvas.height) + 10;
ctx.beginPath();
ctx.arc(X, Y, 5.5, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
}
randSpot()
document.body.addEventListener('click', function(e) {
// I had to remove the following condition
// ctx.isPointInPath(e.clientX, e.clientY)
// because the code didn't wanted to work with the snippet
// but it's unrelated to the problem
ctx.clearRect(0, 0, canvas.width, canvas.height);
randSpot()
});

Related

Filling in two colors while using the rect() method

I was trying to make two different shapes that are different colors but it isn't working. Both of the shapes are the same colors. Please help!(Please note that I am not the best coder in the world)
I've looked for other examples on this website, but all of them use the lineTo() method and I would like to use the rect() method just to make things easier.
//make canvas and set it up
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
canvas.style.position = 'absolute';
canvas.style.left = '0px';
canvas.style.top = '0px';
canvas.style.backgroundColor = '#D0C6C6';
var cH = canvas.height;
var cW = canvas.width;
//draw paddles
//variables
var paddleLength = 120;
var redPaddleY = window.innerHeight / 2;
var bluePaddleY = window.innerHeight / 2;
var paddleWidth = 20;
//drawing starts
function drawPaddles() {
//RED PADDLE
var redPaddle = function(color) {
ctx.fillStyle = color;
ctx.clearRect(0, 0, cW, cH);
ctx.rect(cH / 12, redPaddleY - paddleLength / 2, paddleWidth, paddleLength);
ctx.fill();
};
//BLUE PADDLE
var bluePaddle = function(color) {
ctx.fillStyle = color;
ctx.clearRect(0, 0, cW, cH);
ctx.rect(cH / 12 * 14, bluePaddleY - paddleLength / 2, paddleWidth, paddleLength);
ctx.fill();
};
redPaddle('red');
bluePaddle('blue');
};
var interval = setInterval(drawPaddles, 25);
Whenever you add a shape to the canvas it becomes part of the current path. The current path remains open until you tell the canvas to start a new one with beginPath(). This means that when you add your second rect() it is combined with the first and filled with the same colour.
The simplest fix would be to use the fillRect() function instead of rect which begins, closes and fills a path in one call.
var redPaddle = function(color) {
ctx.fillStyle = color;
ctx.fillRect(cH / 12, redPaddleY - paddleLength / 2, paddleWidth, paddleLength);
};
If you still want to use rect() you should tell the canvas to begin a new path for each paddle.
var redPaddle = function(color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.rect(cH / 12, redPaddleY - paddleLength / 2, paddleWidth, paddleLength);
ctx.fill();
};
I would also suggest moving the clearRect() outside of the drawing functions too. Clear once per frame and draw both paddles.
...
ctx.clearRect(0, 0, cW, cH);
redPaddle();
bluePaddle();
...
You should also investigate requestAnimationFrame() to do your animation loop as it provides many performance improvements over intervals.

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>

Javascript no shapes appear

I'm trying to just make a rectangle but nothing will appear, just the background. I've tried ctx.fill, ctx.fillStyle etc. nothing works:
I'm refering to this part
fill(77, 66, 66);
rect(10,200,100,100);
Here is the whole code for the page
var ctx, W, H;
window.onload = function() {
var canvas = document.getElementById("canvas");
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W;
canvas.height = H;
ctx = canvas.getContext("2d");
setInterval(draw, 1);
function draw() {
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "#E6E6FF"; // this part does appear
ctx.fillRect(0, 0, W, H);
fill(77, 66, 66); // this doesn't appear
rect(10,200,100,100);
}
}
Thanks
You need to call fill and rect on the canvas context.
Also you need to change the fillStyle otherwise you're drawing a rectangle with the same color as the background and it won't show.
var ctx, W, H;
window.onload = function() {
var canvas = document.getElementById("canvas");
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W;
canvas.height = H;
ctx = canvas.getContext("2d");
setTimeout(draw, 1);
function draw() {
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "#E6E6FF";
ctx.fillRect(0, 0, W, H);
ctx.fillStyle = "red"; // need this otherwise the rect will be the same color as the background
ctx.rect(10, 200, 100, 100); // needs to be called on the canvas context
ctx.fill(); // needs to be called on the canvas context, it will fill any path not already filled in.
}
}
You are filling both areas with the same color, and you have to use the context to perform fill functions. you also need to create the rect BEFORE you fill it.
Try this on for size: https://jsfiddle.net/szbk6f67/3/
var ctx, W, H;
window.onload = function () {
var canvas = document.getElementById("canvas");
W = 400;
H = 400;
canvas.width = W;
canvas.height = H;
ctx = canvas.getContext("2d");
setInterval(draw, 1);
function draw() {
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = 'gray';
ctx.fillRect(0, 0, W, H);
ctx.fillStyle = 'black';
ctx.rect(10, 200, 100, 100);
ctx.fill();
}
}

'loading circle' through Canvas

Alright guys, I'm sure this has been asked before, but I couldn't find anything that directly related to what I was doing. So I have these 4 self drawing circles (or gauges.) Each one has it's own value, and I've been looking through just nit picking through codes and books to build this. My question I need to figure out is how I would go about putting in a count up? Basically I want a counter to go from 1 - x (x being the degree of the circle it's in). I've included my js and HTML 5 for you guys to look at.
HTML
<canvas id="a" width="300" height="300"></canvas>
<script>
var canvas = document.getElementById('a');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 1.5 * Math.PI;
var endAngle = 3.2 * Math.PI;
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = 15;
// line color
context.strokeStyle = 'black';
context.stroke();
</script>
Canvas.JS
$(document).ready(function(){
function animate(elementId, endPercent) {
var canvas = document.getElementById(elementId);
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var curPerc = 0;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 15;
context.strokeStyle = '#85c3b8';
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
function render(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
render(curPerc / 100);
});
}
}
render();
}
$(window).scroll(function(){
if($(this).scrollTop()<1600){
animate('a', 85);
animate('b', 95);
animate('c', 80);
animate('d', 75);
}
});
});
Keep in mind that I am very new to canvas, I appreciate all the help guys!
Demo: http://jsfiddle.net/m1erickson/mYKp5/
You can save your gauges as objects in an array:
var guages=[];
guages.push({ x:50, y:100, radius:40, start:0, end:70, color:"blue" });
guages.push({ x:200, y:100, radius:40, start:0, end:90, color:"green" });
guages.push({ x:50, y:225, radius:40, start:0, end:35, color:"gold" });
guages.push({ x:200, y:225, radius:40, start:0, end:55, color:"purple" });
The render function takes a guage object draws its progress
function render(guage,percent) {
var pct=percent/100;
var extent=parseInt((guage.end-guage.start)*pct);
var current=(guage.end-guage.start)/100*PI2*pct-quart;
ctx.beginPath();
ctx.arc(guage.x,guage.y,guage.radius,-quart,current);
ctx.strokeStyle=guage.color;
ctx.stroke();
ctx.fillStyle=guage.color;
ctx.fillText(extent,guage.x-15,guage.y+5);
}
And the animation loop asks render to draw all gauges from 0-100 percent of their full values
function animate() {
// if the animation is not 100% then request another frame
if(percent<100){
requestAnimationFrame(animate);
}
// redraw all guages with the current percent
drawAll(percent);
// increase percent for the next frame
percent+=1;
}
function drawAll(percent){
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw all the guages
for(var i=0;i<guages.length;i++){
render(guages[i],percent);
}
}

drawing canvas works as long i use it one time

I am drawing a canvas and rotating it based on a value, it works if i use the canvas one time on a page.
If i add it the second time to the page, only the last one gets drawn, i cant find the error in my code and i dont get a js error.
i think the problem is in the next function:
function animate(){
function drawnumbers()
{context.save();
context.fillStyle = "#000000";
context.translate(73,0);
context.font="10px Orbitron";
context.textAlign = "center";
context.rotate(((i*(180/min)))*Math.PI/180);
context.fillText(data.values[i].amount,0,3);
context.restore();
};
if (d < defer){
context.clearRect(0,0,400,400);
d++;
context.save();
var ang = ((((d-minn)*(180/angle)))*(Math.PI/180));
context.translate(38,39);
context.scale(.8,.8);
base_image = new Image();
base_image.src = 'http://oi44.tinypic.com/2hfkx8p.jpg';
context.translate(base_image.width/2, base_image.height/2);
context.rotate(ang );
context.drawImage(base_image, -base_image.width/2, -base_image.height/2);
context.restore();
context.save();
context.beginPath();
context.arc(100,100,64,1*Math.PI,2*Math.PI, false);
context.lineWidth = .4;
context.strokeStyle="#00A1DE";
context.globalAlpha = 0.7;
context.stroke();
context.restore();
context.save();
context.translate(100,100);
context.rotate(Math.PI/180);
context.strokeStyle = "#00A1DE";
context.lineWidth = .7;
for (var i=0;i < data.values.length; i++){
context.beginPath();
context.moveTo(62,0);
context.lineTo(67,0);
context.stroke();
context.globalAlpha = 0.7;
drawnumbers();
context.rotate((182/(min))*(-Math.PI/180));
}
context.restore();
context.fillStyle="white";
context.fillRect(38,101,123,75);
context.save();
context.fillStyle = "#00a1de";
context.font = "22px Orbitron";
context.textAlign = "center";
context.fillText(defer, 100, 90);
context.restore();
context.save();
context.fillStyle = "#000000";
context.font = "10px arial";
context.textAlign = "center";
context.fillText(eenheid, 100, 115);
context.restore();
}
else
{
clearTimeout(t);
};
t=setTimeout("animate()",30-d);
};
check example to better understand:
http://jsbin.com/ogEgURu/1/
I had it in a function but it remains the same problem so i think something is wrong with my code.
Can anyone see the problem i am not seeing ?
Your code is way too complex, especially since there is no good reason for this complexity.
Copying a big (>200) lines block of code to duplicate a functionality is error-prone.
You'll be able to see easily the issue once you refactored your code.
Just a few hints :
Very easy one : beautify the code.
No redundancy : If a code lies here twice or more, make a function and factorize.
Break down the code into smaller parts. For example : drawText(context, text, x,y, font ) (to print eenheid and defer), drawNumbers(context), drawRotatingImage(context, angle), ...
use closePath() each time you beginPath();
load once the image when page loads, and wait for it to be loaded before animating.
do not define a function in a loop (drawnumbers).
use a single object to store the several parameters (context, angle, ...), or
even switch to an object oriented style.
have only one animate() loop, that will call several draw(...) functions if need be.
after all this, your code will look much simpler, and the bug should vanish very quickly.
I did this work (partially), in this fiddle :
http://jsfiddle.net/gamealchemist/ztczK/1/ (edited)
The code looks like :
// parameters : settings for one gauge display
var parameters1 = {
data: data,
defer: '520',
context: context,
left: 38,
top: 30,
d: 0,
angle: 0,
scale: 0.8,
//... whatever parameter here
};
var parameters2 = ... ;
split the draw into many functions so it's much simpler to understand :
// draws a gauge
function drawGauge(param) {
preDraw(param);
drawBaseImage(param);
drawArc(param);
drawTheNumbers(param);
writeDefer(param);
writeEenheid(param);
postDraw(param);
}
// translate and scales context, and updates some values for the gauge
function preDraw(param) {
var minn = param.data.values[param.data.values.length - 1].amount;
var maxn = data.values[0].amount;
var angle = maxn - minn;
var d = param.d;
param.ang = ((((d - minn) * (180 / angle))) * (Math.PI / 180));
var ctx = param.context;
ctx.save();
ctx.translate(param.left, param.top);
ctx.scale(param.scale, param.scale);
context.fillStyle = "white";
context.fillRect(0, 60, 123, 75);
}
// restore context
function postDraw(param) {
var ctx = param.context;
ctx.restore();
param.d++;
}
function drawBaseImage(param) {
var ctx = param.context;
var ang = param.ang;
ctx.save();
ctx.translate(base_image.width / 2, base_image.height / 2);
ctx.rotate(ang);
ctx.drawImage(base_image, -base_image.width / 2, -base_image.height / 2);
ctx.restore();
}
function drawArc(param) {
var ctx = param.context;
ctx.save();
ctx.beginPath();
ctx.arc(base_image.width / 2, base_image.height / 2, 64, 1 * Math.PI, 2 * Math.PI, false);
ctx.lineWidth = .4;
ctx.strokeStyle = "#00A1DE";
ctx.globalAlpha = 10.7;
ctx.stroke();
ctx.restore();
}
function writeDefer(param) {
var ctx = param.context;
var defer = param.defer;
ctx.save();
ctx.fillStyle = "#00a1de";
ctx.font = "22px Orbitron";
ctx.textAlign = "center";
ctx.fillText(defer, base_image.width / 2, base_image.height / 2);
ctx.restore();
}
function writeEenheid(param) {
var ctx = param.context;
ctx.save();
ctx.fillStyle = "#000000";
ctx.font = "10px arial";
ctx.textAlign = "center";
ctx.fillText(eenheid, base_image.width / 2, base_image.height / 2 + 20);
ctx.restore();
}
function drawTheNumbers(param) {
var ctx = param.context;
var dataValues = param.data.values;
var count = dataValues.length;
ctx.save();
ctx.translate(base_image.width / 2, base_image.height / 2);
ctx.rotate(Math.PI / 180);
ctx.strokeStyle = "#00A1DE";
ctx.lineWidth = .7;
ctx.fillStyle = "#000000";
ctx.font = "10px Orbitron";
ctx.textAlign = "center";
ctx.globalAlpha = 0.7;
for (var i = 0; i < count; i++) {
ctx.beginPath();
ctx.moveTo(62, 0);
ctx.lineTo(67, 0);
ctx.stroke();
ctx.closePath();
ctx.fillText(dataValues[i].amount, 60, 3);
ctx.rotate(-Math.PI / count);
}
context.restore();
}
then animate becomes very simple, even with several gauges :
function animate() {
context.clearRect(0, 0, canvasWidth, canvasHeight);
drawGauge(parameters1);
drawGauge(parameters2);
setTimeout(animate, 15);
};
base_image.onload = animate();

Categories

Resources