将文本放在多边形的中心

Put Text to center of polygons

我从具有多个多边形的数据库中获取了一组多边形。我想把文本放在多边形的中心。

可能是三角形也可能是长方形也可能是正方形也可能是多边形

字体大小必须根据多边形大小动态变化。

文字颜色与线条颜色相同。

来自数据库的示例:

var polygons = [
  {
    text: "ROI",    color: "#00ff00",
    jointLength: 5,    lineWidth: 3,
    X: [890, 893, 409, 21, 27],    Y: [658, 205, 199, 556, 659],
  },  {
    text: "Lane 3",    color: "#ff0000",
    jointLength: 4,    lineWidth: 3,
    X: [915, 911, 643, 879],    Y: [5, 682, 683, 2],
  },  {
    text: "Lane 4",    color: "#ff0000",
    jointLength: 4,    lineWidth: 3,
    X: [888, 656, 170, 701],    Y: [2, 680, 682, 1],
  },  {
    text: "Lane 5",    color: "#ff0000",
    jointLength: 5,    lineWidth: 3,
    X: [712, 182, 4, 4, 590],    Y: [1, 681, 682, 532, 1],
  },  {
    text: "Speed",    color: "#0000ff",
    jointLength: 4,    lineWidth: 3,
    X: [290, 911, 873, 5],    Y: [367, 357, 668, 664],
  }
];

polygons.forEach((polygon) => {
  const ctx = document.getElementById("canvas").getContext("2d");
  ctx.strokeStyle = polygon.color;
  ctx.lineWidth = polygon.lineWidth;
  ctx.beginPath();
  ctx.moveTo(polygon.X[0], polygon.Y[0]);
  for (let i = 1; i < polygon.jointLength; i++) {
    ctx.lineTo(polygon.X[i], polygon.Y[i]);
  }
  ctx.closePath();
  ctx.stroke();
});
<canvas id="canvas" width=999 height=999></canvas>

主要逻辑解释:

  • 我用算术平均值公式计算的多边形中心
  • 字体的大小我通过font-size = 300获取文本的宽度来计算(但是你可以根据需要更改第一个检查大小)然后检查text with是否大于最小的2 个最近的点之间的距离(如果文本位于多边形的中心,我认为这是一个很好的限制)。如果是,那么我将 font-size 减一(但您可以使用任何步长)

由于这个逻辑,第二个多边形中的文本比它应该的要小,因为我们在顶部有 2 个点,它们彼此非常接近

有一个代码(打开整页以获得更好的可见性):

const polygons = [
  {
    text: "ROI",
    color: "red",
    jointLength: 5,
    lineWidth: 3,
    X: [890, 893, 409, 21, 27],
    Y: [658, 205, 199, 556, 659],
  },
  {
    text: "Lane 3",
    color: "blue",
    jointLength: 4,
    lineWidth: 3,
    X: [915, 911, 643, 879],
    Y: [5, 682, 683, 2],
  },
  {
    text: "Lane 4",
    color: "green",
    jointLength: 4,
    lineWidth: 3,
    X: [888, 656, 170, 701],
    Y: [2, 680, 682, 1],
  },
  {
    text: "Lane 5",
    color: "orange",
    jointLength: 5,
    lineWidth: 3,
    X: [712, 182, 4, 4, 590],
    Y: [1, 681, 682, 532, 1],
  },
  {
    text: "Speed",
    color: "purple",
    jointLength: 4,
    lineWidth: 3,
    X: [290, 911, 873, 5],
    Y: [367, 357, 668, 664],
  },
];

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 1000;

class Polygon {
  #ctx;
  #dots = [];
  #text;
  #color;
  #lineWidth;
  #dotsCount;
  
  constructor(ctx, data) {
    this.#ctx = ctx;
    this.#text = data.text;
    this.#color = data.color;
    this.#lineWidth = data.lineWidth;
    this.#dotsCount = data.jointLength;
    
    for (let i = 0; i < this.#dotsCount; ++ i) {
      this.#dots.push({x: data.X[i], y: data.Y[i]})
    }
  }
  
  #getCenterCoords() {
    const x = this.#dots.reduce((sum, dot) => sum += dot.x, 0) / this.#dotsCount;
    const y = this.#dots.reduce((sum, dot) => sum += dot.y, 0) / this.#dotsCount;
    
    return {x, y};
  }
  
  #distance = (dot1, dot2) => Math.sqrt((dot1.x - dot2.x) ** 2 + (dot1.y - dot2.y) ** 2);
  
  #getMinimalDistanceBetweenDots() {
    let minDist = Infinity;
    
    for (let i = 0; i < this.#dotsCount; ++i) {
      const dot1 = this.#dots[i];
      
      for (let j = i + 1; j < this.#dotsCount; ++j) {
        const dot2 = this.#dots[j];
        const dist = this.#distance(dot1, dot2);
        
        if (dist < minDist) minDist = dist;
      }
    }
    
    return minDist;
  }
  
  #getTextSize() {
    const minAvailableWidth = this.#getMinimalDistanceBetweenDots();
    
    let rightBound = 300;
    let leftBound = 0;
    let fontSize = rightBound;

    while (rightBound - leftBound > 1) {
    
      fontSize = Math.round((leftBound + rightBound) / 2);
      this.#ctx.font = `${fontSize}px verdana`;
      const textSize = this.#ctx.measureText(this.#text).width;
      
      if (textSize > minAvailableWidth) {
        rightBound = fontSize;
        continue;
      }
      
      if (textSize < minAvailableWidth) {
        leftBound = fontSize;
        continue;
      }
      
      if (textSize === minAvailableWidth) {
        break;
      }
    }
    
    return fontSize;
  }
  
  draw() {
    const path = new Path2D();
    const firstDot = this.#dots[0];
    const center = this.#getCenterCoords();
  
    this.#dots.forEach(dot => path.lineTo(dot.x, dot.y));

    path.lineTo(firstDot.x, firstDot.y);

    this.#ctx.strokeStyle = this.#color;
    this.#ctx.lineWidth = this.#lineWidth;
    this.#ctx.lineCap = 'round';
    this.#ctx.lineJoin = 'round';
    this.#ctx.stroke(path);
    
    this.#ctx.font = `${this.#getTextSize()}px verdana`;
    this.#ctx.fillStyle = this.#color;
    this.#ctx.textAlign = 'center'; 
    this.#ctx.fillText(this.#text, center.x, center.y);
  }
}

polygons.forEach((polygon) => new Polygon(ctx, polygon).draw());
<canvas id="canvas"></canvas>