anvaka / VivaGraphJS

Graph drawing library for JavaScript
Other
3.73k stars 423 forks source link

getBBox size of text SVG return 0 ? #24

Closed xdiscovery closed 11 years ago

xdiscovery commented 11 years ago

Hello everybody! I need to get width of text box of the svg text which is created with svgText = Viva.Graph.svg('text') .attr('y', -nodeSize) .attr('text-anchor','middle') .attr('x',nodeSize/2) .attr('width',nodeSize*2) .text(node.data.name')

I tried with var bbox = svgText.getBBox(); var bboxwidth = bbox.width; var bboxheight = bbox.height;

but I get the value is 0.. console.log('size ' + svgText.getBBox().width);

Do you have the same results or is it my code which has smtg awkward? Thank you 4 sharing!

anvaka commented 11 years ago

Hey there. My first guess would be you are trying to call this function before it's actually rendered to the page. You could do lazy evaluation and cache the bounding box of the text (since it's pretty expensive operation) later in the program execution flow. For example in the placeNode callback, you could do:

if (!nodeUI.bbox) {
  nodeUI.bbox = nodeUI.getBBox();
}

If this doesn't help, please share a jsfiddle example to reproduce the bug.

xdiscovery commented 11 years ago

Hi, thank you, indeed:

So I understood I was tring to get the size box before it was actually rendered.

However, my goal is to split the label of a node in multiple lines and I didn't succeed.

I did the following:

I noticed that to access the node label at placeNode () :

nodeUI.textContent (not nodeUI.svgText)

I tried to: 1) replace " " with "\n", but svtText.text does not render the "\n" character 2) attach "tspan" attribute as svtText.text.tspan(label) but got this error:

Uncaught TypeError: Object function (textContent) { if (typeof textContent !== 'undefined') { svgElement.textContent = textContent; return svgElement; } return svgElement.textContent; } has no method 'tspan'

Maybe a workaround to avoid to use other libraries? (http://stackoverflow.com/questions/13207048/create-svg-text-multi-line-with-one-of-them-is-empty-line)

anvaka commented 11 years ago

I see... I have to play with Raphael to see how to integrate it with vivagraph, but I had already solved the multiline text problem. Here is a function for you:

var namespace = 'xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"';

function parseSvgText(svgText) {
    var parser = new DOMParser();

    try {
        return parser.parseFromString(svgText, "text/xml").documentElement;
    } catch (e) {
        alert('cannot parse svg text: ' + svgText);
        throw e;
    }
}

function convertToSvgText(text) {
    var textContent = text.replace('&', '&').split('\n'),
        svgContent = [],
        increment = 11,
        i;
    svgContent.push('<g ' + namespace + '>');
    svgContent.push('<text x="0" y="0" style="font-size:8pt;">');
    for (i = 0; i < textContent.length; ++i) {
        svgContent.push('<tspan x="6" y="' +  (i + 1) * increment + '">' + textContent[i] + '</tspan>');
    }
    svgContent.push('</text>');
    svgContent.push('</g>');

    return parseSvgText(svgContent.join(''));
}
// To convert multiline text use:
convertToSvgText("Hello\nWorld");

Please let me know if this would work...

xdiscovery commented 11 years ago

Hi! thank you! yes, I tested it and it works pretty fine - there is a slower performance, I guess do to the recalculation of the position of each line? However it works pretty ok. The getBBox() function would be easier if labels should be positioned at the center of a node: I have multiple lines I have multiple heights, so position of the label respect to the node may be slightly tricky.

I share a little edit, if someone want to display the labels on a fixed number of lines:

function convertToSvgText(text,posX,posY) { var numberWord = text.split(" ").length, textContent = [], numberLine, svgContent = [], increment = 20, i; var posX = posX, posY = posY;

        if (text.length>15) {
            numberLine = 2;
            pos = text.indexOf(' ',text.length/(numberLine+1));

            textContent[0]=text.substring(0,pos);
            textContent[1]=text.substring(pos+1);
        } else {
            numberLine =1;
            textContent[0] = text;
        }

        svgContent.push('<g ' + namespace + '>');
        svgContent.push('<text x="'+posX+'" y="'+posY+'" text-anchor="middle">');
        for (i = 0; i < numberLine; ++i) {
           svgContent.push('<tspan x="'+posX+'" y="'+  (i + 1) * increment + '">' + textContent[i] + '</tspan>');
        }
        svgContent.push('</text>');
        svgContent.push('</g>');

        return parseSvgText(svgContent.join(''));
    }
anvaka commented 11 years ago

Thank you for sharing!