svgdotjs / svg.js

The lightweight library for manipulating and animating SVG
https://svgjs.dev
Other
11.04k stars 1.08k forks source link

The same code creates different SVGs with and without addTo() #1162

Closed sllegendre closed 1 year ago

sllegendre commented 3 years ago

Bug report

In my angular 10 app, I wrote a method that, using svg.js, will either attach svg to a dom-element or not, depending on whether I pass one to begin with. It always returns draw.svg(). If no dom-element is provided, addTo() is not callend and I can still choose to attach the svg-element to the dom later through angular. In fact, it was my goal: to move dom-manipulation back into angular. Unfortunately, the svgs are not the same.

Code

 makesvg(element?: string): any {

    let draw = element? SVG().addTo(element).size('100%','100%') : SVG().size('100%','100%');

    let foo = draw.rect(this.widthpx, this.heightpx).fill({ color: '#f06', opacity: 0.6 });

    let bar = draw.rect(this.widthpx * 0.9, this.heightpx - this.widthpx * 0.1).fill('#fff').move(.05 * this.widthpx, this.widthpx * .05);

    let baz = draw.text("Some Text").font({
      size: 19,
      family: 'Arial',
    }).center(.5 * this.widthpx, this.heightpx * .1);

    let title = draw.text("More\nfantastic\ntext".toUpperCase()).font({
      size: 50,
      anchor: 'middle',
      leading: '0.9',
      family: 'Arial',
      weight: 'bold'
    }).center(.5 * this.widthpx, this.heightpx * .45)
      .transform({ rotate: -5 });

    let qux = draw.text("...and more text").center(.5 * this.widthpx, this.heightpx * .9);
    draw.viewbox(0, 0, this.widthpx*2, this.heightmm*2);
    return draw.svg();
  }

Explanation

Fuzzyma commented 3 years ago

Text can only be positioned properly if it is attached to the dom because it needs the bounding box of the text to e.g. move it by its center and that is only available if the element is rendered.

The workaround is to not use methods which rely on a rendered element. The most barebone version of that is using attr() to set the attributes yourself. However, in your case, you can simply use text-anchor: middle to center the text and use amove() to position it. That should pretty much do the trick. The difference between move and amove is, that the former will position the text by its upper left edge (which needs a bounding box) while the latter will set x and y value directly as is and therefore position the text by its baseline and anchor

sllegendre commented 3 years ago

Thank you for the quick reply. That makes sense. Is there also a way to make it recognize the \n newline character better? It realizes there is a \n but the text just starts from the beginning in the same line after the \n

Fuzzyma commented 3 years ago

Lew lined are created by moving the line down by its font size and a factor. The only reliable way to read the font size us getComputedStyle which does not work on detached nodes. It just returns an empty string. You can try to add the font size as inline css instead of setting the font size attribute directly but I don't think it will fix it (worth a try though). Beside that there isn't much you can do beside filing an issue report which you already did :/