Come posizionare e centrare il testo in un rettangolo SVG


179

Ho il seguente rettangolo ...

<rect x="0px" y="0px" width="60px" height="20px"/>

Vorrei centrare la parola "Fiction" al suo interno. Per altri rettangoli, la parola svg si avvolge per rimanere al loro interno? Non riesco a trovare nulla di specifico sull'inserimento di testo all'interno di forme centrate sia in orizzontale che in verticale e a capo automatico. Inoltre, il testo non può lasciare il rettangolo.

Guardando l' esempio http://www.w3.org/TR/SVG/text.html#TextElement non aiuta poiché l'elemento x e y dell'elemento di testo sono diversi da x e y del rettangolo. Non sembra esserci larghezza e altezza per gli elementi di testo. Non sono sicuro della matematica qui.

(La mia tabella html non funzionerà.)


1
potresti passare alla risposta usando text-anchor e dominant-baseline? Grazie!
neaumusica il

1
neaumusico, fatto. 8)
Lady Aleena,

Risposte:


310

Una soluzione semplice per centrare il testo in orizzontale e in verticale in SVG:

  1. Imposta la posizione del testo sul centro assoluto dell'elemento in cui vuoi centrarlo:

    • Se è il genitore, potresti semplicemente farlo x="50%" y ="50%".
    • Se è un altro elemento, xsarebbe il xdi quell'elemento + metà della sua larghezza (e simile per yma con l'altezza).
  2. Utilizzare la text-anchorproprietà per centrare il testo in orizzontale con il valore middle:

    mezzo

    I caratteri di rendering sono allineati in modo tale che il centro geometrico del testo di rendering risultante si trovi nella posizione di testo corrente iniziale.

  3. Usa la dominant-baselineproprietà per centrare il testo verticalmente con il valore middle(o a seconda di come vuoi che appaia, potresti voler fare central)

Ecco una semplice demo:

<svg width="200" height="100">
  <rect x="0" y="0" width="200" height="100" stroke="red" stroke-width="3px" fill="white"/>
  <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle">TEXT</text>    
</svg>


1
Ho cercato ovunque una soluzione così semplice - nient'altro funzionava per me L'ho usato per centrare un numero all'interno di una barra di avanzamento radiale
anthonytherockjohnson

7
Nel mio caso, è la dominant-baselineproprietà che fa il lavoro, non il alignment-baseline.
pintoch

1
Se avete intenzione di farlo regolarmente, si può anche aggiungere la seguente: <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Grazie per la soluzione
Manngo,

1
Esegui il tuo snippet di codice. Non funziona Dai un'occhiata: non funziona nemmeno nell'anteprima fornita dallo stackoverflow stesso. Come affermato in precedenza nel commento, è dominant-baselinedavvero necessario utilizzarlo. Mi dispiace, ma questa risposta è in qualche modo fuorviante. Dato che non sembra funzionare in Firefox né Chromium e inoltre devi studiare i commenti per trovare la soluzione corretta da solo, mi sono preso la libertà di aggiungere un'altra risposta con il codice che funziona come previsto. (Per la cronaca: testato con FF e Chromium su Ubuntu Linux.)
Regis,

2
@RegisMay grazie per averlo segnalato. Sono su Chrome su Mac e ha funzionato bene con alignment-baseline, ma sembra che possa essere una cosa bizzarra, perché rivedere le specifiche, dovrebbe essere dominant-baselineper text, e alignment-baselineper tspan, tref, altGlyph, e textPath. Ho aggiornato la risposta di conseguenza.
Alvaro Montoro,

38

SVG 1.2 Piccolo testo aggiunto a capo automatico, ma la maggior parte delle implementazioni di SVG che troverai nel browser (ad eccezione di Opera) non hanno implementato questa funzione. Spetta in genere allo sviluppatore posizionare manualmente il testo.

La specifica SVG 1.1 fornisce una buona panoramica di questa limitazione e delle possibili soluzioni per superarla:

