1

I'm trying to create a text based SVG fill pattern that is dynamic (any text could be used). I want the text to repeat horizontally and vertically, without having to define width/height of the text object in the pattern definition. This Answer provided a lot of info regarding patternUnits, but it wasn't enough to answer my specific question. The pattern seems to require either absolute width/height (doesn't work with dynamic content) or relative % values that are based on the SVG canvas.

Is it possible for a pattern definition to be sized dynamically to fit its children's bounding box (supporting arbitrary text), while using userSpaceOnUse for the pattern's children to set pixel values?

The usecase is consuming user-provided text, so predefined widths/sizes do not work; the solution needs to be agnostic to the text content.

svg{width:100%;height:100%;}
body{height: 100vh;}
body, html{padding:0;margin:0;}
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
<pattern id="GPattern" x="0" y="0" width="100%" height="100%" patternUnits="userSpaceOnUse">
          <text id="text" x="0" y="34" width="100%" height="100%"
          style="font-family: Arial;
                 font-size  : 34;
                 font-weight: 800;
                 stroke     : #000000;
                 fill       : #00ff00;
                "
          >This Text Should Be Dynamic</text>
    </pattern>

  </defs>
  <rect width="100%" height="100%" fill="url(#GPattern)"/>
</svg>

Goal Output: goal-output

Right now the solution I am considering would be to use JS to calculate an absolute width based on content string.length and manually edit the pattern's width attribute, but would prefer if SVG can calculate this automagically.

mix3d
  • 4,122
  • 2
  • 25
  • 47

1 Answers1

1

Leveraging the answer in https://stackoverflow.com/a/13873631/1577447, you can use Javascript to adjust the size of your pattern.

You can trigger adjustPatternSize() every time your code changes. I’ve baked in the default values in case you want to run this in a context where JS isn’t available.

svg{width:100%;height:100%;}
body{height: 100vh;}
body, html{padding:0;margin:0;}
input { position: absolute; left: 10%; bottom: 10%; }
     <input id="textInput" type="text" value="This Text Is Dynamic">

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
<pattern id="GPattern" x="-4" y="-1" width="483" height="46" patternUnits="userSpaceOnUse">
          <text id="text" x="0" y="34" width="100%" height="100%"
          style="font-family: Arial;
                 font-size  : 34;
                 font-weight: 800;
                 stroke     : #000000;
                 fill       : #00ff00;
                "
          ></text>
    </pattern>

  </defs>
  <rect width="100%" height="100%" fill="url(#GPattern)"/>
<script type="text/javascript">
    const padding = 4
    const text = document.getElementById("text");
    const rect = document.getElementById("GPattern");
    const input = document.getElementById("textInput");
    
    input.oninput = handleInput;

    function handleInput(e) {
        adjustPatternSize(e.target.value)
    }

    function adjustPatternSize(string) {
      text.textContent = string;
      const bbox = text.getBBox();
      rect.setAttribute("x",bbox.x - padding);
      rect.setAttribute("y",bbox.y - padding );
      rect.setAttribute("width",bbox.width + 2*padding);
      rect.setAttribute("height",bbox.height + 2*padding);
    }

   adjustPatternSize("This Text Is Dynamic");    
  </script>
</svg>
mix3d
  • 4,122
  • 2
  • 25
  • 47
MXDVL
  • 348
  • 2
  • 9
  • Neither of these options work, because changing the text content breaks the formatting. That's the point of "this text should be dynamic", the text could change, either in the text itself or possible the font size later. Definitely a good start to play with though! – mix3d Apr 21 '21 at 08:11
  • 1
    I’ve updated my reply with the JS option being truly dynamic. It’s only a matter of triggering the `adjustPatternSize` with whichever `string` you want your text to be. – MXDVL Apr 21 '21 at 08:29
  • 1
    Interesting that innerHTML worked for the `` element; I had tried `innerText` but settled on `textContent` (probably safer) – mix3d Apr 21 '21 at 08:53
  • I’ve edited my answer to use `textContent` as well. If you’re happy with it you can mark it as accepted? – MXDVL Apr 21 '21 at 09:54
  • 1
    Totally forgot. Meant to do that when I +1'd the answer. – mix3d Apr 21 '21 at 20:49