Search code examples
svgsvg.js

How can I draw text using SVG.js?


I am trying to figure out how to use SVG.js to draw text. In my attempt below, the text is being created and drawn, but only a few dots show up. This must be part of the text being drawn, but it is being clipped. I am not sure why this clipping is happening or how to fix it.

My end goal is to create an SVG with text fitted inside some geometry. To do this, I need to measure the text first so I can compute the geometry dimensions. Therefore, it is important to me to create the SVG object before adding the SVG to a DOM element.

For the code below, what do I need to do so the text show up correctly?

const draw = SVG().size(300, 300);

const text = draw.text("000000000");

text.attr({
  fill: "red",
  stroke: "green",
  "font-family": "Roboto",
  "font-size": 10
});

const bbox = draw.bbox()

console.log( bbox );

draw.addTo("#rectID");
<script src="https://cdn.jsdelivr.net/npm/@svgdotjs/[email protected]/dist/svg.min.js"></script>

<div id="rectID">
</div>


Solution

  • As explained by Fuzzyma:
    the bbox is calculated by creating a temporarily appended DOM element.

    In fact this approach should work flawlessly without any layout shifts.

    Your main problem is the off canvas position of the <text> element.
    Here's an example generating a horizontally and vertically centered <text> element with a <rect> in the background.
    The SVG's markup is returned via svg() method.

    const draw = SVG().size(300, 300);
    // draw background rect
    const rect = draw.rect();
    const text = draw.text("000000000");
    
    text.attr({
      fill: "red",
      "font-family": "Roboto",
      "font-size": 50,
      x: "50%",
      y: "50%",
      "text-anchor": "middle",
      "dominant-baseline" : 'middle'
    });
    
    // get bounding box
    const bbox = draw.bbox();
    
    // adjust rect dimensions according to bbox
    rect.attr({
      x: bbox.x,
      y: bbox.y,
      width: bbox.width,
      height: bbox.height,
      fill: "#ccc",
      stroke: "green"
    });
    
    
    // get markup
    let markup = draw.svg()
    output.value = markup;
    
    function render(){
      document.body.insertAdjacentHTML('beforeend', markup)
    }
    svg{
      border:1px solid #ccc;
    }
    
    textarea{
      width:100%;
      min-height:10em
    }
    <script src="https://cdn.jsdelivr.net/npm/@svgdotjs/[email protected]/dist/svg.min.js"></script>
    
    <h3>SVG markup</h3>
    <textarea id="output"></textarea>
    
    <p><button onclick="render()">Render SVG</button></p>

    As you can see we can also retrieve the SVG markup without rendering the element - albeit the bbox calculation requires some temporary invisible rendering.