Ogni elemento "text" provoca il rendering di una singola stringa di testo. SVG non esegue alcuna interruzione di riga o ritorno a capo automatico. Per ottenere l'effetto di più righe di testo, utilizzare uno dei seguenti metodi:

  • L'autore o il pacchetto di creazione deve pre-calcolare le interruzioni di riga e utilizzare più elementi di "testo" (uno per ogni riga di testo).
  • L'autore o il pacchetto di authoring deve pre-calcolare le interruzioni di riga e utilizzare un singolo elemento 'text' con uno o più elementi figlio 'tspan' con valori appropriati per gli attributi 'x', 'y', 'dx' e 'dy' per impostare nuove posizioni iniziali per quei personaggi che iniziano nuove linee. (Questo approccio consente la selezione del testo dell'utente su più righe di testo: vedere Selezione del testo e operazioni negli Appunti.)
  • Esprimi il testo da renderizzare in un altro spazio dei nomi XML come XHTML [XHTML] incorporato in linea all'interno di un elemento 'foreignObject'. (Nota: la semantica esatta di questo approccio non è completamente definita in questo momento.)

http://www.w3.org/TR/SVG11/text.html#Introduction

Come primitivo, il wrapping del testo può essere simulato utilizzando l' dyattributo e gli tspanelementi e, come menzionato nelle specifiche, alcuni strumenti possono automatizzarlo. Ad esempio, in Inkscape, seleziona la forma desiderata e il testo desiderato e usa Testo -> Scorri nella cornice. Questo ti permetterà di scrivere il tuo testo, con il wrapping, che si avvolgerà in base ai limiti della forma. Inoltre, assicurati di seguire queste istruzioni per dire a Inkscape di mantenere la compatibilità con SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

Inoltre, ci sono alcune librerie JavaScript che possono essere utilizzate per automatizzare dinamicamente il wrapping del testo: http://www.carto.net/papers/svg/textFlow/

È interessante notare la soluzione di CSVG per racchiudere una forma in un elemento di testo (ad es. Vedere il loro esempio di "pulsante"), sebbene sia importante ricordare che la loro implementazione non è utilizzabile in un browser: http://www.csse.monash.edu .au / ~ CLM / CSVG / about.html

Sto citando questo perché ho sviluppato una libreria ispirata a CSVG che ti consente di fare cose simili e funziona nei browser Web, anche se non l'ho ancora rilasciato.


Grazie per la risposta ... ma ACK! Sembra che dovrò scaricare svg allora. Una delle prime cose a cui gli sviluppatori avrebbero dovuto pensare era allegare del testo (formattato come desiderano gli utenti) alle forme! Aspetterò fino a quando non si metteranno insieme i loro atti prima di farne di più. Proverò a ridisegnarlo in Windows Paint e vedere se lo farà. (Se solo potessi far visualizzare correttamente la tabella ai browser.)
Lady Aleena,

Per quanto riguarda il testo modificabile in svg, Opera supporta l'attributo modificabile di SVG 1.2 Tiny, che consente di ottenere testo modificabile semplicemente aggiungendo un attributo editable="true"all'elemento text o textArea.
Erik Dahlström,

2
@Erik, ancora una volta Opera è davanti alla curva.
jbeard4,

@Lady Aleena, perché non usare semplicemente la soluzione Inkscape che ho citato? Se è solo un'immagine statica (ad es. Nessun comportamento dinamico, nessun aggiornamento dinamico del contenuto del testo), allora dovrebbe funzionare correttamente. Inoltre, sarà scalabile, cosa che Paint no, poiché Paint produce grafica raster. Infine, non credo che Paint supporti il ​​wrapping del testo a qualsiasi livello.
jbeard4,

@ echo-flow Ho perso tutta la fiducia nei programmi di produzione di codice molto tempo fa da quando ho usato (e successivamente scaricato) Microsoft Front Page per generare html. Sono sempre diffidente nei confronti dei programmi che aggiungono il proprio codice proprietario a ciò che sto scrivendo. Front Page ha davvero incasinato il mio HTML. Se sei un utente Inkscape, puoi dirmi se Inkscape inserisce il suo codice proprietario nello svg in cui dovrei andare e rimuovere dopo la creazione di svg?
Lady Aleena,

23

Le risposte precedenti hanno dato scarsi risultati quando si usano gli angoli arrotondati o stroke-widthche è> 1. Ad esempio, ci si aspetterebbe che il codice seguente produca un rettangolo arrotondato, ma gli angoli vengono ritagliati dal svgcomponente padre :

