SBB
SBB

Reputation: 8990

Javascript Dynamic SVG with Marker

I have some javascript that is generating SVG paths ontop of some data to show job progression. I have the lines working that show the connection between one box to another, but I am unable to add a marker arrow.

I am referencing my arrow by using the following structure:

<svg id="svg1" width="0" height="0">
  <defs>
      <marker id="Triangle" viewBox="0 0 10 10" refX="1" refY="5" 
              markerUnits="strokeWidth" markerWidth="4" markerHeight="3"
              orient="auto">
        <path d="M 0 0 L 10 5 L 0 10 z" fill="context-stroke"/>
      </marker>
    </defs>
    <path id="path1" class="path" marker-end="url(#Triangle)" fill="none" stroke-width="10"/>
</svg>

The issue is that it is not generating the triangle correctly and almost looks like its a cut-off square? The triangle should be on the bottom right as the path is connecting the top box to the bottom box.

enter image description here

JS Fiddle:

http://jsfiddle.net/qLhunt7k/

Any idea as to why it's not looking like the triangle I am expecting?

Upvotes: 1

Views: 2022

Answers (1)

Paul LeBeau
Paul LeBeau

Reputation: 101976

Your problem is due to a number of factors:

  1. The marker is off the bottom of your SVG because your line ends at the bottom of your SVG. The marker's offset (refX and refY) is at the base of the arrowhead. So the marker is drawn so that its base (the widest part) is placed where the line ends. So you can only see the top pixel of it.

  2. You can prevent the clipping, and make the marker visible, by setting overflow: visible on the SVG. But you also have to do the same to the #svgContainer container div.

    #svgContainer {
      ...
      overflow: visible;
    }
    
    #svgContainer svg {
      overflow: visible;
    }
    

    An alternative would be to not draw your line all the way to the bottom. So that you leave space at the bottom for the marker to be drawn. Eg. change the path generator to something like:

    " V" + (endY-30));
    

    I've used 30 here because it matches a stroke-width of 0.7em. I would recommend changing your CSS to use a px value for the stroke-width. Otherwise, a change to the font-size in the future may end up breaking the markers again.

    If you choose this approach, you'll also need allow horizontal room for the marker when you calculate the SVG width. Eg:

    if (svg.getAttribute("width") < (endX + stroke * 3))
       svg.setAttribute("width", (endX + stroke * 3));
    
  3. Next, the path {} rule is causing issues. That general definition for path is over-riding the fill="context-stroke" attribute in your marker definition. This is causing the marker to have a large stroke, which again is overflowing the marker bounds and making it look like a rectangle. The fix is to get rid of the path {} style.

    path {
    }
    
  4. The last remaining issue is the fill="context-stroke". This is a new SVG2 thing which is not well supported yet. For instance, it works on Chrome, but doesn't seem to work on Firefox yet. So for now, I recommend just using a plain fill in the marker definition.

    <marker id="Triangle" viewBox="0 0 10 10" refX="0" refY="5" 
            markerUnits="strokeWidth" markerWidth="4" markerHeight="3"
            orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#000"/>
    </marker>
    

Fiddle: http://jsfiddle.net/ptLc1gyx/

Upvotes: 5

Related Questions