Reputation: 7943
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.
Upvotes: 0
Views: 1501
Reputation:
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:
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:
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);
}
Upvotes: 1