Not Understand how this Canvas Code Works - javascript

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.

Related

Canvas won't stack alpha on same line

I'm creating a heatmap of my gps tracks and am fighting a bit with canvas. I'm drawing a line with 10% transparency of all my tracks and the issue is that canvas doesn't want to stack the alpha on one line, e.g. with this:
ctx.fillStyle = 'rgba(1,1,1,0.1)';
points.forEach(point => ctx.lineTo(point.x, point.y));
ctx.stroke();
doesn't work as expected as everything, regardless of whether a track crosses over itself 100 times, has the color rgba(1,1,1,0.1)
Now I can draw each and every segment of the line separately, i.e.:
points.forEach(point => {
ctx.lineTo(point.x, point.y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(point.x, point.y);
});
This properly stacks the alpha values of lines drawn on top of one another, but it creates a new issue - The line caps overlap. e.g. imagine drawing a "V". There will be a tiny diamond shape at the top of the corner that has overlapping lines. This is creating lots of "dots" all over my heatmap.
Any ideas on how to solve this?

HTML Canvas: rectangle stroke appears transparent

To be frankly honest with you, I've got no idea if this is my eye being a b*tch with me or the canvas actually going transparent. The issue seems to be perfectly clear to me however: when I use strokeRect (stroke Rectangle), the stroke appears to be transparent no matter what I do.
See screenshot of the stroke: http://prntscr.com/ijeiu9
See screenshot of a normal fill: http://prntscr.com/ijeiz7
My code is as simple as:
context.strokeStyle = "#F5F5F5";
context.strokeRect(128, 488, 400, 26);
This might even be default behaviour of what I may know, how can I 'fix' this?
MDN documentation describes this situation as follows:
Obtaining crisp lines requires understanding how paths are stroked. In
the images below, the grid represents the canvas coordinate grid. The
squares between gridlines are actual on-screen pixels. In the first
grid image below, a rectangle from (2,1) to (5,5) is filled. The
entire area between them (light red) falls on pixel boundaries, so the
resulting filled rectangle will have crisp edges.
If you consider a path from (3,1) to (3,5) with a line thickness of
1.0, you end up with the situation in the second image. The actual area to be filled (dark blue) only extends halfway into the pixels on
either side of the path. An approximation of this has to be rendered,
which means that those pixels being only partially shaded, and results
in the entire area (the light blue and dark blue) being filled in with
a color only half as dark as the actual stroke color.
This means that to get a crisp line of width 1 you should start your rectangle from the half of a pixel:
let a = document.getElementById("a");
let ac = a.getContext("2d");
ac.strokeStyle = "#F5F5F5";
ac.lineWidth = 1;
ac.strokeRect(20, 20, 150, 100);
let b = document.getElementById("b");
let bc = b.getContext("2d");
bc.strokeStyle = "#F5F5F5";
bc.lineWidth = 1;
bc.strokeRect(20.5, 20.5, 150, 100);
canvas {width: 300px; height: 150px; background-color:black}
<canvas id="a"></canvas>
<canvas id="b"></canvas>

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. :)

Draw a Play button on canvas in javascript - triangle in a circle

So I'm creating a simple userscript for me - I want to draw a play button over shaded version of any GIF image on the site to have them only played when I want to.
I have inspired myself here. To make the whole thing nice, I'm drawing a green circle over the animation. This is my intended effect (I really wonder if once can make some shapes in GIMP):
And this is what I currently have:
The Cleese ironic face animation comes from this GIF portal
I made a fiddle of my current GIF pauser userscript.
The critical code for printing the circle:
//Assume these variables
var ctx = canvas context (2d)
var w = canvas width
var h = canvas height
//The code:
ctx.beginPath();
//I'm making sure the circle will always fit - therefore Math.min
ctx.arc(w/2, h/2, Math.min(60, w/2-20, h/2-20), 0, 2 * Math.PI, false);
ctx.fillStyle = 'rgba(0,180,0,0.5)';
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = 'red';
ctx.stroke();
ctx.closePath();
I hope it's clear that the triangle must always fit into the circle (size of which may vary). It must have the vertical line a little bit shorter then the other two, which must be of equal length.
I'm totally not interested in any existing GIF pausing libraries. Keep in mind that the question is about the triangle, not the GIFs.
Like this? http://jsfiddle.net/32ECU/
//Draw a triangle in it
ctx.strokeStyle = 'white';
ctx.beginPath();
ctx.moveTo(w/3, h/3);
ctx.lineTo(w/3, h-h/3);
ctx.lineTo(w-w/4, h/2);
ctx.lineTo(w/3, h/3);
ctx.fillStyle = 'white';
ctx.fill();

Identical calls to arc producing different sizes

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

Categories