Search code examples
dictionarysvggraphicstransformation

Keeping SVG elements to a fixed size while position scales


How can I keep text or objects to a fixed size while the background and position coordinates change? For example:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:ev="http://www.w3.org/2001/xml-events" 
    width="100%" height="100%"
    viewBox="0 0 50000 50000"
    >
    <circle  cx="25000"  cy="25000" r="100px" fill="red" />
</svg>

In this code, the circle will NOT be 100 pixels, it will be scaled according to the viewbox size, so it will be tiny.

This problem manifests in maps, for example. When you zoom in and out on maps you want the dot representing a city location and the label to stay the same size, not get bigger or smaller when the user zooms.


Solution

  • You can do something very close: you can use a nested svg element with height and width in percentage of the diagonal of the svg viewBox. If you know the size of the svg on screen you can approximate 100px with a percentage.

    You also need to use the viewBox as a mean to pan and zoom in the map. This works for any element text included; using % unit for font-size doesn't refer to the svg viewBox, so the only way to get contstant font-size, you need to encapsulate inside another svg element with size in % units.

    Caveat: the svg, while fixed in size, will appear to scale relative to the top left corner of the inner svg.

    The same approach to a similar problem in another answer.

    for(let i = 0; i < 12; i++) {
      let a = (i - 4)*5
      setTimeout(function() {
        document.querySelector('#svg').setAttribute('viewBox',
          `${a} ${a} ${100 - 2*a} ${100 - 2*a}`);
      }, 500 * i);
     }
      
    <svg id="svg" width="100px" height="100px" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
        <svg x='35' y='35' width='10%' height='10%' viewBox='0 0 100 100'>
           <circle  cx='50'  cy='50' r='50' fill='red'/>
        </svg>
         <circle  cx='65'  cy='65' r='5%' fill='red'/>
         <circle  cx='35'  cy='65' r='10' fill='red'/>
    </svg>

    `