msmoore
msmoore

Reputation: 408

Removing gap between two SVG paths without removing anti-aliasing

I have two SVG paths that have a gap between them.

enter image description here

From reading through other questions (in particular this one) I understand this is because of the native anti-aliasing properties of SVGs.

So I added shapeRendering="crispEdges"

This does remove the gap. However it results in jagged edges because of the removal of anti-aliasing.

enter image description here

<svg height="300" width="300" shapeRendering="crispEdges">
        <path
          d="M150 10 a120 120 0 0 1 103.9230 60"
          fill="none"
          stroke="green"
          stroke-width="20"
        />
        <path
          d="M253.9230 70 a120 120 0 0 1 0 120"
          fill="none"
          stroke="green"
          stroke-width="20"
        />
</svg>

I've also tried the suggestion in this question to add crispEdges to the parent svg of the path and add shapeRendering="optimizeQuality" to the path but that didn't work.

How can I remove the gap AND keep the smooth edges of my svg path?

Upvotes: 0

Views: 515

Answers (3)

herrstrietzel
herrstrietzel

Reputation: 17195

You could also mitigate this gap rendering effect by applying a subtle svg feMorphology dilate filter – resulting in slightly expanded strokes closing thin gaps between paths:

SVG feMorphology filter

svg {
  overflow: visible;
}

.chart path {
  filter: url(#outline);
}

path:hover {
  stroke: red;
}
<svg height="300" width="300" shape-rendering="geometricPrecision">
  <g class="original">
    <path d="M150 10 a120 120 0 0 1 103.9230 60" fill="none" stroke="green" stroke-width="20" />
    <path d="M253.9230 70 a120 120 0 0 1 0 120" fill="none" stroke="green" stroke-width="20" />
  </g>
  <text x="50%" y="50%">Original</text>
</svg>


<svg height="300" width="300">
  <filter filterUnits="userSpaceOnUse" id="outline" >
    <feMorphology in="SourceGraphic" result="DILATED" operator="dilate" radius="0.5" />
  </filter>
  <g class="chart">
    <path d="M150 10 a120 120 0 0 1 103.9230 60" fill="none" stroke="green" stroke-width="20" />
    <path d="M253.9230 70 a120 120 0 0 1 0 120" fill="none" stroke="green" stroke-width="20" />
  </g>
    <text x="50%" y="50%">Dilate filter</text>
</svg>

But this approach will also introduce slightly rounded edges (you can see this effect on hover).
More importantly, svg filters are quite expensive with regards to rendering performance – rather negligible if you only display a few elements per page view.

Add concatenated background path

As suggested by @Robert Longson: you could also prepend a background path based on concatenated d path data.

This task could be achieved with a javaScript helper method cloning the first path and displaying the concatenated paths.

addBgPaths(".addBGPath");

function addBgPaths(selector) {
  let addPathSvgs = document.querySelectorAll(selector);
  addPathSvgs.forEach(function(svg) {
    let paths = document.querySelectorAll(".addBGPath path");
    let firstPath = paths[0];
    let firstPathCloned = firstPath.cloneNode();
    //cloned elements shouldn't have ids to avoid non unique ids
    firstPathCloned.removeAttribute("id");
    let dArr = [firstPath.getAttribute("d")];
    for (let i = 1; i < paths.length; i++) {
      let path = paths[i];
      let d = path.getAttribute("d");
      dArr.push(d);
    }
    firstPathCloned.setAttribute("d", dArr.join(" "));
    svg.insertBefore(firstPathCloned, svg.children[0]);
  });
}
<svg height="300" width="300" shape-rendering="geometricPrecision">
    <path d="M150 10 a120 120 0 0 1 103.9230 60" fill="none" stroke="green" stroke-width="20" />
    <path d="M253.9230 70 a120 120 0 0 1 0 120" fill="none" stroke="green" stroke-width="20" />
  <text x="0" y="50%">Original</text>
</svg>
<svg class="addBGPath" height="300" width="300" shape-rendering="geometricPrecision">
    <path id="first2" d="M150 10 a120 120 0 0 1 103.9230 60" fill="none" stroke="green" stroke-width="20" />
    <path d="M253.9230 70 a120 120 0 0 1 0 120" fill="none" stroke="green" stroke-width="20" />
  <text x="0" y="50%">Add bg path</text>
</svg>

<svg class="addBGPath" height="300" width="300" shape-rendering="geometricPrecision">
    <path id="first" d="M150 10 a120 120 0 0 1 103.9230 60" fill="none" stroke="green" stroke-width="20" />
    <path d="M253.9230 70 a120 120 0 0 1 0 120" fill="none" stroke="red" stroke-width="20" />
  <text x="0" y="50%">Add bg path (red)</text>
</svg>
<svg height="300" width="300" shape-rendering="geometricPrecision">
    <path d="M150 10 a120 120 0 0 1 103.9230 60" fill="none" stroke="green" stroke-width="20" />
    <path d="M253.9230 70 a120 120 0 0 1 0 120" fill="none" stroke="red" stroke-width="20" />
  <text x="0" y="50%">Original (red)</text>
</svg>

If all your path segments have the same color, this is probably the most elegant solution.

But this approach will also introduce colored "halos" when segments use different stroke colors (example #3 compared to #4).

Upvotes: 2

ksav
ksav

Reputation: 20821

As a quick fix, you can make the ends overlap with stroke-linecap="square"

But ideally, you need to create a single path instead of two separate paths.

<svg height="300" width="300" shapeRendering="crispEdges">
        <path
          d="M150 10 a120 120 0 0 1 103.9230 60"
          fill="none"
          stroke="green"
          stroke-width="20"
          stroke-linecap="square"
        />
        <path
          d="M253.9230 70 a120 120 0 0 1 0 120"
          fill="none"
          stroke="green"
          stroke-width="20"
          stroke-linecap="square"
        />
</svg>

Upvotes: 0

user212324
user212324

Reputation: 34

If you able to edit the svg in editor, you can overlap like this. The darker green is the intersection between two paths.

Overlapping

Upvotes: 0

Related Questions