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>
Related
I'm trying to overlay this black rectangle:
By filling another rectangle of the same size on top of it that has a semi-transparent, gradient paint (should look something like this):
I know I can do a transparent paint with the following:
g2d.fillStyle = "rgba(100, 3, 3, 0.5)";
I also know how to do a gradient paint:
var grd=g2d.createLinearGradient(0,0,200,0);
grd.addColorStop(0,"red");
grd.addColorStop(1,"blue");
g2d.fillStyle=grd;
However, I do not know how to combine both the gradient and transparency properties together as one paint to use on my rectangle. How can I do this?
There are two ways:
Global alpha
Set global (consider it a "master alpha") right before drawing something:
ctx.globalAlpha = 0.5; // [0, 1]
ctx.fillRect( ... );
Color alpha
Or define the colors themselves with alphas:
grd.addColorStop(0, "rgba(255,0,0, 0.5)"); // 50% alpha
grd.addColorStop(1, "rgba(0,0,255, 0.5)");
Worth to notice: if you use the latter approach and for example set 0% opacity on one end, the color will still matter as it is interpolated to the point where it becomes fully transparent. In the meanwhile the color definition will bleed through. I.e. don't just set black (unless black is what you need).
First draw the gradient:
var grd=g2d.createLinearGradient(0,0,200,0);
grd.addColorStop(0,"red");
grd.addColorStop(1,"blue");
g2d.fillStyle=grd;
Then draw the semi-transparent background:
g2d.fillStyle = "rgba(200,0,0,0.5)";
g2d.fillRect(x,y,w,h);
I have to scale down images and turn them into jpegs in a browser. For this i have been using a javascript resize function called Hermite-resize
This works great. After re-sizing a few hundred images I have noticed something odd happens once every while. With some images a thin black line appears at the bottom of the scaled down jpeg image.
I thought this might be due to a transparent line being rendered in the scaled down version. After converting to jpeg, the line then becomes black since jpegs turn no background into a black background.
However if i put a white background behind the image before rendering it as a jpeg, then the problem still remains.
If anyone has an idea on what creates this, or how to fix it, i would much appreciate it.
A jsfiddle to clarify what i mean:
canvas.getContext("2d").fillStyle = '#FFF';
canvas.getContext("2d").fillRect(0, 0, W2, H2);
canvas.getContext("2d").putImageData(img2, 0, 0);
document.getElementById("imageid").src = canvas.toDataURL("image/jpeg");
http://jsfiddle.net/gt4r54zr/1/
I'm not sure if it's related but if i set the transparency to full (255) in the Hermite script, then the same black line appears as well:
data2[x2 + 3] = 255;// Original value: gx_a / weights_alpha;
http://jsfiddle.net/3osq1s1w/3/
The function resample_hermite() calculates these ratios:
var ratio_w_half = Math.ceil(ratio_w/2);
var ratio_h_half = Math.ceil(ratio_h/2);
which are eventually used to determine the dimensions of the re-sized image. Note the Math.ceil, which rounds (ratio_h/2) to the next greatest integer. It appears the black line is being rendered in those cases where the mantissa (i.e., everything to the right the decimal point) of (ratio_h/2) is less than 0.5. In these cases, the calculated height of the image is one pixel too large, and there's no data to fill in these pixels.
Changing
var ratio_h_half = Math.ceil(ratio_h/2);
to
var ratio_h_half = Math.round(ratio_h/2);
seems to fix the problem in your example.
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.
When I draw a simple rectangle using the following code the bottom and right edge borders are thicker that the top and left edge borders. Why is this and can I stop it?
var paper = Raphael(10, 50, 500, 500);
var rect = paper.rect(100, 100, 100, 100);
Your rectangle's top and left borders, which are using the default 1 pixel stroke-width, are falling exactly on the top and left borders of your SVG element (as represented by a Raphael paper object. As opposed to pixel based drawing solutions, this means the line is essentially straddling the element's border, resulting in 0.5 pixels of your border stroke being clipped.
To solve, you simply need to shift your drawing over or shift the beginning offset of your SVG element's coordinates.
Here's a fiddle that shows one solution.
The square looks fine to me: http://jsfiddle.net/cMXBC/2/
Could you have some rogue css somewhere that is modifying the stroke of the rect? Try right-clicking the square and inspecting the rectangle in Firebug or with the Chrome inspector to see if there is any style that has been added.
I've looked around the internet and found nothing, I've looked on other KineticJS examples that use a strokeWidth of 1 on their rectangles and they all appear to have a semi-opaque 2 pixel line rather than a nice sharp 1px opaque black line.
Now, I am guessing that as Google has nothing that the solution is either really simple or impossible, but.. do you know how I can get a one px border using KineticJS?
$(window).load(function(){
var stage = new Kinetic.Stage({container: "kineticdiv", width: 700, height: 400});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: stage.attrs.width/2, y: stage.attrs.height/2,
width: 100, height: 100,
fill: "#eee", stroke: "black", strokeWidth: 1
});
layer.add(rect);
stage.add(layer);
});
Anyone got any ideas?
when you draw a line from (x,y1) to (x,y2) (say; the same is true for horizontal lines) you need to worry about whether x is "in the middle of a pixel". if the line is "between pixels" then it will be half in one and half in another. the result will look blurred (it's basically anti-aliasing).
graphics systems vary on whether coordinates are for corners or centres, but you can fix the issue by experimenting a little - you just need to add half a pixel width to the coord and try again.
in the case of an html5 canvas (0,0) is the top left corner, so if you have no transform i guess the top left pixel centre is at (0.5, 0.5).
Another approach: if you use Integer numbers as coordinates and ortogonal 1px weight lines, then you can move the whole stage by [0.5, 0.5] and you dont have to add the half of a pixel to each coordinate, you can then use Integer numbers as coordinate as your whole stage will be moved half of pixel to right and the same to down.
There is a cool approach to get exactly what you want: group two similar shapes. The one at the lower level is one pixel larger then the one at the top. Fill the bottom one with the color you want your border (in your case: Black). works fine for me and has the precision and quality of CSS
The easiest way of solving this with Kinetic is to use the offset properties. So, rather than shifting individual coordinates of what you're drawing, your entire line/shape/group/layer/stage is offset by that much, theoretically getting it where you want it with minimum fuss:
var rect = new Kinetic.Rect({
x: stage.attrs.width/2, y: stage.attrs.height/2,
width: 100, height: 100,
fill: "#eee", stroke: "black", strokeWidth: 1,
offsetX: 0.5,
offsetY: 0.5
});
or, to get a whole bunch of stuff at once:
var layer = new Kinetic.Layer({
offsetX: 0.5,
offsetY: 0.5
});
That said, not all items benefit from this trick. Some, in fact, get fuzzier. So, make sure to apply the offset at the most atomic level that avoids contaminating shapes that don't benefit from it.