Search code examples
javascriptcanvas

Javascript draw white box with a number in the center - number always a little off


I am drawing a white box via javascript and would like to also draw a number in the center of the box but it doesn't matter how I try to change the positioning, the number is always a little off:

enter image description here

// Create a canvas element
const canvas = document.getElementsByTagName("canvas")[0]
canvas.width = 150;
canvas.height = 150;

// Get the drawing context
const ctx = canvas.getContext('2d');

drawNumberBox(ctx, 123, 0,0, 100, 100, "arial");

function drawNumberBox(ctx, number, x, y, width, height, fontName) {
        // Draw the white background rectangle
        ctx.fillStyle = 'white';
        ctx.fillRect(x, y, width, height);

        // Set the font and text alignment
        ctx.font = `100px ${fontName}`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillStyle = 'black';

        // Check if the number fits within the box
        while (ctx.measureText(number).width > width) {
          // Decrease the font size until the number fits within the box
          ctx.font = `${parseInt(ctx.font) - 1}px ${fontName}`;
        }

        // Draw the number centered inside the box
        const xPos = x + (width / 2);
   

        const yPos = y + (height / 2)
        ctx.fillText(number, xPos, yPos);
      }
<html><body>
<canvas style="background:blue; border:red solid 2px"/>
</body></html>


Solution

  • In this fiddle, I've modified your code to center the text by using the actual bounding box properties of the measureText function.

        fontSize = fontSize *  width / (measure.actualBoundingBoxLeft + measure.actualBoundingBoxRight);
        const xPos = x + (width / 2) + ((measure.actualBoundingBoxLeft - measure.actualBoundingBoxRight) / 2);
        const yPos = y + (height / 2) + ((measure.actualBoundingBoxAscent - measure.actualBoundingBoxDescent) / 2);
    

    A more accurate width of the text is the sum of the right and left bounding box sizes and the offset of the true center from the rendered point can be calculated as shown above.

    This is necessary because the font, and even the specific characters used, can cause the actual bounding box to not be centred on the render point.

    *Edit I made a mistake when I wrote my previous answer and updated the font size in the wrong way. I was tired