Identical calls to arc producing different sizes - javascript

I'm trying to figure out why drawing a shape, then drawing over it in a new color (as though to highlight it), and then re-drawing the original (un-highlighting it) is leaving traces of the highlighted color.
I've reproduced the issue in this fiddle. The wedge is drawn in a light-blue color. There's a red button that'll draw over it in red, then another button that re-draws the original shape. All parameters are identical (except for the color), but yet after clicking the button to reset the color, there's a faint trace of red over the wedge.
Before:
After:
Here's the relevant code:
drawWedge(250, 250, 200, 0, 18, "rgb(150, 254, 223)");
$("#red").click(function () {
drawWedge(250, 250, 200, 0, 18, "rgb(255, 0, 0)");
});
$("#back").click(function () {
drawWedge(250, 250, 200, 0, 18, "rgb(150, 254, 223)");
});
function d2r(degrees) {
return degrees * (Math.PI / 180.0);
}
function drawWedge(centerX, centerY, r, start, end, color) {
context.beginPath();
context.moveTo(centerX, centerY);
context.arc(centerX, centerY, r, d2r(start), d2r(end), false);
context.closePath();
context.fillStyle = color;
context.fill();
}

This question was already answered, but I wanted to give a little more thorough explanation.
When you draw at a diagonal, your passing through "parts" of pixels (show in my example). So what does the browser do to the part of the pixel outside of the shape? It uses anti-aliasing (anti-aliasing is always on by default for browsers) to color the rest of the pixel (if you didnt have anti-aliasing the line would look jagged). If you notice, the faint trace of red is not a bright red because its getting blended due to anti-aliasing. And the reason you see it is because when you draw your shape on the canvas, the faint trace of red is not part of your shape, its part of the pixel on the outside of your shape.
Now as the answer mentioned, you can call clearRect to clear the canvas. However, you should read this SO question as it explains things in more detail (the selected answer is not as good as the second answer). Also, ever wonder why they call it a "canvas"? Think of an actual art canvas used by artists, once they paint on the canvas there is no way to take it off unless you get a new canvas or paint over it!

When drawing on canvas, it just keeps stacking things on top of each other until you clear it. The easiest way to clear it is ctx.clearRect(0,0,width,height)
I put that in your drawWedge function here:
http://jsfiddle.net/X7deh/1

Related

canvas drag and color select

So I have a color-selector in canvas. The gradient color fills a shape. The user uses their mouse to select anywhere on the histogram. Pretty standard. They can also drag the mouse anywhere in the shape. To show their location once they've made a final color selection, I added a small fillRect in black that moves along with their mouse. Now here's my problem, as they're moving the mouse occasionally the getImageData function grabs the color of the small black rect rather than the underlying gradient. To fix this problem, I offset the dot by 5px from the point the mouse contacts the gradient. It worked. However, now when they drag the mouse to the exact edge of the shape, it draws that black dot outside the boundaries and looks bad. How can I prevent this while also preventing my mouse from picking up the black sqr?
$colors.on('touchmove mousemove', function (e) {
e.preventDefault();
let gradient = Ybigh.colorctx.createRadialGradient(135, 330, 160, 200, 290, 30);
gradient.addColorStop(0, "#84342f");
gradient.addColorStop(0.25, "#ff5f57");
gradient.addColorStop(1, "white");
colorctx.beginPath();
colorctx.moveTo(20, 185);
colorctx.lineTo(200, 285);
colorctx.lineTo(200, 295);
colorctx.lineTo(20, 400);
colorctx.closePath();
colorctx.fillStyle = gradient;
colorctx.strokeStyle = "#b0aaa6";
colorctx.stroke();
colorctx.fill();
colorctx.fillStyle = "#000000";
colorctx.fillRect(new_color.x-2, new_color.y-2, 2, 2);
});
I figured out a workaround that satisfies the requirements. Essentially, what I did was allow the user to move the mouse around and change the color without a black rect displaying and only on mouseup does the small rect appear to mark the spot. It's a bit hacky, but it does the job as far as I'm concerned.
$("body").mouseup(function () {
glob.hasclicked=false;
colorctx.fillStyle = "#000000";
colorctx.fillRect(glob.rectPosition.x, glob.rectPosition.y, 3, 3);
});

HTML5 canvas line width less that 1 pixel

