web-dev-qa-db-fra.com

Comment ligner un texte svg dans javascript?

Alors voici ce que j'ai:

<path class="..." onmousemove="show_tooltip(event,'very long text 
    \\\n I would like to linebreak')" onmouseout="hide_tooltip()" d="..."/>

<rect class="tooltip_bg" id="tooltip_bg" ... />
<text class="tooltip" id="tooltip" ...>Tooltip</text>

<script>
<![CDATA[
function show_tooltip(e,text) {
    var tt = document.getElementById('tooltip');
    var bg = document.getElementById('tooltip_bg');

    // set position ...

    tt.textContent=text;

    bg.setAttribute('width',tt.getBBox().width+10);
    bg.setAttribute('height',tt.getBBox().height+6);

    // set visibility ...
}
...

Maintenant, mon très long texte d'info-bulle n'a pas de saut de ligne, même si j'utilise alert (); cela me montre que le texte a réellement deux lignes. (Il contient cependant un "\", comment puis-je le supprimer?)
Je ne parviens pas à faire fonctionner CDATA n’importe où.

95
sollniss

Ce n'est pas quelque chose que SVG 1.1 supporte. SVG 1.2 contient l’élément textArea, avec le retour automatique à la ligne, mais il n’est pas implémenté dans tous les navigateurs. SVG 2 ne prévoit pas d'implémenter textArea , mais il possède texte automatiquement mis en cache .

Cependant, étant donné que vous savez déjà où vos sauts de ligne doivent se produire, vous pouvez diviser votre texte en plusieurs <tspan>s, chacun avec x="0" et dy="1.4em" pour simuler les lignes de texte réelles. Par exemple:

<g transform="translate(123 456)"><!-- replace with your target upper left corner coordinates -->
  <text x="0" y="0">
    <tspan x="0" dy="1.2em">very long text</tspan>
    <tspan x="0" dy="1.2em">I would like to linebreak</tspan>
  </text>
</g>

Bien sûr, comme vous voulez le faire à partir de JavaScript, vous devrez créer et insérer manuellement chaque élément dans le DOM.

130
Sergiu Dumitriu

Je suppose que vous avez déjà réussi à le résoudre, mais si quelqu'un recherche une solution similaire, cela fonctionne pour moi:

 g.append('svg:text')
  .attr('x', 0)
  .attr('y', 30)
  .attr('class', 'id')
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 5)
  .text(function(d) { return d.name; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.sname; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.idcode; })

Il y a 3 lignes séparées par un saut de ligne.

23
Kristīne Glode

Avec la solution tspan, disons que vous ne savez pas à l'avance où placer vos sauts de ligne: vous pouvez utiliser cette fonction Nice, que j'ai trouvée ici: http://bl.ocks.org/mbostock/7555321

Cela fait automatiquement les sauts de ligne pour le texte long svg pour une largeur donnée en pixel.

function wrap(text, width) {
  text.each(function() {
    var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(),
        Word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat(text.attr("dy")),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
    while (Word = words.pop()) {
      line.Push(Word);
      tspan.text(line.join(" "));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [Word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(Word);
      }
    }
  });
}
14
steco

Je pense que cela fait ce que tu veux:

function ShowTooltip(evt, mouseovertext){
    // Make tooltip text        
    var tooltip_text = tt.childNodes.item(1);
    var words = mouseovertext.split("\\\n");
    var max_length = 0;

    for (var i=0; i<3; i++){
        tooltip_text.childNodes.item(i).firstChild.data = i<words.length ?  words[i] : " ";
        length = tooltip_text.childNodes.item(i).getComputedTextLength();
        if (length > max_length) {max_length = length;}
    }

    var x = evt.clientX + 14 + max_length/2;
    var y = evt.clientY + 29;
    tt.setAttributeNS(null,"transform", "translate(" + x + " " + y + ")")

    // Make tooltip background
    bg.setAttributeNS(null,"width", max_length+15);
    bg.setAttributeNS(null,"height", words.length*15+6);
    bg.setAttributeNS(null,"x",evt.clientX+8);
    bg.setAttributeNS(null,"y",evt.clientY+14);

    // Show everything
    tt.setAttributeNS(null,"visibility","visible");
    bg.setAttributeNS(null,"visibility","visible");
}

Il divise le texte sur \\\n et pour chacun met chaque fragment dans une tspan. Ensuite, il calcule la taille de la boîte requise en fonction de la longueur maximale du texte et du nombre de lignes. Vous devrez également modifier l'élément de texte info-bulle pour qu'il contienne trois tspans:

<g id="tooltip" visibility="hidden">
    <text><tspan>x</tspan><tspan x="0" dy="15">x</tspan><tspan x="0" dy="15">x</tspan></text>
</g>

Cela suppose que vous n’ayez jamais plus de trois lignes. Si vous voulez plus de trois lignes, vous pouvez ajouter plus de tspans et augmenter la longueur de la boucle for.

9

J'ai un peu adapté la solution de @steco, en commutant la dépendance de d3 à jquery et à ajouter le height de l'élément de texte en tant que paramètre

function wrap(text, width, height) {
  text.each(function(idx,elem) {
    var text = $(elem);
    text.attr("dy",height);
        var words = text.text().split(/\s+/).reverse(),
        Word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat( text.attr("dy") ),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
    while (Word = words.pop()) {
      line.Push(Word);
      tspan.text(line.join(" "));
      if (elem.getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [Word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(Word);
      }
    }
  });
}
2
loretoparisi