Ritorno a capo automatico nel testo SVG


108

Vorrei visualizzare un <text>in SVG che cosa si avvolge automaticamente nel contenitore <rect>allo stesso modo in cui il testo HTML riempie gli <div>elementi. C'è un modo per farlo? Non voglio posizionare le linee con parsimonia usando <tspan>s.

Risposte:


89

Il wrapping del testo non fa parte di SVG1.1, la specifica attualmente implementata. Dovresti piuttosto usare l'HTML tramite l' <foreignObject/>elemento.

<svg ...>

<switch>
<foreignObject x="20" y="90" width="150" height="200">
<p xmlns="http://www.w3.org/1999/xhtml">Text goes here</p>
</foreignObject>

<text x="20" y="20">Your SVG viewer cannot display html.</text>
</switch>

</svg>

5
Questo è il modo sbagliato di utilizzare switch, è necessario utilizzare una delle stringhe di funzionalità definite nelle specifiche svg. Il fallback non verrà mai utilizzato nel tuo esempio. Vedi w3.org/TR/SVG11/feature.html e w3.org/TR/SVG11/struct.html#SwitchElement .
Erik Dahlström

22
Anche <foreignObject /> non è supportato in IE
Doug Amos

3
Ma tieni presente che non tutti i motori possono eseguire il rendering di foreignObjects. In particolare, il batik no.
hrabinowitz

69

Ecco un'alternativa:

<svg ...>
  <switch>
    <g requiredFeatures="http://www.w3.org/Graphics/SVG/feature/1.2/#TextFlow">
      <textArea width="200" height="auto">
       Text goes here
      </textArea>
    </g>
    <foreignObject width="200" height="200" 
     requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
      <p xmlns="http://www.w3.org/1999/xhtml">Text goes here</p>
    </foreignObject>
    <text x="20" y="20">No automatic linewrapping.</text>
  </switch>
</svg>

Notando che anche se foreignObject può essere segnalato come supportato con quella featurestring, non c'è garanzia che l'HTML possa essere visualizzato perché non è richiesto dalla specifica SVG 1.1. Al momento non ci sono featurestring per il supporto di HTML in Foreignobject. Tuttavia, è ancora supportato in molti browser, quindi è probabile che diventi richiesto in futuro, forse con una stringa di caratteristiche corrispondente.

Nota che l' elemento "textArea" in SVG Tiny 1.2 supporta tutte le funzionalità svg standard, ad esempio riempimento avanzato, ecc. E che puoi specificare la larghezza o l'altezza come auto, il che significa che il testo può scorrere liberamente in quella direzione. ForeignObject funge da finestra di ritaglio.

Nota: sebbene l'esempio precedente sia un contenuto SVG 1.1 valido, in SVG 2 l'attributo 'requiredFeatures' è stato rimosso, il che significa che l'elemento 'switch' proverà a eseguire il rendering del primo elemento 'g' indipendentemente dal supporto per SVG 1.2 'textArea ' elementi. Vedere le specifiche dell'elemento interruttore SVG2 .


1
Stavo testando questo codice in FF, il browser non mi ha mostrato né l'elemento textArea né il figlio foreignObject. Quindi, dopo aver letto le specifiche, ha rilevato che l'attributo requiredFeatures si comporta in modo tale che, quando il suo elenco viene valutato in false, l'elemento che ha l'attributo requiredFeatures ei suoi figli non vengono elaborati. Quindi non ci sarà alcuna necessità per l'elemento interruttore. Dopo aver rimosso l'elemento switch, i bambini foreignObject erano visibili (perché il mio browser (FF, 8.01) supporta svg1.1). Quindi penso che non sia necessario l'elemento interruttore qui. Per favore mi faccia sapere.
Rajkamal Subramanian

Aggiornato ora per utilizzare un elemento <g>. Le specifiche svg non dicevano ai visualizzatori di guardare "requiredFeatures" su elementi sconosciuti, quindi è necessario utilizzare un elemento svg noto affinché funzioni come previsto.
Erik Dahlström

Grazie! Avevo bisogno di usare xhtml:divinvece di div, ma potrebbe essere a causa di d3.js. Non sono riuscito a trovare alcun riferimento utile su TextFlow, esiste (ancora) o era solo in qualche bozza?
johndodo

