【问题标题】:Path centroid calculation路径质心计算
【发布时间】:2022-11-21 15:11:58
【问题描述】:

我试图在每个 path 元素的中心显示一个图标,但它看起来不正确

我的代码只是根据 path 的宽度和高度计算中心点

const center = {
  x: (bbox.x - svg_box.x) + bbox.width / 2,
  y: (bbox.y - svg_box.y) + bbox.height / 2,
}

JSFiddle

这可以使用质心函数来改进吗?或者使用d3?
我不知道如何使用 d3 找到现有路径的质心。

谢谢

【问题讨论】:

  • 我会画一个垂直的花瓣(如果你愿意,也可以是水平的)并计算路径的中心。 <use> 使用花瓣 5 次。将 <use> 与图标放在一个组中,并相应地旋转组(0*72、1*72、2*72 ...等)。
  • @enxaneta 它需要是动态的,因为用户可以导入他们喜欢的任何 svg

标签: javascript svg d3.js centroid


【解决方案1】:

D3 有两种质心方法:arc.centroidpath.centroid(来自d3-geo),并且没有一种方法可以像您在此处使用的那样处理路径元素。

但是,我们可以使用 path.centroid 来获取这些路径的质心,但它非常笨拙:您必须根据您的实际路径创建一个 geoJSON 对象,才能将该对象传递给 path.centroid。因此,您最好自己创建一个。

也就是说,让我们看看这种方法是如何工作的。我们可以遍历每条路径,获取它的长度并设置一个虚拟的 geoJSON 对象:

const pathLength = n[i].getTotalLength();
let index = 0;
const geoJSONObject = {
    "type": "Polygon",
    "coordinates": [
      []
    ]
};

然后,我们沿着路径移动并填充 geoJSON 对象(这里 400/1237 只是计算视口值的一种快速方法,如果需要,您可以使用适当的矩阵)...

while (index < pathLength) {
    const point = n[i].getPointAtLength(index);
    geoJSONObject.coordinates[0].push([point.x * (400 / 1237), point.y * (400 / 1232)]);
    index += precision;
};

...最后我们将该对象传递给path.centroid

const centroid = path.centroid(geoJSONObject);

这是带有该解决方案的 sn-p:

const controls = d3.select(".controls"),
  path = d3.geoPath()
  .projection(d3.geoIdentity()),
  precision = 100;

d3.selectAll("path").each((_, i, n) => {
  const pathLength = n[i].getTotalLength();
  let index = 0;
  const geoJSONObject = {
    "type": "Polygon",
    "coordinates": [
      []
    ]
  };
  while (index < pathLength) {
    const point = n[i].getPointAtLength(index);
    geoJSONObject.coordinates[0].push([point.x * (400 / 1237), point.y * (400 / 1232)]);
    index += precision;
  };
  const centroid = path.centroid(geoJSONObject);
  controls.append("div")
    .style("left", centroid[0] + "px")
    .style("top", centroid[1] + "px");
})
.container {
  position: relative;
  display: inline-flex;
}

path {
  outline: 1px solid #0F0;
}

