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

如何为HTML Canvas绘制的无向图节点和边添加可自定义对齐的文本?

Canvas无向图文本排版及扩展需求解决方案

一、节点文本居中对齐方案

Canvas文本默认左对齐、基线对齐,只需要调整内置的对齐属性,再以节点中心为坐标绘制即可实现居中:

const MARKER_SIZE = 5;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

// 绘制节点
ctx.fillRect(Math.floor(pixel.x), Math.floor(pixel.y), MARKER_SIZE, MARKER_SIZE);

// 绘制节点居中文本
ctx.font = "15px Arial";
ctx.textAlign = "center"; // 水平居中对齐
ctx.textBaseline = "middle"; // 垂直居中对齐
// 取节点中心作为文本绘制坐标
const nodeCenterX = Math.floor(pixel.x) + MARKER_SIZE / 2;
const nodeCenterY = Math.floor(pixel.y) + MARKER_SIZE / 2;
// 如果不想文本遮挡节点,可在Y轴加偏移量,比如nodeCenterY + 15 就会显示在节点下方
ctx.fillText("California", nodeCenterX, nodeCenterY);

如果需要左对齐、右对齐等其他对齐方式,修改textAlign为left/right、textBaseline为top/bottom即可。

二、边文本显示在边上方方案

首先计算边的中点坐标,再做偏移即可实现,也可以额外配置旋转让文本和边平行:

基础版(文本水平显示,不跟随边旋转)

// 绘制边
ctx.beginPath();
ctx.moveTo(fromPixel.x + MARKER_SIZE/2, fromPixel.y + MARKER_SIZE/2);
ctx.lineTo(toPixel.x + MARKER_SIZE/2, toPixel.y + MARKER_SIZE/2);
ctx.stroke();

// 绘制边上方文本
const midX = (fromPixel.x + toPixel.x) / 2 + MARKER_SIZE / 2;
const midY = (fromPixel.y + toPixel.y) / 2 + MARKER_SIZE / 2;
ctx.font = "15px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "bottom"; // 文本底部对齐中点,避免和边重叠
ctx.fillText(edge.distance + " km", midX, midY - 5); // 向上偏移5px

进阶版(文本和边平行显示)

const midX = (fromPixel.x + toPixel.x) / 2 + MARKER_SIZE / 2;
const midY = (fromPixel.y + toPixel.y) / 2 + MARKER_SIZE / 2;
// 计算边的倾斜角度
const angle = Math.atan2(toPixel.y - fromPixel.y, toPixel.x - fromPixel.x);

ctx.save(); // 保存当前画布状态
ctx.translate(midX, midY); // 移动坐标系到边中点
ctx.rotate(angle); // 旋转坐标系和边平行
ctx.font = "15px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
ctx.fillText(edge.distance + " km", 0, -5); // 向上偏移5px
ctx.restore(); // 恢复画布状态,避免影响后续绘制

三、用户自行调整对齐方式实现方案

将所有文本相关的参数抽离为可配置变量,页面上加简单的表单控件(下拉选择框、数值输入框),监听控件变化后全量重绘Canvas即可,逻辑如下:

// 抽离可配置参数
const drawConfig = {
  nodeTextAlign: 'center',
  nodeTextBaseline: 'middle',
  nodeOffsetY: 0,
  edgeOffsetY: 5,
  edgeTextRotate: false
};

// 监听控件修改参数,示例为节点对齐方式下拉框
document.querySelector('#node-align-select').addEventListener('change', (e) => {
  drawConfig.nodeTextAlign = e.target.value;
  redrawCanvas(); // 触发全量重绘,清空画布后重新绘制所有节点、边、文本即可
});

简单无向图的元素数量少,全量重绘性能完全足够,不需要做局部更新。

四、简化绘图的JS库推荐

如果你不想手动处理所有底层绘制逻辑,可以选择封装好的绘图库降低开发成本:

  • Konva.js:Canvas 2D绘图封装库,内置了图形、文本的对齐、事件交互能力,修改属性会自动触发重绘,不用自己写重绘逻辑,对新手友好
  • D3.js:主打数据可视化,内置了力导向图等现成的无向图布局方案,不用自己计算节点排布位置
  • mxGraph:专门做关系图、流程图的封装库,自带文本对齐、拖拽调整功能,开箱即用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 17:15:02