如何在 HTML5 画布中使用自定义字体?
如何在 HTML5 画布上绘制外部字体?
如果你想为 Konva.Text 使用自定义字体,只需:
- 将字体样式添加到页面中
- 当字体加载完成后,将
fontFamily属性设置为所需的字体名称
但这里有一个重要事项。当你为 DOM 元素(如 div 或 span)设置字体时,浏览器会在字体加载后自动更新这些元素。但对于画布文本,情况则不同。你需要重新绘制画布。
注意: 对于不支持原生字体加载 API 的旧版浏览器,你可以使用宽度测量方法 - 先用备用字体测量文本宽度,然后定期检查自定义字体的宽度是否发生变化(表明字体已加载)。
加载字体
你可以使用这个异步函数,它结合了原生字体加载 API 和可靠的宽度测量回退及时间保护:
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) {
// 添加短暂延迟以避免罕见的指标尚未准备好的竞态情况
await delay(60);
loadedFonts[fontName] = true;
return;
}
} catch (e) {
// 忽略错误并回退到轮询方法
}
}
const timesWidth = measureFont('Times', 'Times', fontStyle, fontWeight);
const lastWidth = measureFont(fontName, 'Arial', fontStyle, fontWeight);
const waitTime = 60;
const timeout = 6000; // 最多等待6秒
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(
`加载字体 "${fontName}" 超时。这是一个正确的字体族吗?`
);
}
- Vanilla
- React
- Vue