deleting a circle made in HTML5 Canvas [duplicate] - javascript

This question already has answers here:
Erasing previously drawn lines on an HTML5 canvas
(5 answers)
Closed 4 years ago.
I've made a canvas where I draw shapes on. When I want to delete them, I basically create the same shape again but it's white, so I don't delete any other shapes (saved the x and y coordinate, so nothing to worry there)
ctx.fillStyle="#FFFFFF";
ctx.strokeStyle="#FFFFFF";
ctx.beginPath();
ctx.arc(x, y, 40, 0, 2 * Math.PI);
ctx.fill();
The problem is that on some shapes there is still a remaining black rest, that I can't get rid of (it's even worse on other shapes)
What Am I missing?
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(50,50 , 40, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle="#FFFFFF";
ctx.strokeStyle="#FFFFFF";
ctx.beginPath();
ctx.arc(50,50 , 40, 0, 2 * Math.PI);
ctx.fill();
<canvas id="myCanvas" width="1000" height=600 style="border: 1px solid #000000;">
</canvas>
EDIT: https://jsfiddle.net/sfj5y091/3/
EDIT 2:
I solved this problem in the end, by completely redrawing all the shapes after one shape was deleted in the system, which even enabled the deletion of shapes that overlapped without destroying the other shape

The residue is caused by smoothing or anti-aliasing.
To draw the black circle on the initially white background, an "aura" of gray pixels is drawn at the edge of the 40-pixel-radius circle to give it a smooth appearance, and that "aura" is a tiny bit bigger than what you planned to draw.
If you then draw a white 40-pixel-radius circle on top of that, it blends the new white edge pixels with what is now a non-white background. The result is lighter gray pixels, and not white pixels.
If your only option is still to paint over old pixels, then you'll have to use a slightly bigger radius for the white circle:
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(50, 50, 40, 0, 2 * Math.PI); // radius of 40
ctx.fill();
ctx.fillStyle = "#FFFFFF";
ctx.strokeStyle = "#FFFFFF";
ctx.beginPath();
ctx.arc(50, 50, 41, 0, 2 * Math.PI); // radius of 41
ctx.fill();
<canvas id="myCanvas" width="150" height="150" style="border: 1px solid #000000;">
</canvas>
<br /> Nothing to see here ;-)
For more on anti-aliasing, see e.g. Can I turn off antialiasing on an HTML <canvas> element?

Double buffering in action
Try
step1();
setTimeout(function () {
step2();
setTimeout(function () {
step3();
}, 1000);
}, 1000);
function step1() {
clearCanvas('myCanvas1');
drawShape('myCanvas1'
,{type:"circle", strokeStyle:"#000000", fillStyle:"#000000", radious:40, x:50, y:50});
};
function step2() {
clearCanvas('myCanvas2');
showOtherCanvas('myCanvas2', 'myCanvas1');
};
function step3() {
clearCanvas('myCanvas1');
drawShape('myCanvas1'
,{type:"circle", strokeStyle:"#000000", fillStyle:"#000000", radious:40, x:50, y:50});
showOtherCanvas('myCanvas1', 'myCanvas2');
};
function drawCircle (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle=info.fillStyle;
ctx.strokeStyle=info.strokeStyle;
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.stroke();
}
function showOtherCanvas(cnv1, cnv2) {
var c1 = document.getElementById(cnv1);
var c2 = document.getElementById(cnv2);
c1.style['z-index'] = 3;
c2.style['z-index'] = 1;
c1.style['z-index'] = 2;
}
function clearCanvas(canvasID) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle="#FFFFFF";
ctx.strokeStyle="#FFFFFF";
ctx.fillRect(0,0,640,400);
}
function drawShape (canvasID, info) {
switch (info.type) {
case "circle" : drawCircle(canvasID, info);
}
}
<canvas id="myCanvas2" width="640" height="400"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:1">
</canvas>
<canvas id="myCanvas1" width="640" height="400"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:2">
</canvas>
The change is so fast you won't see any flicker.

Related

Canvas stroke and fill color [duplicate]

