文本绘制:fillText与strokeText

文本绘制,说白了就是在Canvas上写字。我刚开始接触Canvas时,觉得这有什么难的?不就是写几个字嘛。但实际做项目才发现,文本渲染的坑比想象中多得多。

Canvas提供了两种绘制文本的方式:fillTextstrokeText。前者是填充文字,后者是描边文字。你可以理解为——一个是用毛笔写,一个是用钢笔勾轮廓。

fillText:填充文字

这是最常用的方法。用法很简单:

ctx.fillText('Hello Canvas', x, y);

三个参数:要显示的文本、x坐标、y坐标。注意,这里的y坐标是文本基线的位置,不是文字顶部。我刚开始就犯过这个错,以为y是文字左上角,结果文字全跑偏了。

你可以先设置字体和颜色:

ctx.font = '24px Arial';
ctx.fillStyle = '#ff6600';
ctx.fillText('Canvas动画开发', 100, 100);

strokeText:描边文字

描边文字只画轮廓,不填充内部。用法和fillText一样:

ctx.strokeStyle = '#333';
ctx.lineWidth = 2;
ctx.strokeText('描边文字', 100, 200);

我在项目中遇到过一种情况:需要做文字特效,先描边再填充,让文字看起来有立体感。做法就是先画描边,再在上面画填充,注意偏移几个像素:

ctx.strokeStyle = '#000';
ctx.lineWidth = 3;
ctx.strokeText('立体效果', 100, 300);
ctx.fillStyle = '#fff';
ctx.fillText('立体效果', 100, 300);
小技巧:描边和填充的顺序很重要。先描边再填充,描边会被填充盖住一半,看起来更自然。反过来先填充再描边,描边会完全覆盖在填充上面,效果比较生硬。

字体设置:font属性

ctx.font的语法和CSS的font属性基本一致。格式是:

ctx.font = 'font-style font-variant font-weight font-size/line-height font-family';

但说实话,在Canvas里你不需要写那么全。我一般只写字号和字体族:

ctx.font = '20px "Microsoft YaHei", sans-serif';

常用的写法:

  • '16px Arial' — 基础用法
  • 'bold 24px "Microsoft YaHei"' — 加粗+字号+字体
  • 'italic 18px "Times New Roman"' — 斜体
  • '600 32px "PingFang SC", sans-serif' — 字重用数字
注意:Canvas的font属性不支持简写中的line-height。你写了也没用,会被忽略。我曾经在这个坑里折腾了半天,后来查文档才发现Canvas根本不解析line-height。

文本对齐:textAlign

文本对齐控制的是文字在水平方向上的定位方式。取值有:

说明 实际效果
start 默认值,从左到右文本左对齐 文字从指定点开始向右排
end 从右到左文本右对齐 文字在指定点左侧结束
left 左对齐 和start在LTR下相同
right 右对齐 和end在LTR下相同
center 居中对齐 文字中心在指定点上

举个例子:

ctx.textAlign = 'center';
ctx.fillText('居中文字', 200, 100);

ctx.textAlign = 'right';
ctx.fillText('右对齐', 200, 150);

我个人习惯在做图表标注时用center,做列表文字时用startleft。你想想看,如果柱状图顶部要显示数值,用center对齐是不是最省心?

文本基线:textBaseline

这个属性控制的是文字在垂直方向上的对齐方式。我敢说,80%的Canvas新手都在这里栽过跟头。

取值有:

说明
alphabetic 默认值,字母基线
top 文字顶部对齐
hanging 悬挂基线(用于印度文等)
middle 文字中部对齐
ideographic 表意文字基线(用于中文、日文)
bottom 文字底部对齐

为什么会有这么多基线?因为不同语言的文字,视觉重心不一样。英文的基线在字母底部,中文的基线在底部偏上一点。

我建议你在做中文项目时,直接用topmiddle。为什么?因为alphabetic会让中文偏上,看起来不居中。我曾经做一个答题App,文字在按钮里总是偏上,调了半天才发现是基线的问题。

ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillText('完美居中', canvas.width / 2, canvas.height / 2);
核心经验:如果你想让文字在某个矩形区域内完美居中,设置textAlign = 'center'textBaseline = 'middle',然后坐标取矩形中心点。这是最稳妥的做法。

measureText:测量文本

有时候你需要知道一段文字到底占多宽。比如做自适应排版、文字截断、气泡框等场景。

measureText方法返回一个TextMetrics对象,里面包含文字的宽度信息:

const metrics = ctx.measureText('Hello Canvas');
console.log(metrics.width); // 输出像素宽度

注意,它只能测量宽度,不能测量高度。为什么?因为Canvas的文本高度取决于字体大小和基线,没有直接API获取。我一般用字号乘以1.2来估算行高。

实际应用场景:

const text = '这是一段需要截断的文字';
const maxWidth = 200;

ctx.font = '16px "Microsoft YaHei"';
if (ctx.measureText(text).width > maxWidth) {
  // 截断处理
  let truncated = text;
  while (ctx.measureText(truncated + '...').width > maxWidth) {
    truncated = truncated.slice(0, -1);
  }
  ctx.fillText(truncated + '...', 50, 100);
} else {
  ctx.fillText(text, 50, 100);
}
实用技巧:measureText必须在设置了font之后调用,否则测量的是默认字体的大小。我习惯在测量前先设置好font,确保测量结果准确。

还有一个冷知识:TextMetrics对象除了width,还有actualBoundingBoxAscentactualBoundingBoxDescent,这两个属性可以帮你计算文字的实际高度。不过浏览器兼容性一般,生产环境慎用。

嗯,文本绘制这块内容就这些。说白了就是四个核心点:怎么画、用什么字体、怎么对齐、怎么测量。掌握了这些,Canvas文本操作基本就通了。下一章我们会讲图像绘制,到时候你会看到文本和图像结合能玩出什么花样来。