首页 > 解决方案 > 使用 Javascript 返回在 SVG 中单击了哪条线段

问题描述

我有一个带有一堆不同路径的 svg。一个示例如下所示:

<path d="M2943648.169104896,472020.4539518825L2943637.730875478,472048.6771215173" />

我正在尝试找到一种方法来获取已单击该行的哪一部分。我可以添加一个返回的 onclick 函数d,但这只会给我上面列出的完整字符串,我只需要相关部分。例如,如果用户单击该行的第一部分,它将返回正确的坐标 ( 2943648.169104896,472020.4539518825),或者如果他们单击第二个段,它将显示 d 值中的其他坐标。其中一些路径具有许多不同的线段。

我在这里找到了一个不推荐使用的方法的答案,我想知道最新的方法是什么。谢谢你。

标签: javascripthtmlwebsvggraphics

解决方案


  • 我从另一个 SO 答案中选择了 Array 段代码的路径:
    Split a svg path d to array of objects

  • 将其包装在创建新 d-path 的代码中,逐步添加每个段

  • mouseover用点击替换

  • 删除路径段的特定颜色

  • 因为 N 路径被覆盖了一些额外的抗锯齿功能

<svg-path-extractor colors="orange,green,blue,gold,hotpink,red">
  <div id=label></div>
  <svg viewBox="0 0 24 24">
    <path d="M 0 0 C 15 2 3 18 7 21 A 1 1 0 0 0 14 6 L 0 10 C 0 10 20 30 20 20 q -2 -4 4.13 -1 Z"></path>
    <g id=segments></g>
  </svg>
</svg-path-extractor>
<script>
  customElements.define("svg-path-extractor", class extends HTMLElement {
    connectedCallback() {
      let addPath = (d, stroke) => {}
      setTimeout(() => {
        let colors = this.getAttribute("colors").split `,`;
        let segments = [...this.querySelectorAll("svg path")].map(p => {
          return this.pathToArray(p);
        }).flat().map(seg => Object.keys(seg).map(key => seg[key]).join ` `);
        segments.map((seg, idx, arr) => {
          let d = arr.slice(0, idx + 1).join(" ");
          let p = document.createElementNS("http://www.w3.org/2000/svg", "path");
          p.setAttribute("d", d);
          p.setAttribute("fill", "none");
          p.setAttribute("stroke", colors.shift());
          p.onmouseover = (evt) => {
          let label = `${idx}d=${d}`.replace(seg,`<b>${seg}</b>`);
            this.querySelector("#label").innerHTML = label;
          }
          this.querySelector("svg #segments").prepend(p);
        })
      });
    }

    pathToArray(path) {
      if (typeof path != "string") path = path.getAttribute("d");
      const PATH_COMMANDS = {
        M: ["x", "y"],
        m: ["dx", "dy"],
        H: ["x"],
        h: ["dx"],
        V: ["y"],
        v: ["dy"],
        L: ["x", "y"],
        l: ["dx", "dy"],
        Z: [],
        C: ["x1", "y1", "x2", "y2", "x", "y"],
        c: ["dx1", "dy1", "dx2", "dy2", "dx", "dy"],
        S: ["x2", "y2", "x", "y"],
        s: ["dx2", "dy2", "dx", "dy"],
        Q: ["x1", "y1", "x", "y"],
        q: ["dx1", "dy1", "dx", "dy"],
        T: ["x", "y"],
        t: ["dx", "dy"],
        A: ["rx", "ry", "rotation", "large-arc", "sweep", "x", "y"],
        a: ["rx", "ry", "rotation", "large-arc", "sweep", "dx", "dy"]
      };
      const items = path.replace(/[\n\r]/g, '').
      replace(/-/g, ' -').
      replace(/(\d*\.)(\d+)(?=\.)/g, '$1$2 ').
      trim().
      split(/\s*,|\s+/);
      const segments = [];
      let currentCommand = '';
      let currentElement = {};
      while (items.length > 0) {
        let it = items.shift();
        if (PATH_COMMANDS.hasOwnProperty(it))
          currentCommand = it;
        else
          items.unshift(it);
        currentElement = {
          type: currentCommand
        };
        PATH_COMMANDS[currentCommand].forEach((prop) => {
          it = items.shift(); // TODO sanity check
          currentElement[prop] = it;
        });
        if (currentCommand === 'M') {
          currentCommand = 'L';
        } else if (currentCommand === 'm') {
          currentCommand = 'l';
        }
        segments.push(currentElement);
      }
      return segments
    }
  });
</script>
<style>
  body {
    font: 12px Arial
  }
  b{
    font-size:1.2em;
    color:darkgreen;
  }
  svg {
    width: 180px;
    background: pink;
  }
  svg #segments path{
    cursor:pointer;
  }

</style>


推荐阅读