文本

5 阅读5分钟

一 🎨 核心文本属性

Canvas 的文本样式和对齐由以下几个关键属性控制,它们都作用于 Canvas 的 2D 渲染上下文(通常简称为 ctx)。

1. font 属性

这是设置文本字体样式的核心属性,其语法与 CSS 的 font 属性完全相同。

  • 默认值: 10px sans-serif
  • 语法: ctx.font = "font-style font-variant font-weight font-size font-family";
属性分量描述常用值
font-style字体样式normal (正常), italic (斜体), oblique (倾斜体)
font-variant字体变体normal (正常), small-caps (小型大写字母)
font-weight字体粗细normal (正常), bold (粗体), 100900 的数字
font-size字体大小必须包含单位,如 16px, 2em
font-family字体族Arial, "Microsoft YaHei", sans-serif

示例:

ctx.font = "bold italic 24px Arial"; // 粗体、斜体、24像素、Arial字体
ctx.font = "30px 'Microsoft YaHei', sans-serif"; // 30像素,优先使用微软雅黑

正确的书写顺序必须遵循以下规则:

font-stylefont-variantfont-weightfont-sizefont-family

📝 顺序规则详解

  1. font-style (字体样式) 例如 italic (斜体) 或 oblique (倾斜体)。

  2. font-variant (字体变体) 例如 small-caps (小型大写字母)。

  3. font-weight (字体粗细) 例如 bold (粗体) 或 700

  4. font-size (字体大小) 例如 16px2em这是第一个必须指定的属性

  5. font-family (字体族) 例如 Arial"Microsoft YaHei"这是第二个必须指定的属性

✅ 核心要点

  • font-sizefont-family 是必选项:如果缺少这两个值中的任何一个,整个 font 属性设置将不会生效。
  • font-size 必须在 font-family 之前:这两者的相对顺序不能颠倒。
  • 前三项顺序可互换font-stylefont-variantfont-weight 这三项之间的顺序可以任意调换,并且它们都是可选项,可以省略。

💻 代码示例

以下是一些正确和错误的写法示例:

// ✅ 正确写法
ctx.font = "italic small-caps bold 24px Arial"; // 完全符合顺序
ctx.font = "bold italic 24px Arial"; // 省略了 font-variant
ctx.font = "24px 'Microsoft YaHei', sans-serif"; // 只写了两个必选项

// ❌ 错误写法
ctx.font = "24px bold Arial"; // font-weight 写在了 font-size 后面
ctx.font = "Arial 24px bold"; // font-family 写在了 font-size 前面
ctx.font = "bold Arial"; // 缺少了必选项 font-size

2. textAlign 属性

此属性用于设置文本的水平对齐方式。对齐的基准点是你在绘制文本时指定的 x 坐标。

  • 默认值: start
  • 可选值:
    • start: 默认值。文本从起点开始绘制(在从左到右的语言环境中,效果同 left)。
    • end: 文本终点对齐起点(在从左到右的语言环境中,效果同 right)。
    • left: 文本左对齐。
    • right: 文本右对齐。
    • center: 文本中心点对齐起点。

示例:

ctx.textAlign = "center"; // 文本将以x坐标为中心进行水平居中
ctx.fillText("居中的文本", canvas.width / 2, 100);

3. textBaseline 属性

此属性用于设置文本的垂直对齐方式,即文本的基线。对齐的基准点是你在绘制文本时指定的 y 坐标。

  • 默认值: alphabetic
  • 可选值:
    • top: 文本基线在文本块的顶部。
    • hanging: 文本基线是悬挂基线(常用于印度系文字)。
    • middle: 文本基线在文本块的中间。
    • alphabetic: 文本基线是标准的字母基线(默认值,适用于大多数拉丁字母)。
    • ideographic: 文本基线是表意字基线(适用于中文、日文等)。
    • bottom: 文本基线在文本块的底部。

示例:

ctx.textBaseline = "middle"; // 文本将以y坐标为中心进行垂直居中
ctx.fillText("垂直居中的文本", 100, canvas.height / 2);

4. direction 属性

此属性用于设置文本的书写方向。

  • 默认值: inherit
  • 可选值:
    • ltr: 从左到右 (Left-to-Right)。
    • rtl: 从右到左 (Right-to-Left)。
    • inherit: 继承父元素的设置。
  • 对于纯英文 'hello font',ltr 和 rtl 看起来是一样的
  • 使用希伯来语/阿拉伯语文字,或混合文本时,效果才明显:
    • ltr: 文本从左到右排列
    • rtl: 文本从右到左排列(起始位置会变化)

二 ✍️ 核心文本方法

设置好属性后,需要使用以下方法来实际绘制文本。