.controls {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.controls>div {
  position: absolute;
  width: 5px;
  height: 5px;
  background-color: red;
}
<script src="https://d3js.org/d3.v7.min.js"></script>

<div class="container">

  <div class="controls"></div>

  <?xml version="1.0" encoding="utf-8"?>
  <!-- Generator: Adobe Illustrator 25.4.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
  <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" x="0px" y="0px" viewBox="0 0 1237 1232" style="enable-background:new 0 0 1237 1232;" xml:space="preserve">
        <style type="text/css">
          .st1 {
            fill: none;
            stroke: #000000;
            stroke-miterlimit: 10;
          }

        </style>
        <g>
          <path class="st1" d="M1036.3,1040.8C893.1,896.6,750.5,753,607.8,609.4c0.1-0.4,0.3-0.8,0.4-1.2c3.4,0.4,6.9,0.8,10.3,1.4
        c37.9,6.1,75.9,12.3,113.8,18.5c40.4,6.5,80.8,13.1,121.2,19.6c40.2,6.5,80.5,13,120.7,19.5c39.9,6.5,79.8,12.9,119.7,19.4
        c36.5,5.9,72.9,11.8,109.4,17.8c1.8,0.3,3.9,1.3,4.8,2.6c0.5,0.7-1,3.2-2.1,4.5c-35.1,42-67.4,86.1-92.8,134.8
        c-20.4,39.2-36.2,80.4-51.4,121.9c-8.4,23-16.1,46.2-24.1,69.3C1037.4,1038.2,1037,1039.1,1036.3,1040.8z" />
          <path class="st1" d="M604.1,609.4c0.9,5.3,1.9,10.6,2.7,15.9c3.6,23.1,7.2,46.2,10.7,69.3c3.1,20.5,6.2,41,9.4,61.4
        c3.5,22.4,7.1,44.9,10.5,67.3c3.2,20.5,6.3,41,9.4,61.4c3.2,20.5,6.4,40.9,9.6,61.4c2.8,18.2,5.6,36.4,8.4,54.6
        c3.5,22.6,7,45.2,10.5,67.8c3.2,20.5,6.3,40.9,9.5,61.4c3.1,20.3,6.3,40.6,9.4,60.9c0.7,4.7,1.8,9.5,2.3,14.2
        c0.2,1.8,0.2,4.5-0.9,5.5c-0.9,0.8-3.7,0.2-5.3-0.4c-43.3-17.2-87-33.3-131.7-46.7c-31.9-9.5-64.4-14.5-97.8-15.8
        c-45-1.8-89.9,0.3-135.9,2.9c92.9-180.7,185.4-360.9,278-541.1C603.4,609.4,603.8,609.4,604.1,609.4z" />
          <path class="st1" d="M511.8,1.5c31.1,200.5,62,400.4,93,600.2c-0.3,0.2-0.6,0.4-0.9,0.6c-2.2-2.1-4.5-4.2-6.7-6.3
        c-26.4-26.6-52.8-53.3-79.2-79.9c-22.7-22.8-45.4-45.6-68.1-68.4c-49.7-50-99.3-100-149-150c-36.8-37-73.5-74-110.3-111
        c-3.8-3.8-7.5-7.5-11.1-11.5c-1.3-1.5-2.1-3.5-3.1-5.2c1.7-0.8,3.4-1.9,5.2-2.3c21.6-4.8,43.3-9,64.7-14.4
        c44.9-11.3,89.2-24.6,130.9-44.9c39-19,72.8-45.5,103.9-75.5C491.4,22.9,501.1,12.4,511.8,1.5z" />
          <path class="st1" d="M600.9,606.3c-10.5,5.3-21,10.6-31.5,16c-69.1,34.9-138.3,69.8-207.4,104.7c-62.6,31.6-125.2,63.2-187.7,94.9
        c-36.4,18.4-72.8,36.9-109.2,55.3c-0.9,0.5-1.7,1.1-2.7,1.3c-1.4,0.4-2.8,0.5-4.2,0.7c-0.2-1.6-0.9-3.2-0.6-4.6
        c1.6-9.2,3.8-18.2,4.9-27.5c2.3-20.2,4.6-40.3,5.7-60.6c1.8-30.3,0.1-60.6-4-90.7c-5.7-41.4-15.8-81.6-31.6-120.3
        C23.7,554,13,533.3,3.2,512.2c-0.5-1-1-2-2.1-4.3c200.5,32.5,400.1,64.8,599.6,97C600.7,605.4,600.8,605.9,600.9,606.3z" />
          <path class="st1" d="M1150.2,329.6c-180,90.5-359.5,180.8-540.4,271.8c1.2-2.8,1.6-4.3,2.3-5.7c30.1-58.7,60.3-117.3,90.4-176
        c22.5-43.9,45-87.7,67.5-131.6c37.8-73.6,75.6-147.3,113.5-220.8c0.8-1.6,2.5-2.7,3.8-4.1c1.2,1.5,2.8,2.7,3.7,4.4
        c24.3,46.6,51.6,91.3,84.4,132.6c33.1,41.6,74.4,73.3,119.8,99.9c16.9,9.9,34.6,18.6,51.9,27.8
        C1148.1,328.4,1148.9,328.9,1150.2,329.6z" />
        </g>
      </svg>
</div>

【讨论】:

    【解决方案2】:

    谢谢@Gerardo,你给了我使用 polylabel 编写这段代码的想法,它可能比 d3 占用空间更小

    import polylabel from "@mapbox/polylabel"
    
    function centroid (path: SVGPathElement, resolution = 100): [number, number] {
      const poly = []
      const step = path.getTotalLength() / resolution
      for (let i = 0; i < resolution; i++) {
        const point = path.getPointAtLength(i * step)
        poly.push([point.x, point.y])
      }
    
      return polylabel([poly], 1.0, false)
    }
    

    【讨论】:

      猜你喜欢
      • 2012-08-17
      • 1970-01-01
      • 1970-01-01
      • 2015-06-04
      • 1970-01-01
      • 2012-04-06
      • 2019-05-12
      相关资源
      最近更新 更多