Home > database >  SVG marker direction arrows appear in DOM, but the marker itself isn't visible
SVG marker direction arrows appear in DOM, but the marker itself isn't visible

Time:12-01

I'm trying to create directional arrows on an svg path that's being rendered using d3. I obtain the start points using the path string and the svg-path-properties npm package. I then push these points to an array, and in another function, I map these points onto the original svg path with the d3 logic to create triangles at each point.

  public mapArrowPoints(): void {
    this.arrowPoints.forEach((point: ArrowPointObject) => {
      const path = d3.path();
      path.moveTo(parseInt(point.x, 10), parseInt(point.y, 10));
      path.lineTo(parseInt(point.x, 10)   8, parseInt(point.y, 10)   2);
      path.lineTo(parseInt(point.x, 10)   0, parseInt(point.y, 10)   8);
      path.closePath();

      const svg = d3.select('svg#topLevel');
      const defs = svg.insert('defs', '#curve');
      const marker = defs.append('marker');

      marker
        .attr('id', 'triangle')
        .attr('viewBox', '0 -5 10 10')
        .attr('markerWidth', 4)
        .attr('markerHeight', 4)
        .attr('orient', 'auto')
        .append('path')
        .attr('d', path.toString())
        .attr('fill', 'red')
        .attr('stroke', 'black')
        .attr('stroke-width', '3px');

      svg
        .select('#curve')
        .attr('marker-start', 'url(#triangle)')
        .attr('marker-mid', 'url(#triangle)')
        .attr('marker-end', 'url(#triangle)');
    });
  }

Now, when I check the browser, I'm able to see my main path, but no triangles. I can inspect the DOM, and see the svg, the defs, marker, and the path they're supposed to be appended onto. I can also select the marker path itself, and see where it should be rendering (a blue box shows where the triangle should be). I can't quite figure out why the triangles aren't showing. Any help will be much appreciated. I've created a stackblitz example for experimenting with solutions.

Note:
I can make the triangles appear on the path without the additional defs and marker logic as shown here:

svg
 .append("path")
 .attr("d", path.toString())
 .attr("stroke", "black")
 .attr("stroke-width", "3px");

This shows where the triangles should be. End of the day, the mission here is to cause these triangles to utilize the orient: "auto" functionality that's available to the marker attribute. If anybody knows a way to orient these triangles without using the markers, I'm open to that possibility. After all, I'm able to render these triangles without the marker tag, so maybe I don't need it? I've included a commented out section in the stackblitz for experimenting with this method.

CodePudding user response:

Besides the fact that you have lots of marker elements with the same id, your marker has a viewBox that makes the triangle to fall outside the marker box. A proper viewBox for the path you have with a 3units stroke would be bigger than it's bounding box so I would use 'viewBox="18 5 14 14"'. You may want to choose a different viewBox.

Here you can see the path you want to use as a marker:

svg{border: solid}
<svg viewBox="18 5 14 14" width="300" >
      <path  d="M20,7L28,9L20,15Z" fill="red" stroke="black" stroke-width="3"></path>
    </svg>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Once you have this wiewBox for the marker you will see the marker but it falls outside the path. This is because the marker ha a refX and a refY attributes refferencing the point where the marker attaches itself to the path. By default this point is {0,0}. I'l be using refX="18.5" refY="9". I'm choosing 9 becose the tip of the triangle has y=9 (d="M20,7L28,9L20,15Z")

In the next example I'm marking this point with a golden circle. You may want to choose a different one.

svg{border: solid}
<svg viewBox="18 5 14 14" width="300" >
      <path  d="M20,7L28,9L20,15Z" fill="red" stroke="black" stroke-width="3"></path>
      
      <circle cx="18.5" cy="9" r=".5" fill="gold"/>
    </svg>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

And this is how the path with the marker would look like:

<svg viewBox="0 0 700 800" >
  <defs>
    <marker id="triangle" viewBox="18 5 14 14" markerWidth="8" markerHeight="8" orient="auto" refX="18.5" refY="9">
      <path  d="M20,7L28,9L20,15Z" fill="red" stroke="black" stroke-width="3px"></path>
    </marker>
  </defs>
 

  <path fill="none"  stroke="black"  id="curve" d="M 0.05965662,9.8756182 C 665.77394,-70.124378 654.96347,387.98451 654.96347,387.98451 L 45.773941,621.3042 650.91307,783.4508" marker-start="url(#triangle)" marker-mid="url(#triangle)" marker-end="url(#triangle)"></path>
</svg>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related