2
Va notato che textarea sembra non essere supportato in futuro bugzilla.mozilla.org/show_bug.cgi?id=413360
George Mauer

1
L'esempio non funziona in Chrome. Non sono stati testati in altri browser.
posfan12

15

Il textPath potrebbe essere utile per alcuni casi.

<svg width="200" height="200"
    xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
  <!-- define lines for text lies on -->
  <path id="path1" d="M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"></path>
 </defs>
 <use xlink:href="#path1" x="0" y="35" stroke="blue" stroke-width="1" />
 <text transform="translate(0,35)" fill="red" font-size="20">
  <textPath xlink:href="#path1">This is a long long long text ......</textPath>
 </text>
</svg>

3
Solo nel caso in cui è accettabile il ritorno a capo di una parola (e non il trattino). Non riesco a pensare a molti casi oltre ai progetti artistici in cui va bene. http://jsfiddle.net/nilloc/vL3zj/
Nilloc

4
@Nilloc Non tutti usano l'inglese, questo metodo va benissimo per cinese, giapponese o coreano.
Zang MingJie

@ZangMingJie Wrapping per linguaggi basati su caratteri (logografici) sembra un caso d'uso completamente diverso rispetto alla divisione delle parole. Il che è importante in tutte le lingue romantico / latino / cirillico / arabo (fonografico), che era il mio punto.
Nilloc

11

Basandomi sul codice di @Mike Gledhill, ho fatto un ulteriore passo avanti e ho aggiunto altri parametri. Se hai un SVG RECT e vuoi che il testo vada a capo al suo interno, questo potrebbe essere utile:

function wraptorect(textnode, boxObject, padding, linePadding) {

    var x_pos = parseInt(boxObject.getAttribute('x')),
    y_pos = parseInt(boxObject.getAttribute('y')),
    boxwidth = parseInt(boxObject.getAttribute('width')),
    fz = parseInt(window.getComputedStyle(textnode)['font-size']);  // We use this to calculate dy for each TSPAN.

    var line_height = fz + linePadding;

// Clone the original text node to store and display the final wrapping text.

   var wrapping = textnode.cloneNode(false);        // False means any TSPANs in the textnode will be discarded
   wrapping.setAttributeNS(null, 'x', x_pos + padding);
   wrapping.setAttributeNS(null, 'y', y_pos + padding);

// Make a copy of this node and hide it to progressively draw, measure and calculate line breaks.

   var testing = wrapping.cloneNode(false);
   testing.setAttributeNS(null, 'visibility', 'hidden');  // Comment this out to debug

   var testingTSPAN = document.createElementNS(null, 'tspan');
   var testingTEXTNODE = document.createTextNode(textnode.textContent);
   testingTSPAN.appendChild(testingTEXTNODE);

   testing.appendChild(testingTSPAN);
   var tester = document.getElementsByTagName('svg')[0].appendChild(testing);

   var words = textnode.textContent.split(" ");
   var line = line2 = "";
   var linecounter = 0;
   var testwidth;

   for (var n = 0; n < words.length; n++) {

      line2 = line + words[n] + " ";
      testing.textContent = line2;
      testwidth = testing.getBBox().width;

      if ((testwidth + 2*padding) > boxwidth) {

        testingTSPAN = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
        testingTSPAN.setAttributeNS(null, 'x', x_pos + padding);
        testingTSPAN.setAttributeNS(null, 'dy', line_height);

        testingTEXTNODE = document.createTextNode(line);
        testingTSPAN.appendChild(testingTEXTNODE);
        wrapping.appendChild(testingTSPAN);

        line = words[n] + " ";
        linecounter++;
      }
      else {
        line = line2;
      }
    }

    var testingTSPAN = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
    testingTSPAN.setAttributeNS(null, 'x', x_pos + padding);
    testingTSPAN.setAttributeNS(null, 'dy', line_height);

    var testingTEXTNODE = document.createTextNode(line);
    testingTSPAN.appendChild(testingTEXTNODE);

    wrapping.appendChild(testingTSPAN);

    testing.parentNode.removeChild(testing);
    textnode.parentNode.replaceChild(wrapping,textnode);

    return linecounter;
}

document.getElementById('original').onmouseover = function () {

    var container = document.getElementById('destination');
    var numberoflines = wraptorect(this,container,20,1);
    console.log(numberoflines);  // In case you need it

};

