Question

I am drawing onto an HTML5 canvas with stroke() and regardless of how or when I set globalAlpha, the stroke is being drawn with some measure of transparency. I'd like for the stroke to be completely opaque (globalAlpha=1). Is there somewhere else where the alpha is being set?

In this jsfiddle, I am drawing a grid of solid black lines onto a canvas. For me, the result shows dots at the intersections, confirming that the lines are partially transparent. Here's the gist of it:

context.globalAlpha=1;
context.strokeStyle="#000";
context.beginPath();
/* draw the grid */
context.stroke();
context.closePath;

The especially weird thing (to me) is that this problem was not occurring in my code before my last computer restart, so I'm guessing there was something hanging around in the cache that was keeping the alpha at my desired level.

I'm obviously missing something here... thanks for any help you can provide.

Was it helpful?

Solution

Real answer :

  • Each point in a canvas has its center in its (+0.5, +0.5) coordinate. So to avoid artifacts, start by translating the context by (0.5, 0.5) , then round the coordinates.

  • css scaling creates artifact, deal only with canvas width and height, unless you want to deal with hidpi devices with webGL, or render at a lower resolution with both webGL and context2D.

-> in your case, your setup code would be (with NO css width/height set ) :

( http://jsfiddle.net/gamealchemist/x9bTX/8/ )

// parameters
var canvasHorizontalRatio = 0.9;
var canvasHeight = 300;
var hCenterCanvas = true;
// setup
var canvasWidth = Math.floor(window.innerWidth * canvasHorizontalRatio);
var cnv = document.getElementById("myCanvas");
cnv.width = canvasWidth;
cnv.height = canvasHeight;
if (hCenterCanvas)
cnv.style['margin-left'] = Math.floor((window.innerWidth - canvasWidth) * 0.5) + 'px';
var ctx = cnv.getContext("2d");
ctx.translate(0.5, 0.5);
gridContext();
     

The rest of the code is the same as your original code, i just changed the size of you squares to get quite the same visual aspect.

ctx.beginPath();
for (var i=60; i<canvasHeight; i+=60) {
    ctx.moveTo(0,i);
    ctx.lineTo(canvasWidth,i);
}
for (i=60; i<canvasWidth; i+=60) {
    ctx.moveTo(i,0);
    ctx.lineTo(i,canvasHeight);
}
ctx.strokeStyle="#000";
ctx.stroke();
ctx.closePath();

With those changes we go from : enter image description here

to :

enter image description here

Edit : to ensure rounding, in fact i think most convenient is to inject the context and change moveTo, lineTo :

function gridContext() {
     var oldMoveTo = CanvasRenderingContext2D.prototype.moveTo;
     CanvasRenderingContext2D.prototype.moveTo = function (x,y) {
               x |= 0; y |= 0; 
               oldMoveTo.call(this, x, y); 
     }
     var oldLineTo = CanvasRenderingContext2D.prototype.lineTo;
     CanvasRenderingContext2D.prototype.lineTo = function (x,y) {
               x |= 0; y |= 0; 
               oldLineTo.call(this, x, y); 
     }
} 

Obviously, you must do this for all drawing functions you need.

OTHER TIPS

When drawing lines on a canvas, the line itself is exactly on the pixel grid. But because the line is one pixel wide, half of it appears in each of the pixels to either side of the grid, resulting in antialising and a line that is basically 50% transparent over two pixels.

Instead, offset your line by 0.5 pixels. This will cause it to appear exactly within the pixel.

Demo

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top