11

I have an SVG inlined to html code. Its is scaled to fit width with preserving aspect ratio. Is there a way to set font size fixed to viewport that can be controlled with media queries?

EDIT: The problem is when i set fixed font-size within width range the font is resized anyway relative to svg size.

#svg-container {
  width: 100%;
  padding-bottom: 70%;
  overflow: hidden;
  border: 1px solid red;
}

#svg-container svg text tspan {
  font-size: 14px;
}


@media (min-height: 800px) {
  #svg-container svg text tspan {
    font-size: 50px;
  }
}
<div id="svg-container">
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   version="1.1"
   viewBox="0 0 500 350"
   data-name="Layer 1"
   id="Layer_1"
   inkscape:version="0.91 r13725"
   sodipodi:docname="Desktop_Enhanced.svg"
   width="100%"
   style="position: absolute; left: 0; top: 0;"   
  >
  <sodipodi:namedview
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1"
     objecttolerance="10"
     gridtolerance="10"
     guidetolerance="10"
     inkscape:pageopacity="0"
     inkscape:pageshadow="2"
     inkscape:window-width="1280"
     inkscape:window-height="961"
     id="namedview51"
     showgrid="false"
     inkscape:zoom="1.0054235"
     inkscape:cx="231.42799"
     inkscape:cy="361.93137"
     inkscape:window-x="1272"
     inkscape:window-y="-8"
     inkscape:window-maximized="1"
     inkscape:current-layer="Layer_1"
     fit-margin-bottom="150" />
  <metadata
     id="metadata75">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title>Desktop</dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <defs
     id="defs3">
    <style
       id="style5">.cls-1{fill:#da1f26;}.cls-2{fill:#252e43;}.cls-3{fill:#d1d3d4;}</style>
  </defs>
  <title
     id="title7">Desktop</title>
  <polyline
     id="polyline9"
     points="329.4 62.24 343.54 76.39 329.4 90.53"
     class="cls-1"
     style="fill:#da1f26"
     transform="translate(59.579252,-43.513817)" />
  <polyline
     id="polyline11"
     points="333.56 328.83 347.7 342.97 333.56 357.11"
     class="cls-2"
     style="fill:#252e43"
     transform="translate(59.579252,-43.513817)" />
  <path
     id="path35"
     d="m 394.84925,294.48618 -25,0 c -57.81,0 -77.28,-34.66 -96.1,-68.18 -15.35,-27.34 -29.86,-53.16 -63.9,-53.16 l -31.27,0 0,10 31.3,0 c 28.19,0 40.7,22.27 55.18,48.06 9.68,17.23 19.69,35.06 35.5,49.13 18.26,16.22 40.93,24.15 69.33,24.15 l 25,0 z"
     class="cls-2"
     inkscape:connector-curvature="0"
     style="fill:#252e43" />
  <path
     id="path43"
     d="m 390.97925,27.876183 -21.09,0 c -28.4,0 -51.07,7.9 -69.32,24.15 -15.81,14.08 -25.82,31.9 -35.5,49.129997 -14.48,25.79 -27,48.06 -55.18,48.06 l -31.31,0 0,10 31.3,0 c 34,0 48.54,-25.82 63.9,-53.16 18.8,-33.569997 38.3,-68.179997 96.11,-68.179997 l 21.09,0 z"
     class="cls-1"
     inkscape:connector-curvature="0"
     style="fill:#da1f26" />
  <path
     id="path45"
     d="m 429.38925,17.956183 a 14.92,14.92 0 1 0 14.92,14.92 14.92,14.92 0 0 0 -14.92,-14.92 z m 0,21 a 6.08,6.08 0 1 1 6.08,-6.08 6.08,6.08 0 0 1 -6.08,6.08 z"
     class="cls-1"
     inkscape:connector-curvature="0"
     style="fill:#da1f26" />
  <path
     id="path47"
     d="m 433.38925,284.53618 a 14.92,14.92 0 1 0 14.92,14.95 14.92,14.92 0 0 0 -14.92,-14.95 z m 0,21 a 6.08,6.08 0 1 1 6.09,-6.05 6.08,6.08 0 0 1 -6.09,6.05 z"
     class="cls-2"
     inkscape:connector-curvature="0"
     style="fill:#252e43" />
  <flowRoot
     style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
     id="flowRoot4236"
     xml:space="preserve"
     transform="translate(0,-72.53)"><flowRegion
       id="flowRegion4238"><rect
         y="179.54243"
         x="18.988897"
         height="58.373276"
         width="94.241196"
         id="rect4240" /></flowRegion><flowPara
       id="flowPara4242">ZakładaZ</flowPara></flowRoot>  <text
     x="27.791037"
     y="169.90349"
     font-size="20px"
     id="text49-1"
     style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:15px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252e43;fill-opacity:1"
     sodipodi:linespacing="125%">
    <tspan
       sodipodi:role="line"
       id="tspan4528"
       x="27.791037"
       y="169.90349">PLEASE CHOOSE</tspan>
  </text>
</svg>


</div>
Michael Schmid
  • 4,601
  • 1
  • 22
  • 22
IT Man
  • 933
  • 3
  • 12
  • 33

5 Answers5

10

The short answer is no.

If the text is in an SVG with a viewBox, and the SVG gets scaled, its contents get scaled also. There is no way to make the text have a "global" size that is unaffected by the SVG scaling.

The only possible solution would be calculate the scaling factor using Javascript and dynamically update the font size every time the SVG size changed.

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • This answer is not 100% correct. You can calcute the current zoom factor of the svg, see this SO answer for an example: https://stackoverflow.com/questions/52576376/how-to-zoom-in-on-a-complex-svg-structure You can now set the font-size attribute of text relative if you know your zoom factor. I do it in Angular like this: [attr.font-size]="15 / scale" – Sebastian S. Oct 09 '20 at 19:28
9

I'd recommend nested SVG tags. You can keep your outermost SVG without viewBox and add all text under this tag, while adding a second SVG to cover the full area but using a viewBox.

You'll need to change positions for the text from absolute to relative values, but it works quite well.

That way your text will change position with the svg size but not scale the text. The inner SVG will, though, since you set a viewBox.

Example: JS for variable container, SVG always fills the div. The scaling is pure SVG.

function changeCircleDiv(element) {
  s = document.getElementById("container").style;
  s.width = element.value + 'px';
  s.height = s.width;
}
body {
  font-size: 2em;
}
<input type="number" onchange="changeCircleDiv(this);" value="100"></input>
<div id="container" style="width: 100px; height: 100px;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%">
        <svg width="100%" height="100%" viewBox="0 0 20 20">
            <circle cx="10" cy="10" r="8" stroke="black" fill="none"/>
        </svg>
        <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle">Center</svg>
    </svg>
</div>
Randelung
  • 356
  • 4
  • 10
5

In the end...

I have removed text from svg and added div text block with absolute positioning inside shared html container. That worked prefect. Div's appearance is controlled over css with media queries - it is independent from svg scaling.

Your can see diagram in action at: https://www.xtech.pl/jak-to-dziala-dla-dostawcy (scroll down to second section on the page, then you can resize your screen to see how it works).

IT Man
  • 933
  • 3
  • 12
  • 33
1

You can do it in this way "Responsive SVG with sticky text" See here: https://bl.ocks.org/veltman/5cd1ba0b3c623e7b5146

Or... if you want to use media queries for font in SVG, the answer is yes. Run this code snippet.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100%" height="200px">
  <style>
    text {
      font-size: 10px;
    }

    @media (max-width: 600px) {
      text {
        font-size: 20px;
      }
    }

    @media (max-width: 800px) {
      text {
        font-size: 16px;
      }
    }

  </style>
  <circle cx="50" cy="50" r="50" fill="orange"/>
  <text x="50" y="60" text-anchor="middle">Testing</text>
</svg>
E_net4
  • 27,810
  • 13
  • 101
  • 139
zurcacielos
  • 1,495
  • 2
  • 9
  • 7
0

I actually found a relatively simple workaround.

using D3's scaleSqrt() you can do some math based on the pixel width that the svg is taking up.

For example if the svg viewbox width is 800, then when the svg has a width of 800px, the scale would be 1. When the pixel width of the svg is 400px, relative to the view box, that would be a scale of 1/2

scaleRatio = d3.scaleSqrt()
    .domain([1600, 800, 600, 400, 200, 100])
    .range([0.5, 1, 1.5, 2, 4, 8]);
const svgWidth = d3.select(svg).node().getBoundingClientRect().width;
someD3Element.attr('transform', `scale(${scaleRatio(svgWidth)})`);
Jordan Klaers
  • 149
  • 11
  • A bit complicated and requires additional javascript reference. – IT Man Oct 11 '19 at 08:30
  • Oh my apologies, I suppose "simple" is subjective. What do you mean by additional JS references? – Jordan Klaers Oct 11 '19 at 14:58
  • I assume that you have to reference d3? Isn't it? – IT Man Oct 12 '19 at 09:30
  • It may be possible to use this map function in place of d3.scalesqrt but I haven't tested. function map( x, in_min, in_max, out_min, out_max){ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } – Jordan Klaers Oct 13 '19 at 16:57