
本文介绍如何使用 Canvas 的 measureText() 方法,让文本从画布中心开始逐字符显示,并随字符增加自动调整位置,保持整体居中对齐,适用于游戏式文字动画(如 ULTRAKILL 风格)。
本文介绍如何使用 canvas 的 `measuretext()` 方法,让文本从画布中心开始逐字符显示,并随字符增加自动调整位置,保持整体居中对齐,适用于游戏式文字动画(如 ultrakill 风格)。
要实现“居中逐字渲染”效果,核心在于动态计算已渲染文本的总宽度,并据此实时更新每个字符的横坐标(x),使其始终以画布水平中心为基准对齐。原始代码中固定递增 x += 17 的方式仅适用于等宽字体且忽略实际字形差异,无法保证视觉居中;而 ULTRAKILL 所用的 VCR_OSD 等像素字体虽接近等宽,但浏览器渲染仍受字体度量、连字、空格等影响,必须依赖 ctx.measureText() 获取精确宽度。
以下是优化后的完整实现:
// 预加载自定义字体(确保渲染前可用)
const font = new FontFace('ULTRAKILL', 'url(VCR_OSD.ttf)');
font.load().then(() => {
document.fonts.add(font);
}).catch(err => console.warn('Font load failed:', err));
function genText(interval = 100, startAt = 0) {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const inputEl = document.getElementById('input');
const textChars = inputEl.value.split('');
// 初始化绘图状态
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = '30px ULTRAKILL';
ctx.textAlign = 'center'; // 关键:设为 center,后续只需指定中心点 x
ctx.textBaseline = 'middle'; // 垂直居中对齐
const y = canvas.height / 2;
let i = 0;
let animationId = null;
// 使用 setTimeout + setInterval 易导致定时器失控,推荐改用 requestAnimationFrame 或单次 setInterval
setTimeout(() => {
animationId = setInterval(() => {
if (i <p>✅ <strong>关键改进说明:</strong> </p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher"><img
src="https://img.php.cn/upload/skill/000/000/081/179109368394970.jpg" alt="Wechat HTML Publisher" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher" class="overflowclass">Wechat HTML Publisher</a>
<p class="overflowclass">直接上传HTML富文本到微信公众号草稿箱。支持完整的HTML格式,无需Markdown转换。</p>
</div>
<a rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
-
ctx.textAlign = 'center'+ctx.textBaseline = 'middle':配合fillText(text, x, y),使x直接代表文本水平中心、y代表垂直中心,大幅简化定位逻辑; - 每次重绘完整字符串:而非只画单个字符,避免因字体度量微小误差累积导致错位;
-
measureText()动态测量:确保即使混合字符(如空格、标点、全角符号)也能精准居中; -
定时器清理:
clearInterval(animationId)防止重复调用或内存泄漏。
⚠️ 注意事项:
- 字体必须完全加载后才能准确测量宽度,因此
genText()最好在document.fonts.ready后调用,或添加加载状态判断; - 若需高性能长文本动画,可缓存
measureText结果或使用getBoundingClientRect()配合离屏 canvas 预渲染; - 移动端需注意
requestAnimationFrame替代setInterval以获得更流畅帧率。
该方案不仅复现了 ULTRAKILL 式的紧凑、居中、逐字浮现效果,也为后续扩展(如颜色渐变、缩放动画、打字音效)提供了清晰稳定的渲染基础。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










