سؤال

I am trying to draw text in canvas but the drawn text has jaggies especially in chrome 31.0.1650. I have tried -webkit-font-smoothing:antialiased,text-shadow but all go in vain.

How to tackle this problem? Thanks in advance.

Here is the style code:

<style>
    #my_canvas{           
        -webkit-font-smoothing: antialiased;
        text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
        text-indent: -9999px; 
    }                   
</style>

The code in body:

<canvas id="my_canvas" height="300" width="2200"></canvas>
<script>
    var canvas = document.getElementById("my_canvas");
    var context = canvas.getContext("2d");
    context.imageSmoothingEnabled =true;
    context.fillStyle = "BLACK";
    context.font = "bold 100px Arial";
    context.fillText("A quick brown fox jumps over the lazy dOg", 50, 200);
</script>
هل كانت مفيدة؟

المحلول

This is an issue with the Chrome text engine.

There is a technique you can use to get around this though:

  • Setup an off-screen canvas double the size of the on-screen canvas
  • Draw the text to the off-screen canvas in scale: font size and position
  • Draw the off-screen canvas to main canvas scaled down (half in this case).

Live demo here

The difference is subtle (as expected) but improves the quality. Here is an enlargement:

Compare

Sidenotes: CSS does not affect content of the canvas, only elements. Image smoothing is enabled by default and affects only images but not text or shapes (we will use this though for this technique).

var scale = 2;  // scale everything x2.

var ocanvas = document.createElement('canvas');
var octx = ocanvas.getContext('2d');

ocanvas.width = canvas.width * scale;   // set the size of new canvas
ocanvas.height = canvas.height * scale;

// draw the text to off-screen canvas instead at double sizes
octx.fillStyle = "BLACK";
octx.font = "bold " + (100 * scale) + "px Arial";
octx.fillText("A quick brown fox jumps over the lazy dOg", 50*scale, 200*scale);

// key step is to draw the off-screen canvas to main canvas
context.drawImage(ocanvas, 0, 0, canvas.width, canvas.height);

What happens is that we are introducing interpolation in the mix here. Chrome will draw the text very rough also to the off-screen canvas, but after the text becomes rasterized we can deal with it as an image.

When we draw the canvas (aka image) to our main canvas at half the size, interpolation (sub-sampling) kicks in and will low-pass filter the pixels giving a smoother look.

It will of course take more memory but if result is important that is a small price to pay nowadays.

You will probably notice that other values than 2 doesn't work so well. This is because the canvas typically uses bi-linear interpolation rather than bi-cubic (which would allow us to use 4). But I think 2x serves us well in this case.

Why not using transform, ie. scale() ? Chrome's text engine (or the way it's used) does not rasterize the text at 1x and then transforms it. It takes the vectors, transforms them and then rasterize the text which will give the same result (ie. scale 0.5, draw double).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top