This question already has an answer here:
html 5 canvas LineTo() line color issues
(1 answer)
Closed 3 years ago.
Problem is that stroke opacity is lower than fill opacity and I can't get stroke color to be the same opacity as fill color.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.strokeStyle = "rgba(255,0,0,1)";
ctx.fillStyle = "rgba(255,0,0,1)";
ctx.strokeRect(20, 20, 25, 25);
ctx.fillRect(20, 50, 25, 25);
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
If we set fillStyle opacity 0.5, than they will be the same, but we can't raise opacity of stroke.
So how set stroke color to be the same as fill color?
They have exactly the same color. The problem is the antialiasing.
You can check that the stroke looks darker if you make it wider.
ctx.lineWidth = 5;
It will have an intense red in the central part of the stroke but a non-saturated one in the edges.
Sadly, you can control it (i.e. turn it off) because it depends on the browser.
You can read more about it (and look for alternatives) in this other question.
The trick that was working better for me is to translate the canvas by half-pixel distance.
ctx.translate(0.5, 0.5);
It was suggested here and here. I can not be sure if is going to achieve the desired effect in every browser though (i tested it on Firefox).
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.strokeStyle = "rgba(255,0,0,1)";
ctx.fillStyle = "rgba(255,0,0,1)";
ctx.translate(0.5, 0.5);
ctx.strokeRect(20, 20, 25, 25);
ctx.fillRect(20, 50, 25, 25);
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">Your browser does not support the HTML5 canvas tag.</canvas>

Cannot get html canvas to size properly using jquery

I'm trying to manipulate a canvas using jquery, but no matter how I set the size something comes out different than when manipulating the html directly.
var size = 100;
var halfSize = size / 2;
drawCircle1();
drawCircle2();
drawCircle3();
// behaves as expected
function drawCircle1() {
var canvas = document.getElementById('c1');
var ctx = canvas.getContext("2d");
ctx.translate(halfSize, halfSize);
ctx.arc(0, 0, halfSize, 0, 2 * Math.PI);
ctx.fill();
}
// draws a round circle, but the canvas size is not right
function drawCircle2() {
var canvas = $('#c2')
var ctx = canvas.get(0).getContext("2d");
ctx.translate(halfSize, halfSize);
ctx.arc(0, 0, halfSize, 0, 2 * Math.PI);
ctx.fill();
}
// canvas sized correctly but circle stretched
function drawCircle3() {
var canvas = $('#c3')
canvas.height(size);
canvas.width(size);
var ctx = canvas.get(0).getContext("2d");
ctx.translate(halfSize, halfSize);
ctx.arc(0, 0, halfSize, 0, 2 * Math.PI);
ctx.fill();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='c1' width=100 height=100 style="border:solid 1px red"></canvas>
<canvas id='c2' width=100 height=100 style="border:solid 1px green"></canvas>
<canvas id='c3' style="border:solid 1px blue"></canvas>
In the second example, I think height is just misspelled.
In the third example, jQuery is setting the CSS height and width. Doing so doesn't change the actual size of the canvas, so the canvas is distorted. Instead, set the width and height attributes, as shown below.
Also see Canvas width and height in HTML5.
var size = 100;
var halfSize = size / 2;
drawCircle1();
drawCircle2();
drawCircle3();
// behaves as expected
function drawCircle1() {
var canvas = document.getElementById('c1');
var ctx = canvas.getContext("2d");
ctx.translate(halfSize, halfSize);
ctx.arc(0, 0, halfSize, 0, 2 * Math.PI);
ctx.fill();
}
// draws a round circle, but the canvas size is not right
function drawCircle2() {
var canvas = $('#c2')
var ctx = canvas.get(0).getContext("2d");
ctx.translate(halfSize, halfSize);
ctx.arc(0, 0, halfSize, 0, 2 * Math.PI);
ctx.fill();
}
// canvas sized correctly but circle stretched
function drawCircle3() {
var canvas = $('#c3')
canvas.attr('width', size);
canvas.attr('height', size);
var ctx = canvas.get(0).getContext("2d");
ctx.translate(halfSize, halfSize);
ctx.arc(0, 0, halfSize, 0, 2 * Math.PI);
ctx.fill();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='c1' width=100 height=100 style="border:solid 1px red"></canvas>
<canvas id='c2' width=100 height=100 style="border:solid 1px green"></canvas>
<canvas id='c3' style="border:solid 1px blue"></canvas>

How to set 2 images to be by the side of each other

I have made use of a canvas to create a gauge and it is displaying perfectly on the web browser. However, I am planning to display 2 gauges that are side by side of each other; one gauge is to reflect on the speed of the user interaction, the other gauge is to serve as a purpose of the counter. Furthermore, the gauge will be activated upon the initial user interaction on the start button.
Issue:
At this point in time, I have managed to create and correctly display the first gauge. Hence, to create 2 canvas images, I have created 2 <canvas> tags, and when I created the second <script> tag for the second <canvas>, the 2nd canvas image is superimposed on the 1st <canvas> tag. Therefore, I wouldn't be able to see the 1st canvas image.
Hence, I would like to ask for help on how to enable the 2 canvas image to be by the side of each other?
Code:
I have removed the <script> code for the creation of the 2nd canvas, it might not have been correct to begin with, hence i have removed it.
HTML
<canvas id="canvas" width="300" height="300">
</canvas>
<canvas id="Counter" width="300" height="300">
</canvas>
CSS
#canvas {
display: block;
width: 300px;
margin: 100px auto;
}
/*Custom font for numbers*/
#font-face {
font-family: "bebas";
}
JAVASCRIPT
window.onload = function(){
//canvas initialization
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//dimensions
var W = canvas.width;
var H = canvas.height;
//Variables
var degrees = 0;
var new_degrees = 0;
var difference = 0;
var color = "#ffa500"; //green looks better to me
var bgcolor = "#654321";
var text;
var animation_loop, redraw_loop;
function init()
{
//Clear the canvas everytime a chart is drawn
ctx.clearRect(0, 0, W, H);
//Background 360 degree arc
ctx.beginPath();
ctx.strokeStyle = bgcolor;
ctx.lineWidth = 30;
ctx.arc(W/2, H/2, 100, 0, Math.PI*2, false); //you can see the arc now
ctx.stroke();
//gauge will be a simple arc
//Angle in radians = angle in degrees * PI / 180
var radians = degrees * Math.PI / 180;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 30;
//The arc starts from the rightmost end. If we deduct 90 degrees from the angles
//the arc will start from the topmost end
ctx.arc(W/2, H/2, 100, 0 - 90*Math.PI/180, radians - 90*Math.PI/180, false);
//you can see the arc now
ctx.stroke();
//Lets add the text
ctx.fillStyle = color;
ctx.font = "50px bebas";
text = Math.floor(degrees/360*100) + "ms";
//Lets center the text deducting half of text width from position x
text_width = ctx.measureText(text).width;
//adding manual value to position y since the height of the text cannot be measured easily. There are hacks but we will keep it manual for now.
ctx.fillText(text, W/2 - text_width/2, H/2 + 15);
}
function draw()
{
//Cancel any movement animation if a new chart is requested
if(typeof animation_loop != undefined) clearInterval(animation_loop);
//random degree from 0 to 360
new_degrees = Math.round(Math.random()*360);
difference = new_degrees - degrees;
animation_loop = setInterval(animate_to, 1000/difference);
}
//function to make the chart move to new degrees
function animate_to()
{
//clear animation loop if degrees reaches to new_degrees
if(degrees == new_degrees)
clearInterval(animation_loop);
if(degrees < new_degrees)degrees++;
else
degrees--;
init();
}
//Lets add some animation for fun
draw();
//Draw a new chart every 2 seconds
redraw_loop = setInterval(draw, 2000);
}
You haven't given your second canvas any styles.
Make them both inline-block, they will be next to eachother.
http://jsbin.com/vabevadoto/edit?html,css,js,output
I added a red outline so you could see the second canvas container is indeed there and next to the other once you make them inline-blocks.
#canvas {
display: inline-block;
width: 300px;
margin: 100px auto;
}
#Counter {
display: inline-block;
width: 300px;
margin: 100px auto;
outline: 1px solid red;
}
This should do the job for you
<canvas id="canvas" width="300" height="300" style="float:left; display:block;">
</canvas>
<canvas id="Counter" width="300" height="300" style="float:left; display:block;">
</canvas>

