Canvas "alphabetic" textBaseline Is Consistent Across Browsers
Earlier this week, I took a look at rendering text to <canvas>
using an adjusted X,Y offset for cross-browser consistency. In that demo, I was using a textBaseline
of top
, which is inherently different from browser to browser. After I posted that, Jan Sedivy - creator of InVision Freehand - told me that while top
may be inconsistent, a textBaseline
of alphabetic
is rendered perfectly across all browsers. And, in fact, also matches the baseline
rendering of the browser's native text (outside of Canvas). As such, I wanted to perform a fast-follow demo to look at how the alphabetic
baseline renders in Chrome, Firefox, and Safari.
Run this demo in my JavaScript Demos project on GitHub.
View this code in my JavaScript Demos project on GitHub.
Just as with my previous demo, I'm going to first render a grid of lines to the Canvas element so that we can clearly see where text is being rendered. Then, I'm going to render sample text at various pixel-sizes so that we can see the relationship between the font-sizes and the rendering:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
Canvas "alphabetic" textBaseline Is Consistent Across Browsers
</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap" />
<style type="text/css">
body {
font-family: "Roboto", sans-serif ;
}
canvas {
box-shadow: 0px 0px 0px 2px #000000 ;
}
</style>
</head>
<body>
<h1>
Canvas "alphabetic" textBaseline Is Consistent Across Browsers
</h1>
<canvas id="demo" width="400" height="400"></canvas>
<script type="text/javascript">
var canvas = document.querySelector( "#demo" );
var canvasWidth = 400;
var canvasHeight = 400;
var context = canvas.getContext( "2d" );
// We have to give the FONT time to load so that we can use it on the canvas.
window.addEventListener(
"load",
() => {
drawGridLines();
drawText( "Nobody puts baby in a corner!" );
}
);
// --------------------------------------------------------------------------- //
// --------------------------------------------------------------------------- //
/**
* I draw the sample text to canvas at various sizes.
*/
function drawText( textValue ) {
var pairings = [
{ fontSize: 10, y: 50 },
{ fontSize: 20, y: 100 },
{ fontSize: 30, y: 150 },
{ fontSize: 40, y: 200 },
{ fontSize: 50, y: 250 },
{ fontSize: 60, y: 300 },
{ fontSize: 70, y: 350 }
];
context.fillStyle = "#000000";
context.textBaseline = "alphabetic";
for ( var pairing of pairings ) {
context.font = `500 ${ pairing.fontSize }px Roboto`;
context.fillText( textValue, 50, pairing.y );
}
}
/**
* I draw the horizontal and vertical grid lines on the canvas so that we can more
* easily see where the text is aligned on different browsers.
*/
function drawGridLines() {
var step = 10;
var jump = 50;
context.lineWidth = 1;
context.strokeStyle = "#cccccc";
// Draw GREY horizontal grid lines.
for ( var i = step ; i < canvasHeight ; i += step ) {
context.beginPath();
context.moveTo( 0, ( i - 0.5 ) );
context.lineTo( canvasWidth, ( i - 0.5 ) );
context.stroke();
}
// Draw GREY vertical grid lines.
for ( var i = step ; i < canvasWidth ; i += step ) {
context.beginPath();
context.moveTo( ( i - 0.5 ), 0 );
context.lineTo( ( i - 0.5 ), canvasHeight );
context.stroke();
}
context.strokeStyle = "#ff3333";
// Draw RED horizontal grid lines.
for ( var i = jump ; i < canvasHeight ; i += jump ) {
context.beginPath();
context.moveTo( 0, ( i - 0.5 ) );
context.lineTo( canvasWidth, ( i - 0.5 ) );
context.stroke();
}
// Draw RED vertical grid lines.
for ( var i = jump ; i < canvasWidth ; i += jump ) {
context.beginPath();
context.moveTo( ( i - 0.5 ), 0 );
context.lineTo( ( i - 0.5 ), canvasHeight );
context.stroke();
}
}
</script>
</body>
</html>
And, when run this is Chrome, Firefox, and Safari, we get the following output:
As you can see, using a textBaseline
of alphabetic
looks more-or-less exactly the same in each browser. But, the big difference, rendering-wise, between this demo and the previous demo is that the text is being rendered above the red lines as opposed to below the red lines. In order to account for that, we'd have to adjust the location of the y
coordinate in the .fillText()
invocation - but that's a post for another day.
Want to use code from this post? Check out the license.
Reader Comments
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →