web-dev-qa-db-fra.com

Comment placer et centrer du texte dans un rectangle SVG

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, est-ce que svg Word va rester en eux? Je n'arrive pas à trouver quoi que ce soit à propos de l'insertion de texte dans des formes centrées horizontalement et verticalement, ainsi que le retour à la ligne. En outre, le texte ne peut pas quitter le rectangle.

L'exemple http://www.w3.org/TR/SVG/text.html#TextElement n'aide en rien, car les éléments x et y de l'élément de texte sont différents de ceux 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 va tout simplement pas au travail.)

110
Lady Aleena

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

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

    • Si c'est le parent, vous pouvez simplement faire x="50%" y ="50%".
    • S'il s'agit d'un autre élément, x serait le x de cet élément + la moitié de sa largeur (et similaire pour y mais avec la hauteur).
  2. Utilisez le text-anchor proprié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 actuel.

  3. Utilisez le dominant-baseline propriété pour centrer le texte verticalement avec la valeur middle (ou selon votre apparence, vous voudrez peut-être le 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>
191
Alvaro Montoro

SVG 1.2 Minuscule ajout d'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 fournit un bon aperçu de cette limitation et des solutions possibles pour la surmonter:

Chaque élément "text" provoque le rendu d’une seule chaîne de texte. SVG n'effectue pas de saut de ligne automatique ni de retour à la ligne. Pour obtenir l'effet de plusieurs lignes de texte, appliquez l'une des méthodes suivantes:

  • L’auteur ou le logiciel 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 les valeurs appropriées pour les attributs 'x', 'y', 'dx' et 'dy' 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 dans le Presse-papiers.)
  • Exprimez le texte à restituer 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é à l'aide des éléments dy et tspan. Comme indiqué dans les spécifications, certains outils peuvent automatiser cette opération. Par exemple, dans Inkscape, sélectionnez la forme souhaitée et le texte souhaité, puis utilisez Text -> Flow into Frame. Cela vous permettra d’écrire votre texte, avec un wrapping, qui sera basé sur les limites de la forme. Assurez-vous également de suivre les instructions ci-dessous 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, certaines bibliothèques JavaScript peuvent être utilisées pour automatiser de manière dynamique le retour à la ligne: http://www.carto.net/papers/svg/textFlow/

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

Je le mentionne 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.

37
jbeard4

Les réponses précédentes donnaient de mauvais résultats en utilisant des angles arrondis ou stroke-width C'est> 1. Par exemple, vous vous attendez à ce que le code suivant produise un rectangle arrondi, mais les angles sont découpés par le composant parent svg:

<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 de placer text dans un svg, puis d'imbriquer ce nouveau svg et le rect dans un élément g, 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 corrige le problème de coupure qui se produit dans les réponses ci-dessus. J'ai également traduit le groupe rect/text à l'aide de l'attribut transform="translate(x,y)" afin de démontrer qu'il offre une approche plus intuitive du positionnement du rect/texte à l'écran.

19
Austin D

Vous pouvez directement utiliser text-anchor = "middle" propriété. Je vous conseille de créer un élément wrapper svg sur votre rectangle et votre texte. De cette façon, vous pouvez utiliser tout l'élément à l'aide d'un sélecteur css. Assurez-vous de placer les propriétés '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>
10
Nalin Agrawal

Si vous créez le SVG par programme, 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}
          dominantBaseline="middle"
          textAnchor="middle"
      >
          {label}
      </text>
  </g>
6
Brennan Cheung

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>
5
Sahil Gupta

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

De cette façon, le texte respectera les limites de la 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>
2
Bruno Bastos

alignment-baseline n'est pas le bon attribut à utiliser ici. La bonne réponse consiste à 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>
2
Regis May

Pour l'alignement horizontal et vertical du texte dans les graphiques, vous pouvez utiliser les styles CSS suivants. En particulier, notez que dominant-baseline:middle est probablement faux, car il se situe (généralement) à mi-chemin entre le haut et la base, et non à mi-chemin entre le haut et le bas. De plus, certaines sources (par exemple Mozilla ) utilisent dominant-baseline:hanging au lieu de dominant-baseline:text-before-Edge. Cela est probablement également faux, car hanging est conçu pour les scripts Indic. Bien sûr, si vous utilisez un mélange de latin, d'indic, d'idéogrammes 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:baseline ce qui est vraiment faux: ce n'est même pas une valeur reconnue! Je suppose que la valeur par défaut de la police, alphabetic, est utilisée, alors ils ont eu de la chance.

Plus edit: Safari (11.1.2) comprend text-before-Edge mais non text-after-Edge. Il échoue également sur ideographic. Super truc, Apple. Vous pourriez donc être forcé d'utiliser alphabetic et de permettre les descendeurs après tout. Pardon.

1
Adam Chalcraft

J'ai eu un sacré bout de temps à centrer quelque chose avec 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);
}
1
john ktejik