Come interrompere la riga di un testo in formato svg all'interno di javascript?


107

Quindi ecco cosa ho:

<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 ...
}
...

Ora il mio testo del suggerimento molto lungo non ha un'interruzione di riga, anche se uso alert (); mi mostra che il testo in realtà ha due righe. (Tuttavia contiene una "\", come faccio a rimuoverla a proposito?)
Non riesco a far funzionare CDATA da nessuna parte.


2
C'è qualche possibilità che questo svgjs.com/textflow possa aiutarti con il tuo suggerimento?
Alvin K.

@AlvinK. il collegamento è interrotto. Ho provato a trovare la nuova posizione, ma non è riuscito.
guettli

Risposte:


150

Questo non è qualcosa che SVG 1.1 supporta. SVG 1.2 ha l' textAreaelemento, con avvolgimento automatico delle parole, ma non è implementato in tutti i browser. SVG 2 non prevede di implementarlotextArea , ma ha un testo a capo automatico .

Tuttavia, dato che sai già dove dovrebbero verificarsi le interruzioni di riga, puoi suddividere il testo in più messaggi <tspan>, ciascuno con x="0"e dy="1.4em"per simulare righe di testo effettive. Per esempio:

<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>

Ovviamente, poiché vuoi farlo da JavaScript, dovrai creare e inserire manualmente ogni elemento nel DOM.


2
E come faccio a riconoscere dove mettere il <tspan>s? Sostituire? Diviso?
sollniss

2
Provato var tspan = document.createElement('tspan') tspan.setAttribute('x','0'); tspan.setAttribute('dy','1.2em'); tspan.textContent = text; tt.appendChild(tspan); non mostra alcun testo.
sollniss

2
Ti andrebbe di approfondire il motivo per cui è necessario x = '0' dy = '1.2em' ? Funziona, infatti, proprio come hai detto. Tuttavia, mi aspettavo che funzionasse anche senza quegli attributi. Invece, non viene visualizzato nulla ... Inoltre, non sono del tutto chiaro sul motivo per cui si verifica l'interruzione di riga . Non è che abbiamo impostato la larghezza del contenitore su qualcosa di fisso, in modo che possa imporre l'interruzione di riga, vero?
Konrad Viltersten

4
x=0è una coordinata assoluta: sposta il frammento di testo all'origine del sistema di coordinate corrente . L' transformattributo gsull'elemento definisce un nuovo sistema di coordinate corrente e, supponendo che il testo sia allineato a sinistra, il tspan viene spostato a sinistra. Funziona come un'istruzione di ritorno a capo. dy=1.2emè una coordinata relativa : sposta il frammento di testo di questa quantità, rispetto al frammento di testo corrente. Funziona come un'istruzione di avanzamento riga. Combinato, ottieni un CR / LF.
Sergiu Dumitriu

Non ancora provato: potresti farlo anche senza il gruppo? <text x = "100" y = "100"> <tspan x = "100" y = "100"> testo molto lungo </tspan> <tspan x = "100" y = "115"> Vorrei interruzione di riga </tspan> </text> ??
Richard

25

Suppongo che tu sia già riuscito a risolverlo, ma se qualcuno sta cercando una soluzione simile, allora questo ha funzionato per me:

 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; })

Ci sono 3 righe separate da interruzione di riga.


21
FWIW: sembra che l'OP stesse usando JavaScript puro; questa risposta sembra sfruttare D3 .
Ben Mosher

Sto usando D3 e il tuo approccio ha funzionato per me. Grazie per averlo postato. Ho scoperto che dovevo eliminare i vecchi cucchiaini prima di aggiungerne di nuovi, in questo modo: focus.selectAll ("tspan"). Remove ();
Darren Parker

1
Attenzione con questo approccio che annida i tag <tspan> poiché concatena .append (). Ciò può causare alcuni piccoli mal di testa con i CSS a seconda di ciò che si desidera fare.
seneyr

Vedi qui per un approccio che eviti l'annidamento descritto da @seneyr
bszom

16

Con la soluzione tspan, diciamo che non sai in anticipo dove mettere le interruzioni di riga: puoi usare questa simpatica funzione, che ho trovato qui: http://bl.ocks.org/mbostock/7555321

Ciò fa automaticamente interruzioni di riga per svg di testo lungo per una data larghezza in 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);
      }
    }
  });
}

9

Penso che questo faccia quello che vuoi:

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");
}

Divide il testo \\\ne per ogni mette ogni frammento in un cucchiaino. Quindi calcola la dimensione della casella richiesta in base alla lunghezza massima del testo e al numero di righe. Dovrai anche modificare l'elemento di testo del suggerimento per contenere tre cucchiaini:

<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>

Ciò presuppone che tu non abbia mai più di tre righe. Se vuoi più di tre righe puoi aggiungere più cucchiaini e aumentare la lunghezza del ciclo for.


Perché è "\\\n"piuttosto che "\n"?
ralien

2

Ho adattato un po 'la soluzione di @steco, cambiando la dipendenza da d3a jquerye aggiungendo l' heightelemento di testo come parametro

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

usa HTML invece di javascript

<html>
  <head><style> * { margin: 0; padding: 0; } </style></head>
  <body>
    <h1>svg foreignObject to embed html</h1>

    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 300 300"
      x="0" y="0" height="300" width="300"
    >

      <circle
        r="142" cx="150" cy="150"
        fill="none" stroke="#000000" stroke-width="2"
      />

      <foreignObject
        x="50" y="50" width="200" height="200"
      >
        <div
          xmlns="http://www.w3.org/1999/xhtml"
          style="
            width: 196px; height: 196px;
            border: solid 2px #000000;
            font-size: 32px;
            overflow: auto; /* scroll */
          "
        >
          <p>this is html in svg 1</p>
          <p>this is html in svg 2</p>
          <p>this is html in svg 3</p>
          <p>this is html in svg 4</p>
        </div>
      </foreignObject>

    </svg>

</body></html>


Penso che tu voglia dire "usa SVG invece di JavaScript"
Valerio Bozz

È "HTML in SVG", la soluzione migliore per me!
Kévin Berthommier,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.