You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas如何实现主文本下方右对齐且不超宽度的多行文本

Canvas文本绘制:实现主文本下方右对齐、自动换行的副标题

需求说明

现有Canvas代码已实现居中显示主文本Some String Here,需要添加副标题,满足以下要求:

  • 副标题位于主文本正下方,右对齐且不超出主文本的宽度范围
  • 若副标题宽度超过主文本宽度的70%,自动换行显示

现有代码与问题

原始主文本绘制代码

const canvas = createCanvas(600, 316);
const ctx = canvas.getContext('2d');

const codeName = 'Some String Here';

const image = await loadImage(...);

ctx.drawImage(image, 0, 0);

ctx.font = 'bold 30px Sans serif';
ctx.fillStyle = '#d75c5c';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const titleX = canvas.width / 2;
const titleY = canvas.height / 2;
ctx.fillText(codeName, titleX, titleY);

尝试的副标题代码(存在对齐问题)

const authorText = `Bottom String`
ctx.font = '20px Sans serif';
ctx.fillStyle = '#494949';
ctx.textAlign = 'right';
ctx.textBaseline = 'top';

const titleTextWidth = ctx.measureText(codeName).width;
const titleTextHeight = ctx.measureText(codeName).actualBoundingBoxAscent;
const spacing = 15;

const authorX = titleX + titleTextWidth; // 错误:主文本右边界是titleX + titleTextWidth/2
const authorY = titleY + titleTextHeight + spacing;
ctx.fillText(authorText, authorX, authorY);

问题:副标题向右超出主文本范围,原因是主文本居中显示,其右边界应为titleX + titleTextWidth/2,而非titleX + titleTextWidth,导致对齐基准点错误。

解决方案

步骤1:修正副标题对齐位置

调整副标题的X坐标为主文本的右边界,同时修正Y坐标计算(使用actualBoundingBoxDescent更准确,因为主文本基线是middle,下方偏移需取基线到主文本底部的距离加上间距)。

步骤2:实现自动换行逻辑

当副标题宽度超过主文本宽度的70%时,拆分文本为多行,每行宽度不超过主文本宽度的70%,依次向下绘制。

完整代码

const canvas = createCanvas(600, 316);
const ctx = canvas.getContext('2d');

const codeName = 'Some String Here';
const authorText = 'This is a long bottom text that needs to wrap when it exceeds 70% of the main text width';

// 加载并绘制背景图
const image = await loadImage(...);
ctx.drawImage(image, 0, 0);

// 绘制主文本
ctx.font = 'bold 30px Sans serif';
ctx.fillStyle = '#d75c5c';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const titleX = canvas.width / 2;
const titleY = canvas.height / 2;
ctx.fillText(codeName, titleX, titleY);

// 计算主文本相关参数
const titleMetrics = ctx.measureText(codeName);
const titleTextWidth = titleMetrics.width;
// 主文本从middle基线到底部的距离
const titleBottomOffset = titleMetrics.actualBoundingBoxDescent;
const spacing = 15; // 主文本与副标题的间距

// 绘制副标题配置
ctx.font = '20px Sans serif';
ctx.fillStyle = '#494949';
ctx.textAlign = 'right';
ctx.textBaseline = 'top';

// 计算副标题的最大允许宽度(主文本宽度的70%)
const maxSubtitleWidth = titleTextWidth * 0.7;
// 副标题的起始X坐标(主文本右边界)
const subtitleStartX = titleX + titleTextWidth / 2;
// 副标题的起始Y坐标(主文本底部 + 间距)
let subtitleY = titleY + titleBottomOffset + spacing;

// 自动换行处理函数
function drawWrappedText(text, x, y, maxWidth, lineHeight) {
    const words = text.split(' ');
    let currentLine = '';
    
    for (let i = 0; i < words.length; i++) {
        const testLine = currentLine + (currentLine ? ' ' : '') + words[i];
        const testWidth = ctx.measureText(testLine).width;
        
        if (testWidth > maxWidth && i > 0) {
            // 绘制当前行
            ctx.fillText(currentLine, x, y);
            currentLine = words[i];
            y += lineHeight;
        } else {
            currentLine = testLine;
        }
    }
    // 绘制最后一行
    ctx.fillText(currentLine, x, y);
}

// 获取副标题行高(基于字体大小,这里取字体大小的1.2倍)
const subtitleLineHeight = 20 * 1.2;

// 绘制副标题(支持自动换行)
drawWrappedText(authorText, subtitleStartX, subtitleY, maxSubtitleWidth, subtitleLineHeight);

关键说明

  1. 对齐修正:副标题的X坐标设为titleX + titleTextWidth/2,即主文本的右边界,配合textAlign: 'right',确保副标题右对齐且不超出主文本范围。
  2. Y坐标计算:使用titleMetrics.actualBoundingBoxDescent获取主文本从middle基线到底部的距离,加上间距后作为副标题的起始Y坐标,保证位置准确。
  3. 自动换行:通过拆分文本为单词,逐行测试宽度,超过最大允许宽度时换行,确保每行宽度不超过主文本宽度的70%。

内容的提问来源于stack exchange,提问作者GoodBoyNeon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 07:57:07