1. fillText() 方法

在指定坐标 (x, y) 处绘制实心填充的文本。

  • 语法: ctx.fillText(text, x, y [, maxWidth]);
  • 参数:
    • text: 要绘制的文本字符串。
    • x: 文本起始点的 x 轴坐标。
    • y: 文本起始点的 y 轴坐标。
    • maxWidth (可选): 允许的最大文本宽度。如果文本宽度超过此值,浏览器会自动缩放字体以适应宽度。

2. strokeText() 方法

在指定坐标 (x, y) 处绘制文本的轮廓(描边)

  • 语法: ctx.strokeText(text, x, y [, maxWidth]);
  • 参数: 与 fillText 相同。

3. measureText() 方法

用于测量文本的宽度,返回一个 TextMetrics 对象。这在需要动态计算文本位置时非常有用。

  • 语法: ctx.measureText(text)
  • 返回值: 一个包含文本宽度等信息的 TextMetrics 对象。你可以通过 .width 属性获取文本的宽度(以像素为单位)。

示例:

const text = "Hello Canvas";
const metrics = ctx.measureText(text);
console.log(`文本宽度为: ${metrics.width}px`);

🧩 综合示例

下面是一个完整的示例,展示了如何结合使用这些属性和方法在 Canvas 上绘制一个水平和垂直都居中的文本。

<canvas id="myCanvas" width="400" height="200" style="border:1px solid #ccc;"></canvas>

<script>
  const canvas = document.getElementById('myCanvas');
  const ctx = canvas.getContext('2d');

  // 1. 设置文本样式
  ctx.font = 'bold 40px "Microsoft YaHei", sans-serif';
  ctx.fillStyle = '#336699'; // 设置填充颜色

  // 2. 设置对齐方式
  ctx.textAlign = 'center';     // 水平居中
  ctx.textBaseline = 'middle';  // 垂直居中

  // 3. 计算画布中心点坐标
  const x = canvas.width / 2;
  const y = canvas.height / 2;

  // 4. 绘制实心文本
  ctx.fillText('Canvas 文本', x, y);

  // 5. 绘制描边文本
  ctx.strokeStyle = '#ffcc00';
  ctx.lineWidth = 1;
  ctx.strokeText('Canvas 文本', x, y);
</script>

三 源码案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
    <canvas id="myCanvas"></canvas>
    <script>
        const canvas = document.getElementById('myCanvas');
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        // 1.textAlign 属性
        const ctx = canvas.getContext('2d');
        ctx.beginPath();
        ctx.moveTo(250, 50);
        ctx.lineTo(250, 350);
        ctx.strokeStyle = 'skyblue';
        ctx.stroke();
        const textAligns = ['start', 'end', 'left', 'right', 'center'];
        for (let i = 0; i < textAligns.length; i++) {
            ctx.textAlign = textAligns[i];
            ctx.font = '20px Arial';
            ctx.fillText('textAlign: ' + textAligns[i], 250, 100 + i * 50);
        }
        // 2.textBaseline 属性
        ctx.beginPath();
        ctx.moveTo(550, 200);
        ctx.lineTo(1450, 200);
        ctx.strokeStyle = 'red';
        ctx.stroke();
        const textBaselines = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'];
        for (let i = 0; i < textBaselines.length; i++) {
            ctx.textBaseline = textBaselines[i];
            ctx.font = '20px Arial';
            ctx.fillText(textBaselines[i], 600 + i*150, 200);
        }
        // 
        ctx.beginPath();
        ctx.textAlign = 'left'
        ctx.textBaseline = 'alphabetic'
        ctx.direction = 'ltr'
        ctx.font = "italic small-caps bold 24px Arial"; // 完全符合顺序
        ctx.fillText('hello font', 50, 50);
        ctx.lineWidth = 1;
        ctx.strokeStyle = '#eee';
        ctx.strokeText('hello font', 50, 50);


        // 3.direction 属性演示 - 需要 RTL 文字或配合 textAlign 才能看到效果
        ctx.beginPath();
        ctx.textAlign = 'left';
        ctx.textBaseline = 'alphabetic';

        // ltr (默认)
        ctx.direction = 'ltr';
        ctx.font = "24px Arial";
        ctx.fillText('LTR: שלום עולם (Shalom)', 50, 550);

        // rtl - 文字会从右向左排列
        ctx.direction = 'rtl';
        ctx.fillText('RTL: שלום עולם (Shalom)', 50, 600);

        // 混合文本对比
        ctx.direction = 'ltr';
        ctx.fillText('LTR: Hello 世界', 50, 650);
        ctx.direction = 'rtl';
        ctx.fillText('RTL: Hello 世界', 50, 700);

    </script>
</body>
</html>