So platzieren und zentrieren Sie Text in einem SVG-Rechteck

179

Ich habe das folgende Rechteck ...

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

Ich möchte das Wort "Fiktion" darin zentrieren. Wird bei anderen Rechtecken ein SVG-Zeilenumbruch verwendet, um in diesen zu bleiben? Ich kann anscheinend nichts spezielles zum Einfügen von Text in Formen finden, die sowohl horizontal als auch vertikal zentriert sind, sowie zum Zeilenumbruch. Außerdem kann der Text das Rechteck nicht verlassen.

Ein Blick auf das Beispiel http://www.w3.org/TR/SVG/text.html#TextElement hilft nicht, da sich x und y des Textelements von x und y des Rechtecks ​​unterscheiden. Es scheint keine Breite und Höhe für Textelemente zu geben. Ich bin mir der Mathematik hier nicht sicher.

(Meine HTML-Tabelle wird einfach nicht funktionieren.)

Lady Aleena
quelle
1
Könnten Sie bitte mit Textanker und Dominant-Baseline zur Antwort wechseln? Vielen Dank!
Neaumusic
1
neaumusic, fertig. 8)
Lady Aleena

Antworten:

309

Eine einfache Lösung, um Text in SVG horizontal und vertikal zu zentrieren:

  1. Stellen Sie die Position des Texts auf die absolute Mitte des Elements ein, in dem Sie ihn zentrieren möchten:

    • Wenn es der Elternteil ist, könnten Sie es einfach tun x="50%" y ="50%".
    • Wenn es sich um ein anderes Element handelt, xwäre dies das xElement + die Hälfte seiner Breite (und ähnlich für, yaber mit der Höhe).
  2. Verwenden Sie die text-anchorEigenschaft, um den Text horizontal mit dem Wert zu zentrieren middle:

    Mitte

    Die gerenderten Zeichen werden so ausgerichtet, dass sich die geometrische Mitte des resultierenden gerenderten Texts an der anfänglichen aktuellen Textposition befindet.

  3. Verwenden Sie die dominant-baselineEigenschaft, um den Text vertikal mit dem Wert zu zentrieren middle(oder je nachdem, wie er aussehen soll, möchten Sie dies möglicherweise tun central)

Hier ist eine einfache 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>

Alvaro Montoro
quelle
1
Ich habe überall nach einer so einfachen Lösung gesucht - nichts anderes hat für mich funktioniert. Ich habe diese verwendet, um eine Zahl in einem radialen Fortschrittsbalken zu zentrieren
anthonytherockjohnson
7
In meinem Fall ist es die dominant-baselineEigenschaft, die den Job erledigt, nicht die alignment-baseline.
Pintoch
1
Wenn Sie dies regelmäßig tun, können Sie auch Folgendes hinzufügen : <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Danke für die Lösung.
Manngo
1
Führen Sie Ihr eigenes Code-Snippet aus. Es funktioniert nicht. Probieren Sie es aus: Es funktioniert sogar nicht in der Vorschau, die von stackoverflow selbst bereitgestellt wird. Wie bereits im Kommentar erwähnt, dominant-baselinemuss in der Tat verwendet werden. Es tut mir leid, aber diese Antwort ist irgendwie irreführend. Da es weder in Firefox noch in Chromium zu funktionieren scheint und Sie außerdem die Kommentare studieren müssen, um selbst die richtige Lösung zu finden, habe ich mir erlaubt, eine weitere Antwort mit Code hinzuzufügen, der wie erwartet funktioniert. (Für die Aufzeichnungen: Getestet mit FF und Chromium unter Ubuntu Linux.)
Regis
2
@RegisMay danke für den Hinweis. Ich bin auf Chrome auf dem Mac und es funktionierte gut mit alignment-baseline, aber es scheint , dass es sich um eine skurrile Sache sein kann , weil die Spezifikationen überprüft, es sein sollte dominant-baselinefür textund alignment-baselinefür tspan, tref, altGlyph, und textPath. Ich habe die Antwort entsprechend aktualisiert.
Alvaro Montoro
38

SVG 1.2 Tiny hat Textumbruch hinzugefügt, aber die meisten Implementierungen von SVG, die Sie im Browser finden (mit Ausnahme von Opera), haben diese Funktion nicht implementiert. In der Regel liegt es an Ihnen, dem Entwickler, den Text manuell zu positionieren.

Die SVG 1.1-Spezifikation bietet einen guten Überblick über diese Einschränkung und die möglichen Lösungen, um sie zu überwinden:

