
本文介绍如何使用 canvas api 实现文字从画布中心逐字显示,并实时根据已渲染字符总宽度动态调整起始位置,确保文本始终视觉居中、对称展开。
本文介绍如何使用 canvas api 实现文字从画布中心逐字显示,并实时根据已渲染字符总宽度动态调整起始位置,确保文本始终视觉居中、对称展开。
要在 HTML Canvas 中实现类似《ULTRAKILL》中那种“文字从中心生长、每新增一个字符都自动重平衡居中”的效果,关键不在于固定步进偏移(如 x += 17),而在于每次绘制前重新计算整段已生成文本的总宽度,并据此动态确定首字符的横坐标。
Canvas 提供了 ctx.measureText() 方法,可精确获取指定字体下任意字符串的渲染宽度(单位:像素)。利用该能力,我们就能在每一帧中:
- 取出当前已渲染的子串(即
text.slice(0, i).join('')); - 调用
ctx.measureText(...).width获取其总宽度; - 将首字符的 x 坐标设为
(canvas.width - textWidth) / 2,从而让整段文本水平居中。
以下是优化后的完整实现(含健壮性增强):
const font = new FontFace('ULTRAKILL', 'url(VCR_OSD.ttf)');
font.load().then((font) => {
document.fonts.add(font);
}).catch(err => console.warn('Failed to load ULTRAKILL font:', err));
function genText(interval = 100, startAt = 0) {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const inputEl = document.getElementById('input');
const text = inputEl.value.split('');
// 预设字体样式(必须在 measureText 前设置,否则宽度计算不准确)
ctx.font = '30px ULTRAKILL';
ctx.textAlign = 'left'; // 保持 fillText 行为一致
ctx.textBaseline = 'middle';
const centerY = canvas.height / 2;
let i = 0;
// 每次调用前清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
setTimeout(() => {
const timer = setInterval(() => {
if (i >= text.length) {
clearInterval(timer);
return;
}
// ✅ 动态计算已渲染文本宽度
const currentText = text.slice(0, i + 1).join('');
const textWidth = ctx.measureText(currentText).width;
// ✅ 居中定位:首字符 x = (canvas宽 - 总宽) / 2
const startX = (canvas.width - textWidth) / 2;
// 清空并重绘全部已生成字符(避免残留/重叠)
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillText(currentText, startX, centerY);
i++;
}, interval);
}, startAt);
}
⚠️ 重要注意事项:
-
字体加载必须完成后再调用
genText(),否则measureText()返回宽度为0;建议添加加载状态判断或 Promise 链式调用; -
ctx.font必须在measureText()之前设置,且与实际渲染字体完全一致(包括字号、字体族); - 每次只渲染「当前完整字符串」而非单个字符,才能保证视觉连贯性和居中精度(原逻辑逐字
fillText并累加x会导致错位); - 若需支持换行、多行或富文本,应改用分词+布局引擎(如
canvas-text库),但本例聚焦单行居中动画,简洁高效。
该方案不仅精准复刻了目标效果,还具备良好可维护性——只需修改 ctx.font 或输入源,即可适配不同字体、字号与内容,是 Canvas 文本动态渲染的经典实践模式。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











