How to use custom font for HTML5 canvas?
How to draw external font on html5 canvas?
If you want to use custom font for Konva.Text
you just need to:
- Add font style to your page
- Set
fontFamily
attribute to required font-face when font is loaded
But there is one important thing here. When you set font for DOM elements (like div
or span
) browsers will automatically update that elements when font is loaded. But it doesn't work the same for canvas text. You need to redraw canvas again.
Note: For older browsers that don't support the native Font Loading API, you can use a width measurement approach - measure text width with fallback font, then periodically check if the custom font width differs (indicating it's loaded).
Loading font
You may use this async function that combines the native Font Loading API with a reliable width-measurement fallback and timing guard:
const loadedFonts = {};
function measureFont(fontName, fallbackFont, fontStyle = 'normal', fontWeight = '400') {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const sampleText = 'The quick brown fox 0123456789';
ctx.font = `${fontStyle} ${fontWeight} 16px '${fontName}', ${fallbackFont}`;
return ctx.measureText(sampleText).width;
}
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function loadFont(fontName, fontStyle = 'normal', fontWeight = '400') {
if (loadedFonts[fontName]) return;
const hasFontsLoadSupport = !!(document.fonts && document.fonts.load);
const arialWidth = measureFont('Arial', 'Arial', fontStyle, fontWeight);
if (hasFontsLoadSupport) {
try {
await document.fonts.load(`${fontStyle} ${fontWeight} 16px '${fontName}'`);
const newWidth = measureFont(fontName, 'Arial', fontStyle, fontWeight);
const shouldTrustChanges = arialWidth !== newWidth;
if (shouldTrustChanges) {
// Small guard delay to avoid rare race when metrics are not ready yet
await delay(60);
loadedFonts[fontName] = true;
return;
}
} catch (e) {
// ignore and fallback to polling
}
}
const timesWidth = measureFont('Times', 'Times', fontStyle, fontWeight);
const lastWidth = measureFont(fontName, 'Arial', fontStyle, fontWeight);
const waitTime = 60;
const timeout = 6000; // do not wait more than 6 seconds
const attemptsNumber = Math.ceil(timeout / waitTime);
for (let i = 0; i < attemptsNumber; i++) {
const newWidthArial = measureFont(fontName, 'Arial', fontStyle, fontWeight);
const newWidthTimes = measureFont(fontName, 'Times', fontStyle, fontWeight);
const somethingChanged =
newWidthArial !== lastWidth ||
newWidthArial !== arialWidth ||
newWidthTimes !== timesWidth;
if (somethingChanged) {
await delay(60);
loadedFonts[fontName] = true;
return;
}
await delay(waitTime);
}
console.warn(
`Timeout for loading font "${fontName}". Is it a correct font family?`
);
}
- Vanilla
- React
- Vue