Jedes 'Text'-Element bewirkt, dass eine einzelne Textzeichenfolge gerendert wird. SVG führt keinen automatischen Zeilenumbruch oder Zeilenumbruch durch. Verwenden Sie eine der folgenden Methoden, um die Wirkung mehrerer Textzeilen zu erzielen:

  • Der Autor oder das Authoring-Paket muss die Zeilenumbrüche vorberechnen und mehrere Textelemente verwenden (eines für jede Textzeile).
  • Der Autor oder das Authoring-Paket muss die Zeilenumbrüche vorberechnen und ein einzelnes 'Text'-Element mit einem oder mehreren untergeordneten' tspan'-Elementen mit geeigneten Werten für die Attribute 'x', 'y', 'dx' und 'dy' verwenden. um neue Startpositionen für die Zeichen festzulegen, die neue Zeilen beginnen. (Dieser Ansatz ermöglicht die Auswahl von Benutzertext über mehrere Textzeilen hinweg - siehe Textauswahl und Operationen in der Zwischenablage.)
  • Drücken Sie den Text aus, der in einem anderen XML-Namespace wie XHTML [XHTML] gerendert werden soll, der inline in ein 'alienObject'-Element eingebettet ist. (Hinweis: Die genaue Semantik dieses Ansatzes ist derzeit noch nicht vollständig definiert.)

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

Als Grundelement kann der Textumbruch mithilfe des dyAttributs und der tspanElemente simuliert werden. Wie in der Spezifikation erwähnt, können einige Tools dies automatisieren. Wählen Sie in Inkscape beispielsweise die gewünschte Form und den gewünschten Text aus und verwenden Sie Text -> In Frame fließen. Auf diese Weise können Sie Ihren Text mit Umbruch schreiben, der basierend auf den Grenzen der Form umbrochen wird. Befolgen Sie außerdem diese Anweisungen, um Inkscape anzuweisen, die Kompatibilität mit SVG 1.1 aufrechtzuerhalten: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

Darüber hinaus gibt es einige JavaScript-Bibliotheken, mit denen das Umbrechen von Text dynamisch automatisiert werden kann: http://www.carto.net/papers/svg/textFlow/

Es ist interessant, die Lösung von CSVG zum Umschließen einer Form mit einem Textelement zu beachten (siehe z. B. das Beispiel "Schaltfläche"), obwohl zu erwähnen ist, dass die Implementierung in einem Browser nicht verwendet werden kann: http://www.csse.monash.edu .au / ~ clm / csvg / about.html

Ich erwähne dies, weil ich eine CSVG-inspirierte Bibliothek entwickelt habe, mit der Sie ähnliche Dinge tun können und die in Webbrowsern funktioniert, obwohl ich sie noch nicht veröffentlicht habe.

jbeard4
quelle
Danke für die Antwort ... aber ACK! Es scheint, dass ich dann svg ausgeben muss. Eines der ersten Dinge, an die die Entwickler hätten denken müssen, war das Anhängen von Text (formatiert nach Wunsch der Benutzer) an Formen! Ich werde warten, bis sie sich zusammengetan haben, bevor ich mehr damit mache. Ich werde versuchen, es in Windows Paint neu zu zeichnen und zu sehen, ob dies funktioniert. (Wenn ich nur die Browser dazu bringen könnte, die Tabelle korrekt anzuzeigen.)
Lady Aleena
Über bearbeitbaren Text in svg unterstützt Opera das bearbeitbare Attribut von SVG 1.2 Tiny, mit dem bearbeitbarer Text durch einfaches Hinzufügen eines Attributs editable="true"zum Text- oder textArea-Element abgerufen werden kann.
Erik Dahlström
2
@Erik, wieder einmal ist Opera der Kurve voraus.
jbeard4
@ Lady Aleena, warum nicht einfach die von mir erwähnte Inkscape-Lösung verwenden? Wenn es sich nur um ein statisches Bild handelt (z. B. kein dynamisches Verhalten, keine dynamische Aktualisierung des Textinhalts), sollte dies problemlos funktionieren. Außerdem ist es skalierbar, was Paint nicht kann, da Paint Rastergrafiken erzeugt. Schließlich glaube ich nicht, dass Paint das Umbrechen von Text auf irgendeiner Ebene unterstützt.
jbeard4
@ echo-flow Ich habe vor langer Zeit jegliches Vertrauen in Code-produzierende Programme verloren, seit ich Microsoft Front Page zum Generieren von HTML verwendet (und anschließend ausgegeben) habe. Ich bin immer vorsichtig, wenn Programme dem, was ich schreibe, ihren eigenen proprietären Code hinzufügen. Die Titelseite hat mein HTML wirklich durcheinander gebracht. Wenn Sie ein Inkscape-Benutzer sind, können Sie mir sagen, ob Inkscape einen eigenen proprietären Code in die SVG-Datei einfügt, in den ich nach der Erstellung der SVG-Datei gehen und ihn entfernen müsste?
Lady Aleena
23