JavaScript canvas arc small angle ignore

For some reason when using small angle on arc function in canvas 2d context
It is not shown, even though angle is about 1deg, which is not even that small
How does one fix or overcomes this issue? (I'm using chrome, just in case)
Example:
var c = document.getElementById("canvas").getContext("2d");
//THIS IS SHOWN
c.beginPath();
c.moveTo(200, 200);
c.arc(200, 200, 200, 0, 0.023);
c.lineTo(200, 200);
c.fill();
//THIS IS NOT SHOWN???
c.beginPath();
c.moveTo(200, 200);
c.arc(200, 200, 200, Math.PI, Math.PI + 0.022); //change 22 to 23 to "fix"
c.lineTo(200, 200);
c.fill();
#canvas {
background-color: lightblue;
}
<canvas id="canvas" width="400" height="400"></canvas>

Erasing previously drawn lines on an HTML5 canvas

To play around with HTML5 canvas, I decided to make an app which draws an analogue clockface. Everything's fine, except that old lines don't get erased in the way that I would expect. I've included part of the code below - DrawHands() gets called once a second:
var hoursPoint = new Object();
var minutesPoint = new Object();
var secondsPoint = new Object();
function drawHands()
{
var now = new Date();
drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "white", 1);
var seconds = now.getSeconds();
secondsPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * seconds, 0.75 * radius);
drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "black", 1);
drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "white", 3);
var minutes = now.getMinutes();
minutesPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * minutes, 0.75 * radius);
drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "black", 3);
drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "white", 3);
var hours = now.getHours();
if (hours >= 12) { hours -= 12; } // Hours are 0-11
hoursPoint = getOtherEndOfLine(centerX, centerY, (2 * Math.PI / 12 * hours) + (2 * Math.PI / 12 / 60 * minutes), 0.6 * radius);
drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "black", 3);
}
To make sense of the above, there are two helper functions:
drawLine(x1, y1, x2, y2, color, thickness)
getOtherEndOfLine(x, y, angle, length)
The problem is that while all the hands get drawn as expected in black, they never get erased. I would expect that since the same line is drawn in white (the background colour) it would effectively erase what was previously drawn at that point. But this doesn't seem to be the case.
Anything I'm missing?
Instead of erasing the things you don't want you can:
save the state of the canvas
draw the things you don't want
restore the canvas to the saved state to 'erase' them
This can be accomplished pretty easily using ImageData:
var canvas = document.querySelector('canvas'),
context = canvas.getContext('2d');
context.fillStyle = 'blue';
context.fillRect(0,0,200,200);
// save the state of the canvas here
var imageData = context.getImageData(0,0,canvas.width,canvas.height);
// draw a red rectangle that we'll get rid of in a second
context.fillStyle = 'red';
context.fillRect(50,50,100,100);
setTimeout(function () {
// return the canvas to the state right after we drew the blue rect
context.putImageData(imageData, 0, 0);
}, 1000);
<canvas width=200 height=200>
For reasons that I could expand upon, you should consider clearing your canvas and redrawing it entirely unless there are performance or compositing reasons not to.
You want clearRect, something like this:
//clear the canvas so we can draw a fresh clock
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
//redraw your clock here
/* ... */
The reason you can't just redraw the line in white and hope for it to erase the old line is because there might be some anti-aliasing/bleeding. You'll also notice that a straight horizontal line drawn on a pixel versus a half-pixel looks very different because of this.
When you do your white "erase" lines, try drawing them with a larger lineWidth by about 3 or 4. That should work for your case.
You should also draw all of the white lines first, then all of the black lines, in case they intersect.
A quick and easy way to clear a canvas is to set the width:
context.canvas.width = context.canvas.width;
My solution is double buffering :
var shapes =
[{type:"circle", x:50, y:50, radious:40, lineWidth:2, strokeStyle:"#FF0000", fillStyle:"#800000"}
,{type:"rectangle", x:50, y:50, width:100, height: 100, lineWidth:2, strokeStyle:"#00FF00", fillStyle:"#008000"}
,{type:"line", x1:75, y1:100, x2:170, y2:75, lineWidth:3, strokeStyle:"#0000FF"}
];
step1();
setTimeout(function () {
step2();
setTimeout(function () {
step3();
}, 1000);
}, 1000);
function step1() {
clearCanvas('myCanvas1');
shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
};
function step2() {
clearCanvas('myCanvas2');
shapes.pop();
shapes.forEach((sh) => { drawShape('myCanvas2', sh); });
showOtherCanvas('myCanvas2', 'myCanvas1');
};
function step3() {
clearCanvas('myCanvas1');
shapes.pop();
shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
showOtherCanvas('myCanvas1', 'myCanvas2');
};
function showOtherCanvas(cnv1, cnv2) {
var c1 = document.getElementById(cnv1);
var c2 = document.getElementById(cnv2);
c1.style['z-index'] = 3;
c2.style['z-index'] = 1;
c1.style['z-index'] = 2;
}
function clearCanvas(canvasID) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,480,320);
}
function drawShape (canvasID, info) {
switch (info.type) {
case "line" : drawLine(canvasID, info);
case "rectangle" : drawRectangle(canvasID, info);
case "circle" : drawCircle(canvasID, info);
}
}
function drawLine (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.strokeStyle = info.strokeStyle;
ctx.lineWidth = info.lineWidth
ctx.beginPath();
ctx.moveTo(info.x1, info.y1);
ctx.lineTo(info.x2, info.y2);
ctx.stroke();
}
function drawRectangle (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle = info.fillStyle;
ctx.strokeStyle = info.strokeStyle;
ctx.lineWidth = info.lineWidth
ctx.fillRect(info.x, info.y, info.width, info.height);
ctx.strokeRect(info.x, info.y, info.width, info.height);
}
function drawCircle (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle = info.fillStyle;
ctx.strokeStyle = info.strokeStyle;
ctx.lineWidth = info.lineWidth
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.stroke();
}
<canvas id="myCanvas2" width="480" height="320"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:1">
</canvas>
<canvas id="myCanvas1" width="480" height="320"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:2">
</canvas>
The change is so fast you won't see any flicker.

Categories