Is there any way to draw a rectangle whose composing lines have width thinner than 1 pixel?
This code works perfectly, as expected:
// context is a HTML5 canvas 2D context
context.lineWidth = 1;
context.strokeStyle = "black";
context.rect(0, 0, 20, 20);
context.stroke();
It draws a nice rectangle.
But, if I try to draw a rectangle with thinner lines:
// See line width
context.lineWidth = 0.5;
context.strokeStyle = "black";
context.rect(0, 0, 20, 20);
context.stroke();
It still draws a rectangle whose borders have 1 pixel width.
I'm dealing with the canvas object here, and not CSS, where you have ways to "simulate" this.
Although it doesn't make much sense, you can acheive that with using a regular 1-pixel line with a 50% scaled canvas (but again it's a 1-pixel rendition, read below). See this snippet:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
function scale() {
context.scale(0.5, 0.5);
draw();
}
function draw() {
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
}
draw()
<canvas width="400" height="150"></canvas>
<button onclick="scale()">Scale down</button>
But again, I wonder how you expect the half-pixel line to look on your screen, antialiasing?
Right :) I suppose I was thinking on some way of drawing thinner lines, like, for example, when you use CSS styles. I've looked around and I don't think I can use alternate units.
There's no way to make something that's smaller than the smallest component unit, in our case a pixel. You can mimic the thinner look by transparency, or opacity, or even some sort of antialiasing (which again relies on transparency or the colouring of the neighbouring pixels), but not by trying to go below one pixel.
I agree, there is a sub-pixel rendering mode in browsers, for example, when you work with percentages, but in the end, the browser just renders full pixels with some of the modification I've described above.
And you know if you could render unit smaller than pixels, you'd technically have infinite resolutions on displays. I wish it was possible. :)

Not Understand how this Canvas Code Works

I'm playing around with Canvas Code. I wrote a function that draws out a path
function draw_faces() {
var canvas = document.getElementById("faces_bkgd");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var happy_face = new Path2D();
happy_face.rect(10,10,100,100);
ctx.fillStyle = "rgb(0,0,200)";
ctx.fill(happy_face);
happy_face.moveTo(50,50);
happy_face.lineTo(90,90);
happy_face.lineTo(90,50);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fill(happy_face);
ctx.save();
ctx.translate(50,50);
ctx.fillStyle="rgb(0,200,0)";
ctx.fill(happy_face);
ctx.restore();
}
fiddle demo
It gives me the following output at the top left of the page, there is a red square with a blue triangle in it. To the bottom of the red square, there is a overlapping green square with a triangle cutout.
Why is it a green square with a triangle cutout instead of a green square with a blue triangle in it?
Also, what do you guys use to debug Canvas on Web Inspector for Safari?
EDIT: I created some images explaining step by step what is happening. If you would like me to go into more detail please let me know. If there are certain parts you do not understand please let me know. If there are terms (methods, etc) that don't make sense let me know. Anything vague let me know. :)
Here are the important parts of you code with comments on what really is happening.
First part of your code draws a blue square with an instance of Path2D that you have defined as a rect.
var happy_face = new Path2D();
happy_face.rect(10, 10, 100, 100);
ctx.fillStyle = "rgb(0,0,200)";
ctx.fill(happy_face);
Then instead of creating a new instance of Path2D, you use your previous variable, happy_face, that is still defined as a rect and now you cut out a triangle and set the color to red and draw it.
//The moveTo and lineTo cut out a triangle in your square.
happy_face.moveTo(50, 50);
happy_face.lineTo(90, 90);
happy_face.lineTo(90, 50);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fill(happy_face);
Since you didn't move this square with a triangle cut out in it, you draw on top of your blue square. This gives the impression the triangle you cut out is blue, but that's just the old square underneath.
Going to ignore the save and restore methods since they don't do anything worthwhile. You then do a tanslate.. which just moves the pointer from where you start drawing from (0,0) to (50, 50). At ths point you change colors from red to green and you start drawing at (50, 50) happy_face ( the old square with a triangle cut out in it ).
ctx.save();
ctx.translate(50,50);
ctx.fillStyle="rgb(0,200,0)";
ctx.fill(happy_face);
ctx.restore();
Unfortunately you can't debut "canvas". Because when you draw something, you can't just move it. It's literally drawn on the canvas. If you draw a square on top of the other square, well the previous square is lost.

Javascript canvas gap after fill of shapes with common border

