2

I want to create an SVG that scales to the width of the container without the parts of it resizing or distorting. So depending on the width, the same image might be rendered as any of the following:

enter image description here

As you can see, the left and right parts are (or at least appear to be) part of the same path, and they're the same size and aspect ratio in all cases.

It seems like the capabilities of SVG should allow this, but I don't know how. Obviously no graphics program will do this, so the SVG will need to be hand-crafted.

Possible solutions:

  • Multiple objects: constructing the shape out of overlapping parts is an option, but risks messy overlap artefacts.

  • Image swap: I don't want to construct a fixed number of these sizes, because I can't anticipate every possible screen, window or device size. Instead I want an image that scales to whatever the size is.

  • JavaScript: I'd really prefer to avoid this, as it requires every page or context the SVG is placed in to run a script to fix it.

Marcus Downing
  • 10,054
  • 10
  • 63
  • 85

1 Answers1

2

If you have only parts to position to the sides, you can take advantage of the preserveAspectRatio attribute of nested <svg> elements.

<svg width="100%" height="50px">
  <rect fill="grey" width="100%" height="100%" />
  <svg width="100%" height="100%" viewBox="0 0 50 50" preserveAspectRatio="xMinYMid meet">
    <circle fill="white" cx="25" cy="25" r="18" />
  </svg>
  <svg width="100%" height="100%" viewBox="0 0 50 50" preserveAspectRatio="xMaxYMid meet">
    <circle fill="white" cx="25" cy="25" r="18" />
  </svg>
</svg>

The same could work for four corners, if you used a further nesting level and a positioning trick:

body { height: 90vh; }
<svg width="100%" height="100%">
  <rect fill="grey" width="100%" height="100%" />
  <svg width="100%" height="50">
    <svg width="100%" height="100%" viewBox="0 0 50 50" preserveAspectRatio="xMinYMid meet">
      <circle fill="white" cx="25" cy="25" r="18" />
    </svg>
    <svg width="100%" height="100%" viewBox="0 0 50 50" preserveAspectRatio="xMaxYMid meet">
      <circle fill="white" cx="25" cy="25" r="18" />
    </svg>
  </svg>
  <svg width="100%" height="50" y="100%" transform="translate(0 -50)">
    <svg width="100%" height="100%" viewBox="0 0 50 50" preserveAspectRatio="xMinYMid meet">
      <circle fill="white" cx="25" cy="25" r="18" />
    </svg>
    <svg width="100%" height="100%" viewBox="0 0 50 50" preserveAspectRatio="xMaxYMid meet">
      <circle fill="white" cx="25" cy="25" r="18" />
    </svg>
  </svg>
</svg>
ccprog
  • 20,308
  • 4
  • 27
  • 44
  • That's certainly part of it. I still need to work out how to make a solid object with parts that scale out - or fake it by combining objects. – Marcus Downing Jul 23 '18 at 08:56
  • Maybe I can achieve a result like that with clipping masks? Have two very-wide boxes, and clip them so they overlap in the middle. – Marcus Downing Jul 23 '18 at 08:58
  • If an object has scaling and non-scaling parts, that certainly justifies to define them separately as partial objects - nothing fake about it in my opinion. Your second idea sounds more like three boxes, if I look at your example: two non-scaling to the sides, on scaling in the middle. – ccprog Jul 23 '18 at 13:44
  • The embedded SVGs serve to clip the contents without need for an explicit clipping mask. It'll take some experimentation, but I think this can work. – Marcus Downing Jul 24 '18 at 09:41