Comment placer et centrer du texte dans un rectangle SVG

179

J'ai le rectangle suivant ...

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

Je voudrais centrer le mot "Fiction" à l'intérieur. Pour les autres rectangles, le mot svg s'enroule-t-il pour y rester? Je n'arrive pas à trouver quoi que ce soit de spécifiquement sur l'insertion de texte dans des formes centrées à la fois horizontalement et verticalement et sur le retour à la ligne. De plus, le texte ne peut pas quitter le rectangle.

Regarder l' exemple http://www.w3.org/TR/SVG/text.html#TextElement n'aide pas puisque les x et y de l'élément de texte sont différents des x et y du rectangle. Il ne semble pas y avoir de largeur et de hauteur pour les éléments de texte. Je ne suis pas sûr du calcul ici.

(Ma table html ne fonctionnera tout simplement pas.)

Dame Aleena
la source
1
pourriez-vous s'il vous plaît passer à la réponse en utilisant text-anchor et dominant-baseline? Merci!
neaumusic
1
neaumusic, terminé. 8)
Lady Aleena

Réponses:

310

Une solution simple pour centrer le texte horizontalement et verticalement en SVG:

  1. Définissez la position du texte sur le centre absolu de l'élément dans lequel vous souhaitez le centrer:

    • Si c'est le parent, vous pouvez le faire x="50%" y ="50%".
    • Si c'est un autre élément, xserait le xde cet élément + la moitié de sa largeur (et similaire pour ymais avec la hauteur).
  2. Utilisez la text-anchorpropriété pour centrer le texte horizontalement avec la valeur middle:

    milieu

    Les caractères rendus sont alignés de telle sorte que le milieu géométrique du texte rendu résultant se trouve à la position initiale du texte actuelle.

  3. Utilisez la dominant-baselinepropriété pour centrer le texte verticalement avec la valeur middle(ou selon à quoi vous voulez qu'il ressemble, vous voudrez peut-être faire central)

Voici une démo simple:

<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
la source
1
J'ai cherché partout une solution aussi simple - rien d'autre ne fonctionnait pour moi J'ai utilisé cela pour centrer un nombre dans une barre de progression radiale
anthonytherockjohnson
7
Dans mon cas, c'est la dominant-baselinepropriété qui fait le travail, pas le alignment-baseline.
pintoch
1
Si vous allez le faire régulièrement, vous pouvez également ajouter ce qui suit: <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Merci pour la solution.
Manngo
1
Exécutez votre propre extrait de code. Ça ne marche pas. Vérifiez-le: cela ne fonctionne même pas dans l'aperçu fourni par stackoverflow lui-même. Comme indiqué précédemment dans le commentaire dominant-baselinedoit en effet être utilisé. Je suis désolé, mais cette réponse est en quelque sorte trompeuse. Comme cela ne semble pas fonctionner dans Firefox ni dans Chromium et en plus vous devez étudier les commentaires afin de trouver la bonne solution par vous-même, je me suis permis d'ajouter une autre réponse avec un code qui fonctionne comme prévu. (Pour les enregistrements: Testé avec FF et Chromium sur Ubuntu Linux.)
Regis
2
@RegisMay merci de l'avoir signalé. Je suis sur Chrome sur Mac et il a bien fonctionné avec alignment-baseline, mais il semble que ce peut être une chose bizarre parce que l' examen des spécifications, il devrait être dominant-baselinepour text, et alignment-baselinepour tspan, tref, altGlyphet textPath. J'ai mis à jour la réponse en conséquence.
Alvaro Montoro
38

SVG 1.2 Tiny a ajouté un habillage de texte, mais la plupart des implémentations de SVG que vous trouverez dans le navigateur (à l'exception d'Opera) n'ont pas implémenté cette fonctionnalité. C'est généralement à vous, le développeur, de positionner le texte manuellement.

La spécification SVG 1.1 donne un bon aperçu de cette limitation et des solutions possibles pour la surmonter:

Chaque élément 'text' entraîne le rendu d'une seule chaîne de texte. SVG n'effectue aucun saut de ligne ou retour à la ligne automatique. Pour obtenir l'effet de plusieurs lignes de texte, utilisez l'une des méthodes suivantes:

  • L'auteur ou le package de création doit pré-calculer les sauts de ligne et utiliser plusieurs éléments «texte» (un pour chaque ligne de texte).
  • L'auteur ou le package de création doit pré-calculer les sauts de ligne et utiliser un seul élément 'text' avec un ou plusieurs éléments enfants 'tspan' avec des valeurs appropriées pour les attributs 'x', 'y', 'dx' et 'dy' pour définir de nouvelles positions de départ pour les personnages qui commencent de nouvelles lignes. (Cette approche permet à l'utilisateur de sélectionner du texte sur plusieurs lignes de texte - voir Sélection de texte et opérations du presse-papiers.)
  • Exprimez le texte à rendre dans un autre espace de noms XML tel que XHTML [XHTML] incorporé en ligne dans un élément 'ForeignObject'. (Remarque: la sémantique exacte de cette approche n'est pas complètement définie pour le moment.)

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

En tant que primitive, l'habillage de texte peut être simulé en utilisant l' dyattribut et les tspanéléments, et comme mentionné dans la spécification, certains outils peuvent automatiser cela. Par exemple, dans Inkscape, sélectionnez la forme souhaitée et le texte souhaité, puis utilisez Texte -> Flux dans le cadre. Cela vous permettra d'écrire votre texte, avec un habillage, qui s'enroulera en fonction des limites de la forme. Assurez-vous également de suivre ces instructions pour indiquer à Inkscape de maintenir la compatibilité avec SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

En outre, il existe des bibliothèques JavaScript qui peuvent être utilisées pour automatiser de manière dynamique l'habillage de texte: http://www.carto.net/papers/svg/textFlow/

Il est intéressant de noter la solution de CSVG pour envelopper une forme dans un élément de texte (par exemple, voir leur exemple "bouton"), bien qu'il soit important de mentionner que leur implémentation n'est pas utilisable dans un navigateur: http://www.csse.monash.edu .au / ~ clm / csvg / about.html

Je mentionne cela parce que j'ai développé une bibliothèque inspirée de CSVG qui vous permet de faire des choses similaires et fonctionne dans les navigateurs Web, même si je ne l'ai pas encore publiée.

jbeard4
la source
Merci pour la réponse ... mais ACK! Il semble que je devrai vider svg alors. L'une des premières choses auxquelles les développeurs auraient dû penser était de joindre du texte (formaté selon le souhait des utilisateurs) aux formes! J'attendrai qu'ils se ressaisissent avant de continuer. Je vais essayer de le redessiner dans Windows Paint et voir si cela le fera. (Si seulement je pouvais obtenir les navigateurs pour afficher le tableau correctement.)
Lady Aleena
À propos du texte modifiable en svg, Opera prend en charge l'attribut modifiable de SVG 1.2 Tiny, qui permet d'obtenir du texte modifiable en ajoutant simplement un attribut editable="true"à l'élément text ou textArea.
Erik Dahlström
2
@Erik, encore une fois Opera est en avance sur la courbe.
jbeard4
@Lady Aleena, pourquoi ne pas simplement utiliser la solution Inkscape que j'ai mentionnée? S'il s'agit simplement d'une image statique (par exemple, pas de comportement dynamique, pas de mise à jour dynamique du contenu du texte), cela devrait fonctionner correctement. De plus, il sera évolutif, ce que Paint ne fera pas, car Paint produit des graphiques raster. Enfin, je ne crois pas que Paint prend en charge l'habillage de texte à n'importe quel niveau.
jbeard4
@ echo-flow J'ai perdu toute confiance dans les programmes produisant du code il y a longtemps depuis que j'ai utilisé (et par la suite vidé) la page d'accueil de Microsoft pour générer du HTML. Je me méfie toujours des programmes qui ajoutent leur propre code propriétaire à ce que j'écris. La première page a vraiment gâché mon html. Si vous êtes un utilisateur d'Inkscape, pouvez-vous me dire si Inkscape place son propre code propriétaire dans le svg dans lequel je devrais entrer et supprimer après la création du svg?
Lady Aleena
23

Les réponses précédentes ont donné de mauvais résultats en utilisant des coins arrondis ou stroke-widthc'est> 1. Par exemple, vous vous attendez à ce que le code suivant produise un rectangle arrondi, mais les coins sont coupés par le svgcomposant parent :

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

Au lieu de cela, je recommande d'encapsuler le textdans un svg, puis d'imbriquer ce nouveau svget le rectensemble dans un gélément, comme dans l'exemple suivant:

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

Cela résout le problème d'écrêtage qui se produit dans les réponses ci-dessus. J'ai également traduit le groupe rect / texte en utilisant l' transform="translate(x,y)"attribut pour démontrer que cela fournit une approche plus intuitive pour positionner le rect / texte à l'écran.

Austin D
la source
16

Si vous créez le SVG par programmation, vous pouvez le simplifier et faire quelque chose comme ceci:

  <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
la source
3
Ne devrait-il pas être dominant-baselineet text-anchor, pas dominantBaselineet textAnchor?
Nathaniel M. Beaver
1
@ NathanielM.Beaver Oui, ça devrait l'être. Bonne prise. Le code ci-dessus est de React qui nécessite malheureusement que les attributs soient renommés en utilisant camelCase.
Brennan Cheung
13

Vous pouvez directement utiliser la text-anchor = "middle"propriété. Je conseille de créer un élément svg wrapper sur votre rectangle et votre texte. De cette façon, vous pouvez utiliser l'ensemble de l'élément en utilisant un sélecteur css. Assurez-vous de placer la propriété «x» et «y» du texte à 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>

zookastos
la source
7

Blog détaillé: 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
la source
Pas centré sur Firefox alors que d'autres solutions le sont. codepen.io/anon/pen/oEByYr
tgf
J'ai testé sur Firefox 58.0.1 (64 bits), ça marche. Pouvez-vous mentionner la version pour laquelle cela ne fonctionne pas pour vous?
Sahil Gupta
Firefox 58.0.2 64 bits. Le texte est légèrement plus haut qu'il ne devrait l'être. Cela peut ne pas être facilement perceptible au début, mais si votre boîte s'adapte très étroitement, c'est plus évident; essayez de réduire la hauteur.
tgf le
6

alignment-baselinen'est pas le bon attribut à utiliser ici. La bonne réponse est d'utiliser une combinaison de dominant-baseline="central"et 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>

Régis May
la source
2

Une façon d'insérer du texte à l'intérieur d'un rectangle est d'insérer un objet étranger, qui est un DIV, à l'intérieur d'un objet rect.

De cette façon, le texte respectera les limites du 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
la source
1

J'ai eu beaucoup de mal à obtenir quelque chose de centré en utilisant SVG, alors j'ai lancé ma propre petite fonction. j'espère que cela devrait vous aider. Notez que cela ne fonctionne que pour les éléments 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);
}
john ktejik
la source
1

Pour l'alignement horizontal et vertical du texte dans les graphiques, vous pouvez utiliser les styles CSS suivants. En particulier, notez que dominant-baseline:middlec'est probablement faux, car c'est (généralement) à mi-chemin entre le haut et la ligne de base, plutôt qu'à mi-chemin entre le haut et le bas. De plus, certaines sources (par exemple Mozilla ) utilisent à la dominant-baseline:hangingplace de dominant-baseline:text-before-edge. C'est également probablement faux, car il hangingest conçu pour les scripts indiens. Bien sûr, si vous utilisez un mélange de latin, d'indien, d'idéographes ou autre, vous aurez probablement besoin de lire la documentation .

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

Edit: Je viens de remarquer que Mozilla utilise également dominant-baseline:baselinece qui est définitivement faux: ce n'est même pas une valeur reconnue! Je suppose que c'est par défaut la police par défaut, c'est-à- alphabeticdire qu'ils ont eu de la chance.

Plus d'édition: Safari (11.1.2) comprend text-before-edgemais pas text-after-edge. Il échoue également ideographic. Super truc, Apple. Vous pourriez donc être obligé d'utiliser alphabeticet d'autoriser les descendeurs après tout. Désolé.

Adam Chalcraft
la source