I would like to understand & learn a solution for the issue I am experiencing when I am filling two shapes/paths with a shared border, i.e. after the fill is called for all the shapes there is still a tiny gap present between them.
The snippet presents the code doing the drawing of the involved shapes/paths:
ctx.beginPath();
ctx.moveTo(pBottom.x, pBottom.y);
ctx.lineTo(0, 0);
ctx.lineTo(pTop.x, pTop.y);
ctx.lineTo(pTopFront.x, pTopFront.y);
ctx.lineTo(pBottomFront.x, pBottomFront.y);
ctx.fillStyle = gradientTopSide;
ctx.fill();
ctx.fillStyle = gradientBottomSide;
ctx.fill();
ctx.beginPath();
ctx.moveTo(pBottom.x, pBottom.y);
ctx.arc(0, 0, radiusBackArc, (angleBottomBack) * Math.PI / 180, (angleTopBack) * Math.PI / 180);
ctx.lineTo(0, 0);
ctx.fillStyle = gradientBackArc;
ctx.fill();
The gaps are visible to the left (by the blue circle), to the top of it and to the bottom. They are where the fills of radial and linear gradients meet.
I wanted play a bit with canvas to create a simple light/torch effect and these lines are ruining my fun. Certainly cause I do not know how to fill them in a nice way without ruining the gradient effect.
Please find the JSFiddle presenting the issue.
When you deal with gradients involving transparency you'll run into overlaps where alpha channel values will multiply as well as sub-pixeling, and rounding errors for the points and possibly also for gradient calculations.
Chances are that the calculations you do in the code has to be rounded off properly. If they aren't you will have canvas do sub-pixeling of those pixels and it's hard to maintain a concise result, in particular when alpha is involved. It's the quirks of canvas and this way of drawing gradient lines. Canvas does not properly "bend" (join) in the corners either so you get overlapping drawings - not much we can do about that part.
I see two ways to solve this is a simple manner:
Use an image which you draw in instead of building a shape - this is fast and the result just as good unless you need different sizes (if animated, that doesn't matter so much though). I would personally go with this option (and you'll get more artistic freedom as well).
Draw the shape with a different technique:
I'll demonstrate the latter here, and as a bonus you can drop a lot of code. If you need transparency than you should go with option 1. Also, it ain't perfect either as we still have to rely on canvas' way of doing this, but it could be perhaps an improved replacement when it comes to the overlapping issues:
Use a single closed path of the shape
Modify current shape by removing the last lineTo() and replace it with closePath()
Important: change lineJoin to round
We create a loop where we draw the shape overlapping, a and for each iteration:
Change solid color based on iteration
Scale slightly
Move slightly
I made a simple version of this here but I'll leave it to you to figure out the more satisfying details of values if you choose to use it.
var d2r = Math.PI / 180, i = 0, iterations = 14;
ctx.lineJoin = "round";
ctx.lineWidth = 3;
for(; i < iterations; i++) {
ctx.beginPath();
ctx.moveTo(pBottom.x, pBottom.y);
ctx.arc(0, 0, radiusBackArc, angleBottomBack * d2r, angleTopBack * d2r);
ctx.lineTo(pTopFront.x, pTopFront.y);
ctx.arc(0, 0, radiusFrontArc, angleTopFront * d2r, angleBottomFront * d2r);
// don't set endpoint=startpoint, instead just close the shape
ctx.closePath();
// using HSL will allow us to easily set a gradient ligthness:
ctx.strokeStyle = "hsl(0, 0%, " + (i/iterations*100) + "%)";
ctx.stroke();
ctx.translate(i*0.1,-i*0.02);
ctx.scale(0.99, 0.98);
}
Modified fiddle here

Javascript Canvas fillRect transparent black

I'm developing a resource monitor with JavaScript, and I pretend to complete it with an cool background animation.
I'm having trouble with fillRect and transparent colors in fillStyle, eg.:
function draw() {
ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "#00FF00";
ctx.strokeStyle = "#00FF00";
ctx.beginPath();
ctx.arc(getRandom(0, c.width),getRandom(0,c.height),25,0,2*Math.PI);
ctx.arc(getRandom(0, c.width),getRandom(0,c.height),25,0,2*Math.PI);
ctx.stroke();
}
It works fine, but it doesn't fill completely, leaving some "ghosts" where circles already passed before.
There is anyway to fix this and make background pure black again?
Notes:
I can't draw pure black, because I want the drawed lines smoothly disappear
Image of the problem: http://i.stack.imgur.com/GrPxX.png
Note that yellow dots are most recent lines, orange are transitional lines, which give the smooth effect, and red dot are the "ghosts"
When you fill the rectangle, you're using a semi-transparent black. What that will do is to darken what's there, but it won't obliterate it, because it's semi-transparent. If you want to cover it up with pure black, either set full opacity (max alpha value), or else use an rgb colour rather than rgba. If you use rgb, the alpha value will implicitly be set to opaque.

Categories