javascript - 在多边形中心绘制文本

标签 javascript canvas html5-canvas

我从数据库中获取了一组多边形。每个形状可以是三 Angular 形、矩形、正方形或任何多边形。

我想在每个多边形的中心绘制文本。字体大小必须根据每个多边形的大小动态变化。文本颜色应与线条颜色匹配。

数据库示例:

colored 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>

最佳答案

主要逻辑说明:

  • 通过算术平均公式计算出的多边形I的中心
  • 我通过使用 font-size = 300 获取文本宽度来计算字体大小(但您可以根据需要更改第一个检查大小),然后检查是否 text with大于两个最近的点之间的最小距离(我认为如果文本位于多边形的中心,这是一个很好的限制)。如果是,那么我开始使用二分搜索算法找到正确的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>

关于javascript - 在多边形中心绘制文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72402212/

相关文章:

d3.js - 在 Canvas 上使用 D3 轴功能

javascript - 如何在javascript中向onclick函数发送参数

javascript - onkeydown 事件在 Canvas 上不起作用?

javascript - 在 "round" Canvas 上画线

javascript - 如何使用 onclick 将对象放入 Canvas 中?

javascript - HTML5 Canvas - 使用鼠标通过 anchor 旋转

javascript - 我如何合并 datauris 类型 : image/png to mpg video file in javascript

javascript - Canvas 仅缩放尺寸但不缩放坐标

javascript - express.js request.query 为空

javascript - HTML 样式表更改错误