Grazie. che funziona perfettamente in Chrome. Ma non funziona in Firefox. Dice sul link demo. Valore imprevisto NaN durante l'analisi dell'attributo dy. svgtext_clean2.htm: 117 cercando di trovare una soluzione.
akshayb

Successivamente l'ho fatto funzionare in Firefox. Ecco qui:
MSC

1
(Ho premuto INVIO troppo presto proprio ora.) Successivamente ho funzionato in Firefox e IE. Se hai bisogno di aiuto, dai un'occhiata a democra.me/wrap_8_may_2014.htm . C'è un commento su Firefox nel codice.
MSC

Come puoi vedere, ho espanso molto il codice per ridurre il riquadro di delimitazione verso l'alto o verso il basso o troncarlo con un'ellissi nel posto giusto.
MSC

Modificherei una riga nel codice MSC:, boxwidth = parseInt(boxObject.getAttribute('width'))accetterei solo la larghezza in pixel, mentre boxwidth = parseInt(boxObject.getBBox().width), accetterei qualsiasi tipo di unità di misura
Massimiliano Caniparoli


7

Il codice seguente funziona correttamente. Esegui lo snippet di codice come funziona.

Forse può essere ripulito o farlo funzionare automaticamente con tutti i tag di testo in SVG.

function svg_textMultiline() {

  var x = 0;
  var y = 20;
  var width = 360;
  var lineHeight = 10;
  
  

  /* get the text */
  var element = document.getElementById('test');
  var text = element.innerHTML;

  /* split the words into array */
  var words = text.split(' ');
  var line = '';

  /* Make a tspan for testing */
  element.innerHTML = '<tspan id="PROCESSING">busy</tspan >';

  for (var n = 0; n < words.length; n++) {
    var testLine = line + words[n] + ' ';
    var testElem = document.getElementById('PROCESSING');
    /*  Add line in testElement */
    testElem.innerHTML = testLine;
    /* Messure textElement */
    var metrics = testElem.getBoundingClientRect();
    testWidth = metrics.width;

    if (testWidth > width && n > 0) {
      element.innerHTML += '<tspan x="0" dy="' + y + '">' + line + '</tspan>';
      line = words[n] + ' ';
    } else {
      line = testLine;
    }
  }
  
  element.innerHTML += '<tspan x="0" dy="' + y + '">' + line + '</tspan>';
  document.getElementById("PROCESSING").remove();
  
}


svg_textMultiline();
body {
  font-family: arial;
  font-size: 20px;
}
svg {
  background: #dfdfdf;
  border:1px solid #aaa;
}
svg text {
  fill: blue;
  stroke: red;
  stroke-width: 0.3;
  stroke-linejoin: round;
  stroke-linecap: round;
}
<svg height="300" width="500" xmlns="http://www.w3.org/2000/svg" version="1.1">

  <text id="test" y="0">GIETEN - Het college van Aa en Hunze is in de fout gegaan met het weigeren van een zorgproject in het failliete hotel Braams in Gieten. Dat stelt de PvdA-fractie in een brief aan het college. De partij wil opheldering over de kwestie en heeft schriftelijke
    vragen ingediend. Verkeerde route De PvdA vindt dat de gemeenteraad eerst gepolst had moeten worden, voordat het college het plan afwees. "Volgens ons is de verkeerde route gekozen", zegt PvdA-raadslid Henk Santes.</text>

</svg>


1
Ritorno a capo automatico nel testo SVG :) Il mio codice javascript crea linee quando il testo è troppo lungo. Sarebbe bello se lavorassi su tutti i tag di testo all'interno di SVG. automatico senza modificare l'id = "" in javascript. Peccato che SVG non abbia più righe da solo.
Peter

Bella soluzione, ma puoi allinearla al centro?
Krešimir Galić

Dovrebbe essere accettata la risposta tbh. La soluzione javascript è abbastanza minima e ha senso.
Zac

4

Ho pubblicato la seguente procedura dettagliata per l'aggiunta di un finto ritorno a capo automatico a un elemento "testo" SVG qui:

A capo automatico SVG - Show stopper?

Devi solo aggiungere una semplice funzione JavaScript, che divide la tua stringa in elementi "tspan" più brevi. Ecco un esempio di come appare:

Esempio SVG

Spero che questo ti aiuti !

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.