<svg width="200" height="100">
  <!--this rect should have rounded corners-->
  <rect x="0" y="0" rx="5" ry="5" width="200" height="100" stroke="red" stroke-width="10px" fill="white"/>
  <text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle">CLIPPED BORDER</text>    
</svg>

Invece, ti consiglio di racchiudere textin a svge quindi annidare quello nuovo svge quello rectinsieme all'interno di un gelemento, come nell'esempio seguente:

<!--the outer svg here-->
<svg width="400px" height="300px">

  <!--the rect/text group-->
  <g transform="translate(50,50)">
    <rect rx="5" ry="5" width="200" height="100" stroke="green" fill="none" stroke-width="10"/>
    <svg width="200px" height="100px">
      <text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle">CORRECT BORDER</text>      
    </svg>
  </g>

  <!--rest of the image's code-->
</svg>

Questo risolve il problema di clipping che si verifica nelle risposte sopra. Ho anche tradotto il gruppo rettangolo / testo usando l' transform="translate(x,y)"attributo per dimostrare che ciò fornisce un approccio più intuitivo al posizionamento del rettangolo / testo sullo schermo.


16

Se stai creando SVG a livello di codice, puoi semplificarlo e fare qualcosa del genere:

  <g>
      <rect x={x} y={y} width={width} height={height} />
      <text
          x={x + width / 2}
          y={y + height / 2}
          dominant-baseline="middle"
          text-anchor="middle"
      >
          {label}
      </text>
  </g>

3
Non dovrebbe essere dominant-baselinee text-anchor, non dominantBaselinee textAnchor?
Nathaniel M. Beaver,

1
@ NathanielM.Beaver Sì, dovrebbe essere. Buona pesca. Il codice sopra è da React che purtroppo richiede che gli attributi vengano rinominati usando camelCase.
Brennan Cheung,

13

Puoi usare direttamente la text-anchor = "middle"proprietà. Vi consiglio di creare un elemento svg wrapper sul rettangolo e sul testo. In questo modo puoi usare l'intero elemento usando un selettore css. Assicurati di posizionare le proprietà 'x' e 'y' del testo al 50%.

    <svg class="svg-rect" width="50" height="40">
        <rect x="0" y="0" rx="3" ry="3" width="50" height="40" fill="#e7e7e7"></rect>
        <text x="50%" y="50%" text-anchor="middle" stroke="black" stroke-width="1px" dy=".3em">N/A</text>
    </svg>


7

Blog con dettagli completi: http://blog.techhysahil.com/svg/how-to-center-text-in-svg-shapes/

<svg width="600" height="600">
  <!--   Circle -->
  <g transform="translate(50,40)">
    <circle cx="0" cy="0" r="35" stroke="#aaa" stroke-width="2" fill="#fff"></circle>
    <text x="0" y="0" alignment-baseline="middle" font-size="12" stroke-width="0" stroke="#000" text-anchor="middle">HueLink</text>
  </g>
  
  <!--   In Rectangle text position needs to be given half of width and height of rectangle respectively -->
  <!--   Rectangle -->
  <g transform="translate(150,20)">
    <rect width="150" height="40" stroke="#aaa" stroke-width="2" fill="#fff"></rect>
    <text x="75" y="20" alignment-baseline="middle" font-size="12" stroke-width="0" stroke="#000" text-anchor="middle">HueLink</text>
  </g>
  
  <!--   Rectangle -->
  <g transform="translate(120,140)">
    <ellipse cx="0" cy="0" rx="100" ry="50" stroke="#aaa" stroke-width="2" fill="#fff"></ellipse>
    <text x="0" y="0" alignment-baseline="middle" font-size="12" stroke-width="0" stroke="#000" text-anchor="middle">HueLink</text>
  </g>
  
  
</svg>


Non centrato su Firefox mentre lo sono altre soluzioni. codepen.io/anon/pen/oEByYr
tgf

Ho provato su Firefox 58.0.1 (64-bit), funziona. Puoi citare la versione per la quale non funziona per te?
Sahil Gupta,

