Explain Codes LogoExplain Codes Logo

Drawing text to <canvas> with @font-face does not work at the first time

html
responsive-design
web-development
best-practices
Anton ShumikhinbyAnton Shumikhin·Nov 24, 2024
TLDR

To fix <canvas> text visibility issues with @font-face fonts, preload the font using FontFace API. Ensure to add it to the document and only draw on the canvas once the font is loaded. Let's demonstrate this process:

const ctx = document.getElementById('canvas').getContext('2d'); const myFont = new FontFace('MyAwesomeFont', 'url(awesomefont.woff2)'); myFont.load().then(function(font) { // "I've got the power!" - Font, probably document.fonts.add(font); ctx.font = '20px MyAwesomeFont'; ctx.fillText('Text with custom font', 10, 50); }).catch(console.error);

Here, the .load() preloads the font and document.fonts.add() ensures the font is available for the canvas, putting an end to the first-time rendering issue.

Using preload for solid font display

You can force the font to load before drawing text on the <canvas>, and the trick is to use invisible <div> with the font-family property:

@font-face { font-family: 'MyAwesomeFont'; src: url('awesomefont.woff2') format('woff2'); }

And the in HTML, you add:

<div style="font-family: 'MyAwesomeFont'; opacity: 0; font-size: 0;">Preloading</div>

In essence, you're telling the browser: "Hey, look! There's a font here. Better load it fast!"

Event-based loading: A two-step tango

To assert control over font loading on <canvas>, try a tool like Google's WebFont Loader:

WebFont.load({ custom: { families: ['MyAwesomeFont'] }, active: function() { drawCanvasText("Now _, that's a load off my mind."); // subtle Queen reference } }); function drawCanvasText(text) { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.font = '20px MyAwesomeFont'; ctx.fillText(text, 10, 50); }

The active callback fires once the font has successfully loaded and the stage is set for the text to sparkle.

Error handling: Because it happens

Despite our best efforts, errors happen. To mitigate this, you can use an Image element trick to ensure font loading:

let fontLoader = new Image(); fontLoader.onerror = function() { // "Error, schmerror, I've got a canvas to paint! ᕕ( ᐛ )ᕗ" drawCanvasText(); }; fontLoader.src = 'awesomefont.woff2';

An error may fire due to the browser attempting to load the font file as an image, but used cleverly, this “mistake” signals that the font file has indeed been processed. Sneaky, huh?

Respect Browsers: Playing nice with compatibility

The FontFace API isn't supported universally. It's crucial to check for browser support to avoid unexpected errors or degraded user experiences:

if ('fonts' in document) { // "Look Ma, no hands!" loadCustomFont(); } else { // "Fallbacks. The parachutes of the coding world." fallBackStrategy(); }

Multi-browser testing ensures your artful canvas renditions display consistently across viewing platforms.

The Devil's in the detail: Accurate font usage

Being precise with the font details in your @font-face rule ensures consistent rendering:

@font-face { font-family: 'MyAwesomeFont'; src: url('awesomefont.woff2') format('woff2'); font-weight: 400; font-style: normal; }

Remember to cover all bases by specifying font-family, font-style, and font-weight. Command the typography you summon on canvas, wizard style.