1

Is there a way in SVG to re-use part of a <path> while keeping the other parts of the <path> intact?

I'm trying to build a static SVG file (no HTML or JS allowed, although can leverage external CSS) that has 20+ instances of a variable-width horizontal curly brace that looks like this: enter image description here

All the instances are the same height but each one has a different width, which means that the arcs (a) are identical but the initial position and horizontal lines (h) will be different for each one. This also means that I can't use <use> and then scale each instance horizontally, because scaling would result in stretched-out arcs which look bad. (BTW, while researching this I learned about vector-effect="non-scaling-stroke" which, if combined with <use> would be a perfect solution if there were no curves involved.)

I know I can use <use> and override the d attribute in each one, but is there a better way than cloning the same d attribute 20+ times and editing the h values inside each one? An ideal solution would be to be able to re-use sub-paths, e.g. d="${left} h 100 ${middle} h 100 ${left}" which I could trivially do if JS were allowed, but alas I can't use script in this context.

Is there a way to do something like below (in SVG; I know I could do it in HTML) where paths each pick up where the last one ended?

<use href="#left">
<path d="h 200">
<use href="#mid">
<path d="h 200">
<use href="#right">

If not, is there another way to do what I'm looking for?

Here's the SVG I have now: (https://codepen.io/justingrant/pen/rNLqQRo)

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3000 500" width="3000" height="500" fill="none" stroke-width="5" stroke="green" >
  <path d="m 80,80 
           a 30 30 0 0 1 30,-30
           h 100
           a 30 30 0 0 0 30,-30
           a 30 30 0 0 0 30,30
           h 100 
           a 30 30 0 0 1 30,30"/>
  <path d="m 500,80 
           a 30 30 0 0 1 30,-30 
           h 40
           a 30 30 0 0 0 30,-30
           a 30 30 0 0 0 30,30
           h 40 
           a 30 30 0 0 1 30,30"/>
</svg>

I know I could run the raw code through a build step or pre-processor, but I'd prefer a native SVG solution before hacking together something like that.

Justin Grant
  • 44,807
  • 15
  • 124
  • 208
  • Create a template then run an XSLT process (or similar) to generate all the static files you want at compile time rather than runtime. – Robert Longson Nov 09 '20 at 21:31

2 Answers2

1

What you want is possible with a bit of trickery. However you need to be okay with it always being drawn against a background of a known colour.

See the below example. Try running it, switch to "Full page", then change the width of the browser window.

path {
  stroke: green;
  stroke-width: 4;
  fill: none;
}

.bg-color {
  fill: white;
}
<svg width="100%" height=="50">
  <defs>
    <g id="left">
      <rect x="0" y="0" width="27" height="50" class="bg-color"/>
      <path d="M 2 50 a 25 25 0 0 1 25 -25"/>
    </g>
    <g id="centre">
      <rect x="-25" y="0" width="50" height="50" class="bg-color"/>
      <path d="M -25 25 a 25 25 0 0 0 25 -25 a 25 25 0 0 0 25 25"/>
    </g>
    <g id="right">
      <rect x="-27" y="0" width="27" height="50" class="bg-color"/>
      <path d="M -27 25 a 25 25 0 0 1 25 25"/>
    </g>
  </defs>
  <path d="M 0 25 L 9999 25"/>
  <use xlink:href="#left" x="0" y="0"/>
  <use xlink:href="#centre" x="50%" y="0"/>
  <use xlink:href="#right" x="100%" y="0"/>
</svg>

How it works is that we draw a green line the full width of the SVG. Then we draw over the top of that the two end shapes and the centre shape. Those three parts have to hide the section of green line behind each of them. So they each have to include a rectangle that matches the background colour.

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
0

This solution only works in Chrome (not Firefox), and requires some more extensive external SCSS, but I still wanted to share it because it's the closest I've come to a structural solution at runtime.

Use SCSS @for to generate CSS classes for all multiples of 10, where that number denotes the length of the straight part in pixels. Then, set the class of the path and use the fact that you can set path() in CSS in some bleeding edge browsers.

@for $i from 1 through 20 {
  path.path-#{$i * 10} {
    d: path('m 20,80 a 30 30 0 0 1 30,-30 h #{$i * 10} a 30 30 0 0 0 30,-30 a 30 30 0 0 0 30,30 h #{$i * 10} a 30 30 0 0 1 30,30');
  }
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3000 500" width="3000" height="500" fill="none" stroke-width="5" stroke="green" >
  <path class="path-100"/>
  <path class="path-40"/>
</svg>

You can see it in action here.

Ruben Helsloot
  • 12,582
  • 6
  • 26
  • 49