【问题标题】:Adding text labels depending on the position of the line (canvas)根据线的位置添加文本标签(画布)
【发布时间】:2020-02-29 12:14:41
【问题描述】:

美好的一天 Stackoverflowers,

我正在使用画布创建平面图。我有点卡在一行旁边添加标签的部分。它应该水平和垂直居中并放置在线条之外。

这就是我想要实现的目标。

此平面图由 4 行组成。我希望根据行数为每行创建一个标签。

this.floorplan.getWalls().forEach((wall) => {
    this.drawWall(wall);
});

private drawWall(wall: Wall) {
    var startX = wall.startX();
    var startY = wall.startY();
    var endX = wall.endX();
    var endY = wall.endY();

    this.context.beginPath();
    this.context.moveTo(startX, startY);
    this.context.lineTo(endX, endY);
    this.context.lineWidth = width;
    this.context.strokeStyle = color;
    this.context.stroke();

    // add labels here
    var label = wall.getLabel();
}

希望有人能解释一下。

谢谢。

【问题讨论】:

  • 嗨!您可以从墙壁坐标中推断出居中位置。然后,我注意到你的标签在墙外。因此,也许您可​​以为您的Wall 对象添加一个属性,例如indoor: 'up'(在墙D 的情况下)以显示面向室内区域的墙的一侧超出了线。所以如果你想放置你的标签,你将它放在这条线下。 (您也可以在户外使用,具体取决于最适合您的方式)
  • 好的,谢谢。得到它的工作。刚刚添加了一个带有位置的属性标签详细信息,所以我不必担心即时计算它。

标签: javascript html canvas html5-canvas


【解决方案1】:

为确保您知道线的哪一侧在外侧或内侧,您必须统一线的方向,以便它们始终沿顺时针方向移动。

然后很容易找到线的哪一侧在外面。如果站在线的起点沿线看,外面在你的左边。

该示例显示了如何为已转换(沿线)未转换的文本和沿线的文本(确保始终向上)呈现行的左侧的文本

将未转换的文本从线条移开,使文本中心位于线条中心左侧 90 度,并移动以使角尽可能靠近。我在文本中心添加了一条细线,以显示文本中心与线对齐的位置。

requestAnimationFrame(mainLoop);
const w = canvas.width;
const h= canvas.height;
const ctx = canvas.getContext("2d");

function drawLabledLine(label, x, y, x1, y1, fontSize = 12) {
    ctx.font = fontSize + "px arial";
    ctx.lineWidth = 1;
    ctx.fillStyle = ctx.strokeStyle = "black";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle"; // Rather than mess around with this
                                 // I use the same alignment and just change the
                                 // position to put the text where it is needed
  
    // normalize line
    var nx = x1 - x;
    var ny = y1 - y;
    const dist = (nx * nx + ny * ny) ** 0.5;
    nx /= dist;
    ny /= dist;
    
    // set the transform
    ctx.setTransform(nx, ny, -ny, nx, x, y);
    
    // The transformed is now aligned to the line. Along the line is X and 
    // 90 deg clockwise is right of the line
    
    ctx.beginPath();
    ctx.lineTo(0, 0);
    ctx.lineTo(dist, 0);
    ctx.lineTo(dist - 4, -4);
    ctx.stroke();


    var offset = -fontSize * 0.6;
    var distAlong = dist / 2; /// where to put the line
    
    // Use the normal's of the line to workout how
    // to render the text so it is always readable
    if (nx < 0) {
       ctx.setTransform(-nx, -ny, ny, -nx, x, y);
       offset = -offset;
       distAlong = - distAlong;
    }
    ctx.fillText(label, distAlong, offset);

}


function drawLabledLineTextHor(label, x, y, x1, y1, fontSize = 12) {

    ctx.font = fontSize + "px arial";
    ctx.lineWidth = 1;
    ctx.fillStyle = ctx.strokeStyle = "black";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle"; // Rather than mess around with this
                                 // I use the same alignment and just change the
                                 // position to put the text where it is needed
  
    // normalize line
    var nx = x1 - x;
    var ny = y1 - y;
    const dist = (nx * nx + ny * ny) ** 0.5;
    nx /= dist;
    ny /= dist;
    
    // set the transform
    ctx.setTransform(nx, ny, -ny, nx, x, y);
    
    // The transformed is now aligned to the line. Along the line is X and 
    // 90 deg clockwise is right of the line
    
    ctx.beginPath();
    ctx.lineTo(0, 0);
    ctx.lineTo(dist, 0);
    ctx.lineTo(dist - 4, -4);
    ctx.stroke();


    // need the text width so that the text can be moved away from the line
    const textW = ctx.measureText(label).width;
    
    // Offset a little more than half the font size to stop text from
    // touching (top and (bottom if hanging char eg `jgq`))
    var offset = -fontSize * 0.575 - (textW / 2 * -ny) * (-ny < 0 ? -1 : 1);
    var distAlong = dist / 2; // where along the line to move out from (left)
                              // to place the text
    
    // Show center line 
    ctx.lineWidth = 0.25;
    ctx.beginPath();
    ctx.lineTo(dist / 2, 5);
    ctx.lineTo(dist / 2, offset + fontSize / 2 );
    ctx.stroke();
    
    
    // set transform origin to center of line
    ctx.setTransform(
        1, 0,  // x Axis
        0, 1,  // y Axis
        x + nx * distAlong, // origin
        y + ny * distAlong,
     );
    
    // The vector -ny,nx is CW (right of the line) so the offset is negative
    // to that direction as we want to move left of the line
    ctx.fillText(label, -ny * offset, nx * offset);

}

const points = [
    {x: -40, y: -40, tx: 0, ty: 0}, // tx,ty is tranformed pos
    {x:  40, y: -40, tx: 0, ty: 0},
    {x:  40, y:  40, tx: 0, ty: 0},
    {x: -40, y:  40, tx: 0, ty: 0},
];
const lines = [
    {txt: "A", p1: points[0], p2: points[1], method: drawLabledLine},
    {txt: "B", p1: points[1], p2: points[2], method: drawLabledLineTextHor},
    {txt: "Line C", p1: points[2], p2: points[3], method: drawLabledLine},
    {txt: "Line D", p1: points[3], p2: points[0], method: drawLabledLineTextHor},
]

function mainLoop(time) {
    ctx.setTransform(1,0,0,1,0,0); // default transform
    ctx.clearRect(0,0,w,h);
    var ang = time / 1000;

    // rotate and move to center of canvas all points
    const xAx = Math.cos(ang);
    const xAy = Math.sin(ang);
    for (const p of points) {
        p.tx = p.x * xAx - p.y * xAy + w / 2;
        p.ty = p.x * xAy + p.y * xAx + h / 2;
    }
 
    // render the lines
    for(const l of lines) {
        l.method(l.txt, l.p1.tx, l.p1.ty, l.p2.tx, l.p2.ty);
    }
    requestAnimationFrame(mainLoop);
}
&lt;canvas id="canvas" width="200" height="200"&gt;&lt;/canvas&gt;

【讨论】:

  • 好吧。我能说什么。这真太了不起了。我不需要手动添加标签。非常感谢,效果很好。
猜你喜欢
  • 1970-01-01
  • 2011-08-01
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
  • 2013-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多