4

I am trying to insert a text at the middle of a circle drawn with HTML Canvas, but its always appearing a little above the center. (I tried baseline, but it didn't work). I don't want to hard-coded the fillText co-ordinates.

var canvas = document.getElementById('completionStatus');

var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 200;

context.beginPath();
context.fillStyle = '#B8D9FE';
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fill();
context.strokeStyle = '#ffffff';
context.font = 'bold 130pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'white';
context.fillText('70%', centerX, centerY);
context.stroke();
<canvas id="completionStatus" width="400" height="400"></canvas>

Here's a fiddle : http://jsfiddle.net/wku0j922/2/

Daud
  • 7,429
  • 18
  • 68
  • 115
  • have you tired this : context.fillText('80%',250,300); – Balvant Ahir Jun 16 '16 at 12:44
  • Does this answer your question? [Center (proportional font) text in an HTML5 canvas](https://stackoverflow.com/questions/13771310/center-proportional-font-text-in-an-html5-canvas) – rofrol Jun 09 '22 at 11:02

4 Answers4

7

Simply use the textBaseline property on the context. When both textBaseline is set to middle and textAlign is set to center you can place it exactly at the position of your circle center and it will be centered across the board.

var c1 = document.getElementById('c1');
var c2 = document.getElementById('c2');
var ct1 = c1.getContext('2d');
var ct2 = c2.getContext('2d');

c1.width = c2.width = 200;
c1.height = c2.height = 200;

ct1.font = ct2.font = '20px Helvetica';

ct1.textAlign = ct2.textAlign = 'center';
ct2.textBaseline = 'middle';

ct1.beginPath();
ct1.arc(100,100,99,0,Math.PI * 2);
ct1.stroke();
ct1.closePath();

ct2.beginPath();
ct2.arc(100,100,99,0,Math.PI * 2);
ct2.stroke();
ct2.closePath();

ct1.fillText('textBaseline not set', 100, 100);
ct2.fillText('textBaseline middle', 100, 100);
<canvas id="c1"></canvas>
<canvas id="c2"></canvas>

Here is it implemented in your snippet:

var canvas = document.getElementById('completionStatus');

var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 200;

context.beginPath();
context.fillStyle = '#B8D9FE';
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fill();
context.strokeStyle = '#ffffff';
context.font = 'bold 130pt Calibri';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillStyle = 'white';
context.fillText('70%', centerX, centerY);
context.stroke();
<canvas id="completionStatus" width="400" height="400"></canvas>

Docs: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline

somethinghere
  • 16,311
  • 2
  • 28
  • 42
  • except that [UAs don't agree on the base of textBaseline](https://bugzilla.mozilla.org/show_bug.cgi?id=737852) – Kaiido Jun 16 '16 at 15:15
  • You mean that Firefox can't agree? Look, it might not be 100% accurate but the other solutions are even less accurate or predictable, requiring UA sniffing if the above is a concern anyhow. It's good to keep this in mind, but unless you are a hardcode font graphic developer the difference isn't going to make it a hassle :) – somethinghere Jun 16 '16 at 15:17
  • Unfortunately this does/did in some cases... And on this point, even if I would prefer that every UAs do the same, I can understand why FF can't agree on nothing, while they've got the specs on their side. (read the three last comments in this bug report, [particularly #20](https://bugzilla.mozilla.org/show_bug.cgi?id=737852#c20). Ps : there are 100% accurate solutions, but way harder, implying reading the pixel positions etc. user @markE posted some almost elegant solution somewhere in here. – Kaiido Jun 16 '16 at 15:21
  • I know - but to be honest, this is the reason I know many people that moved away from Firefox - they might be more spec adherent but they're also the exception rather then the rule (this slide started with the whole H264 debacle). However, since calculating it manually with JS is just as unpredictable, this seems like the best way. At least it works the same on 90% of the browsers, and 10% get a slightly lesser experience. I chalk that up as a win-win! – somethinghere Jun 16 '16 at 15:23
3

Declaring the textBaseline prior to defining the fillText seems to work.

var canvas = document.getElementById('completionStatus');

var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 200;

context.beginPath();
context.fillStyle = '#B8D9FE';
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fill();
context.strokeStyle = '#ffffff';
context.font = 'bold 130pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'white';

// Defining the `textBaseline`… 
context.textBaseline = "middle";

context.fillText('70%', centerX, centerY);
context.stroke();
#completionStatus {
    border: 1px solid #000;
}
<canvas id="completionStatus" width="500" height="500"></canvas>
Ito Pizarro
  • 1,607
  • 1
  • 11
  • 21
  • For future reference, Stack Overflow provides some (now very improved) snippets so your code can be available on this site as long as it exists, and not as long as jsfiddle will be around. Otherwise I would suggest _actually giving the answer in this answer_, as you just said 'this works' but with no indication of what you did... In short, be a little more explanatory :) – somethinghere Jun 16 '16 at 13:14
  • @somethinghere Good point. I edited the answer to show the SO tool(s) and, consequently, display the code. – Ito Pizarro Jun 16 '16 at 13:27
1

Canvas puts the text baseline in vertical centrum.

To truly center the text, you need to compensate for the font size:

context.font = 'bold ' + radius + 'px Calibri';
context.fillText('70%', centerX, centerY + Math.floor(radius/4));
Emil S. Jørgensen
  • 6,216
  • 1
  • 15
  • 28
  • Why shouldn't context.setBaseLine('Center') work then ? – Daud Jun 16 '16 at 12:54
  • Because that still puts the baseline at vertical center. The textstring is written [over the baseline](https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/Typography_Line_Terms.svg/2000px-Typography_Line_Terms.svg.png), not on it: – Emil S. Jørgensen Jun 16 '16 at 12:59
  • You need the medianline in the center, – Emil S. Jørgensen Jun 16 '16 at 13:00
  • 1
    This is cumbersome. There is a `textBaseline` property that will do this for you. When the text baseline is middle _it will center_ - the only downside being that some fonts might **appear** less centered as they might have different computed boxes etc... In that case you _will_ have to manually correct for it, there is no programmatic way to do it. – somethinghere Jun 16 '16 at 13:12
0

I changed the centerY property to 300 and it seemed to have worked.

context.fillText('70%', centerX, 300);
alex
  • 479,566
  • 201
  • 878
  • 984