Firefox 58.0.2 64 bit. Il testo è leggermente più alto di quanto dovrebbe essere. All'inizio potrebbe non essere facilmente evidente, ma se la tua scatola si adatta molto da vicino è più ovvio; prova a ridurre l'altezza.
martedì

6

alignment-baselinenon è l'attributo giusto da usare qui. La risposta corretta è utilizzare una combinazione di dominant-baseline="central"e text-anchor="middle":

<svg width="200" height="100">
    <g>
        <rect x="0" y="0" width="200" height="100" style="stroke:red; stroke-width:3px; fill:white;"/>
        <text x="50%" y="50%" style="dominant-baseline:central; text-anchor:middle; font-size:40px;">TEXT</text>
    </g>
</svg>


2

Un modo per inserire testo all'interno di un rettangolo è inserire un oggetto estraneo, che è un DIV, all'interno di un oggetto rettangolo.

In questo modo, il testo rispetta i limiti del DIV.

var g = d3.select("svg");
					
g.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width","100%")
.attr("height","100%")
.attr("fill","#000");


var fo = g.append("foreignObject")
 .attr("width","100%");

fo.append("xhtml:div")
  .attr("style","width:80%;color:#FFF;margin-right: auto;margin-left: auto;margin-top:40px")
  .text("Mussum Ipsum, cacilds vidis litro abertis Mussum Ipsum, cacilds vidis litro abertis Mussum Ipsum, cacilds vidis litro abertis");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.js"></script>
<svg width="200" height="200"></svg>


1

Ho avuto un sacco di tempo per centrare qualsiasi cosa usando SVG, quindi ho implementato la mia piccola funzione. speriamo che ti possa aiutare. Nota che funziona solo con elementi SVG.

function centerinparent(element) { //only works for SVG elements
    var bbox = element.getBBox();
    var parentwidth = element.parentNode.width.baseVal.value;
    var parentheight = element.parentNode.height.baseVal.value;
    var newwidth = ((parentwidth / 2) - (bbox.width / 2)) - 2; //i start everything off by 2 to account for line thickness
    var newheight = ((parentheight / 2) - (bbox.height / 2)) - 2;
    //need to adjust for line thickness??

    if (element.classList.contains("textclass")) { //text is origined from bottom left, whereas everything else origin is top left
        newheight += bbox.height; //move it down by its height
    }

    element.setAttributeNS(null, "transform", "translate(" + newwidth + "," + newheight + ")");
    // console.log("centering BOXES:  between width:"+element.parentNode.width.baseVal.value + "   height:"+parentheight);
    // console.log(bbox);
}

1

Per l'allineamento orizzontale e verticale del testo nella grafica, potresti voler utilizzare i seguenti stili CSS. In particolare, si noti che dominant-baseline:middleprobabilmente è sbagliato, poiché questo è (di solito) a metà strada tra la parte superiore e la linea di base, piuttosto che a metà strada tra la parte superiore e quella inferiore. Inoltre, alcune fonti (ad esempio Mozilla ) usano al dominant-baseline:hangingposto di dominant-baseline:text-before-edge. Anche questo è probabilmente sbagliato, poiché hangingè progettato per gli script Indic. Ovviamente, se stai usando una miscela di latino, indi, ideografi o altro, probabilmente dovrai leggere la documentazione .

/* Horizontal alignment */
text.goesleft{text-anchor:end}
text.equalleftright{text-anchor:middle}
text.goesright{text-anchor:start}
/* Vertical alignment */
text.goesup{dominant-baseline:text-after-edge}
text.equalupdown{dominant-baseline:central}
text.goesdown{dominant-baseline:text-before-edge}
text.ruledpaper{dominant-baseline:alphabetic}

Modifica: ho appena notato che Mozilla utilizza anche ciò dominant-baseline:baselineche è decisamente sbagliato: non è nemmeno un valore riconosciuto! Suppongo che sia l'impostazione predefinita per il carattere predefinito, ovvero alphabetic, quindi sono stati fortunati.

Altre modifiche: Safari (11.1.2) comprende text-before-edgema non text-after-edge. Inoltre fallisce ideographic. Roba fantastica, Apple. Quindi potresti essere costretto a usare alphabetice consentire a tutti i discensori. Scusate.

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.