Die vorherigen Antworten ergaben schlechte Ergebnisse bei Verwendung von abgerundeten Ecken oder stroke-widthdas ist> 1. Sie würden beispielsweise erwarten, dass der folgende Code ein abgerundetes Rechteck erzeugt, die Ecken werden jedoch von der übergeordneten svgKomponente abgeschnitten :

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

Stattdessen empfehle ich, das textin ein zu wickeln svgund dann das Neue svgund das rectZusammen in ein gElement zu verschachteln , wie im folgenden Beispiel:

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

Dies behebt das Beschneidungsproblem, das in den obigen Antworten auftritt. Ich habe auch die Rect / Text-Gruppe mit dem transform="translate(x,y)"Attribut übersetzt, um zu demonstrieren, dass dies einen intuitiveren Ansatz für die Positionierung des Rect / Text auf dem Bildschirm bietet.

Austin D.
quelle
15

Wenn Sie die SVG programmgesteuert erstellen, können Sie sie vereinfachen und Folgendes tun:

  <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>
Brennan Cheung
quelle
3
Sollte es nicht sein dominant-baselineund text-anchornicht dominantBaselineund textAnchor?
Nathaniel M. Beaver
1
@ NathanielM.Beaver Ja, das sollte es sein. Guter Fang. Der obige Code stammt von React, für den leider die Umbenennung von Attributen mit camelCase erforderlich ist.
Brennan Cheung
13

Sie können die text-anchor = "middle"Eigenschaft direkt verwenden . Ich empfehle, ein Wrapper-SVG-Element über Ihrem Rechteck und Text zu erstellen. Auf diese Weise können Sie das gesamte Element mit einem CSS-Selektor verwenden. Stellen Sie sicher, dass Sie die Texteigenschaften 'x' und 'y' als 50% platzieren.

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

Zookastos
quelle
7

Vollständiges Detail-Blog: 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>

Sahil Gupta
quelle
Nicht in Firefox zentriert, während andere Lösungen sind. codepen.io/anon/pen/oEByYr
tgf
Ich habe auf Firefox 58.0.1 (64-Bit) getestet, es funktioniert. Können Sie die Version erwähnen, für die es für Sie nicht funktioniert?
Sahil Gupta
Firefox 58.0.2 64bit. Der Text ist etwas höher als er sein sollte. Es ist zunächst vielleicht nicht leicht zu erkennen, aber wenn Ihre Box sehr genau passt, ist dies offensichtlicher. Versuchen Sie, die Höhe zu verringern.
tgf
6

alignment-baselineist nicht das richtige Attribut, um hier zu verwenden. Die richtige Antwort ist die Verwendung einer Kombination aus dominant-baseline="central"und 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>

Regis May
quelle
2

Eine Möglichkeit, Text in ein Rechteck einzufügen, besteht darin, ein Fremdobjekt, das ein DIV ist, in ein direktes Objekt einzufügen.

Auf diese Weise entspricht der Text den Grenzen des 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>

Bruno Bastos
quelle
1

Ich hatte eine verdammte Zeit damit, mit SVG alles zu zentrieren, also rollte ich meine eigene kleine Funktion. hoffentlich sollte es dir helfen. Beachten Sie, dass dies nur für SVG-Elemente funktioniert.

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);
}
John Ktejik
quelle
1

Für die horizontale und vertikale Ausrichtung von Text in Grafiken möchten Sie möglicherweise die folgenden CSS-Stile verwenden. Beachten Sie insbesondere, dass dies dominant-baseline:middlewahrscheinlich falsch ist, da dies (normalerweise) auf halbem Weg zwischen der Oberseite und der Grundlinie und nicht auf halbem Weg zwischen der Oberseite und der Unterseite liegt. Einige Quellen (z. B. Mozilla ) verwenden dominant-baseline:hangingstattdessen anstelle von dominant-baseline:text-before-edge. Dies ist wahrscheinlich auch falsch, da hanginges für Indic-Skripte ausgelegt ist. Wenn Sie eine Mischung aus Latein, Indisch, Ideogrammen oder was auch immer verwenden, müssen Sie wahrscheinlich die Dokumentation lesen .

/* 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}

Bearbeiten: Ich habe gerade bemerkt, dass Mozilla auch verwendet, dominant-baseline:baselinewas definitiv falsch ist: Es ist nicht einmal ein anerkannter Wert! Ich gehe davon aus, dass standardmäßig die Schriftart verwendet wird alphabetic, also haben sie Glück gehabt.

Mehr bearbeiten: Safari (11.1.2) versteht text-before-edgeaber nicht text-after-edge. Es schlägt auch fehl ideographic. Tolles Zeug, Apple. Sie könnten also gezwungen sein, alphabeticNachkommen zu verwenden und zuzulassen. Es tut uns leid.

Adam Chalcraft
quelle