82

I'm trying to center text in a circle with SVG. The size of the text will be dynamic.

My code on Plunker:

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     xml:space="preserve"
     style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
     viewBox="0 0 500 500">

  <g id="UrTavla">
      <circle style="fill:url(#toning);stroke:#010101;stroke-width:1.6871;stroke-miterlimit:10;" cx="250" cy="250" r="245">
      
      </circle>
      <text x="50%" y="50%" stroke="#51c5cf" stroke-width="2px" dy=".3em"> Look, I’m centered!Look, I’m centered! </text>
  </g>
</svg>
bad_coder
  • 11,289
  • 20
  • 44
  • 72
אVי
  • 1,913
  • 6
  • 22
  • 35

6 Answers6

102

Add text-anchor="middle" to the text element.

Plunker

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 500 500">
  <g id="UrTavla">
    <circle style="fill:url(#toning);stroke:#010101;stroke-width:1.6871;stroke-miterlimit:10;" cx="250" cy="250" r="245">
    </circle>
    <text x="50%" y="50%" text-anchor="middle" stroke="#51c5cf" stroke-width="2px" dy=".3em">Look, I’m centered!Look, I’m centered!</text>
  </g>
</svg>
Kees de Kooter
  • 7,078
  • 5
  • 38
  • 45
Weafs.py
  • 22,731
  • 9
  • 56
  • 78
  • 10
    what if their circle were not centered in the whole svg? then how would you center the text in the circle? e.g. http://plnkr.co/edit/bFeiKl5B67rjwhM028os?p=preview – Michael Jul 13 '17 at 03:06
  • 2
    @Michael In that case I would use JavaScript to centre the text – Weafs.py Jul 13 '17 at 05:51
  • 7
    Works good! But why the magic number .3em for dy not other value? – Leo Gao Sep 26 '17 at 04:15
  • 1
    @Michael use the circle's radius and current position. i.e: current cx + radius = x value of text – Kevin Cohen Oct 19 '17 at 18:02
  • 15
    This should not be the accepted answer because it depends on the circle being centered itself, and in addition it centers the text vertically using a y-offset that depends of the font size. Instead the `x` and `y` properties of the `text` element should be equal to the `cx` and `cy` properties of the `circle` element, and the properties `textAnchor='middle'` and `alignmentBaseline='central'` should be added to the `text` element. See the alternative answers below for details. – Nikolaj Nov 08 '20 at 13:25
  • 1
    Downvote reason: Answer is wrong and doesn't work. It's centering the text on the entire container not on the circle. – NickG Jul 19 '21 at 11:25
50

The solution proposed and accepted is INVALID when you want to draw a circle that is not centered on container !

Using x="50%" y="50%" on text tag works only when SVG element contains a circle that is centered on viewPort.

If you want to draw 3 circles, you must also change (x,y) text coordinates so that they are equal to (cx,cy) circle coordinates has done in following example

As proposed by random, I have added alignment-baseline="middle" for first circle in code snippet only so you can see that for Label text is now perfectly aligned (vertically).

<svg height="350" width="350">
    <circle cx="110" cy="110" r="100"
            stroke="red"
            stroke-width="3"
            fill="none"
            />
    <text x="110" y="110" 
          text-anchor="middle"
          stroke="red"
          stroke-width="1px"
          alignment-baseline="middle"
          > Label
    </text>
    <circle cx="240" cy="110" r="100" 
            stroke="blue" 
            stroke-width="3"
            fill="none"
            />
    <text x="240" y="110" 
          text-anchor="middle" 
          stroke="blue" 
          stroke-width="1px"
          > Ticket
    </text>
    <circle cx="170" cy="240" r="100" 
            stroke="green" 
            stroke-width="3" 
            fill="none"
            />
    <text x="170" y="240" 
          text-anchor="middle" 
          stroke="green" 
          stroke-width="1px"
          > Vecto
    </text>
</svg>

Just for the fun, I have put code with 3 circles to select each parts. Just click on it !

    function setReadableCode()
        {
        var circLabel = document.getElementById('circLabel');
        var circTicket = document.getElementById('circTicket');
        var circVecto = document.getElementById('circVecto');
        var interLabelTicket = document.getElementById('interLabelTicket');
        var interTicketVecto = document.getElementById('interTicketVecto');
        var interVectoLabel = document.getElementById('interVectoLabel');
        var interLabelTicketVecto = document.getElementById('interLabelTicketVecto');
        }

    function clickCircle(sCircle, sInter2a, sInter2b, sInter3)
        {
        var circ = document.getElementById(sCircle);
        var inter2a = document.getElementById(sInter2a);
        var inter2b = document.getElementById(sInter2b);
        var inter3 = document.getElementById(sInter3);

        var sColor = '';

        if (circ.style.fill == '' || circ.style.fill == 'white')
            {
            sColor = 'yellow';
            }
        else
            {
            sColor = 'white';
            }

        circ.style.fill = sColor;
        inter2a.style.fill = sColor;
        inter2b.style.fill = sColor;
        inter3.style.fill = sColor;

        setReadableCode();
        }

                    function clickCircLabel() {
                        clickCircle('circLabel', 'interLabelTicket', 'interVectoLabel', 'interLabelTicketVecto');
                    }

                    function clickCircTicket() {
                        clickCircle('circTicket', 'interLabelTicket', 'interTicketVecto', 'interLabelTicketVecto');
                    }

                    function clickCircVecto() {
                        clickCircle('circVecto', 'interVectoLabel', 'interTicketVecto', 'interLabelTicketVecto');
                    }

                    function clickIntersection2(sInter2, sInter3) {
                        var inter2 = document.getElementById(sInter2);
                        var inter3 = document.getElementById(sInter3);
                        var sColor = '';

                        if (inter2.style.fill == '' || inter2.style.fill == 'white') {
                            sColor = 'yellow';
                        }
                        else {
                            sColor = 'white';
                        }

                        inter2.style.fill = sColor;
                        inter3.style.fill = sColor;

                        setReadableCode();
                    }

                    function clickInterLabelTicket() {
                        clickIntersection2('interLabelTicket', 'interLabelTicketVecto');
                    }

                    function clickInterTicketVecto() {
                        clickIntersection2('interTicketVecto', 'interLabelTicketVecto');
                    }

                    function clickInterVectoLabel() {
                        clickIntersection2('interVectoLabel', 'interLabelTicketVecto');
                    }

                    function clickInterLabelTicketVecto() {
                        var inter = document.getElementById('interLabelTicketVecto');
                        var sColor = '';

                        if (inter.style.fill == '' || inter.style.fill == 'white') {
                            sColor = 'yellow';
                        }
                        else {
                            sColor = 'white';
                        }

                        inter.style.fill = sColor;
                        setReadableCode();
                    }
text 
    {
    font-family:Arial;
    }
<svg height="350" width="350">
<circle id="circLabel" cx="110" cy="110" r="100" stroke="red" stroke-width="0" fill="white" onclick="clickCircLabel();"/>
<text x="60" y="110" text-anchor="middle" stroke="red" stroke-width="1px" onclick="clickCircLabel();">Label</text>
<circle id="circTicket" cx="210" cy="110" r="100" stroke="blue" stroke-width="0" fill="yellow" onclick="clickCircTicket();"/>
<text x="260" y="110" text-anchor="middle" stroke="blue" stroke-width="1px" onclick="clickCircTicket();">Ticket</text>
<circle id="circVecto" cx="160" cy="196.602541" r="100" stroke="green" stroke-width="0" fill="white" onclick="clickCircVecto();" />
<text x="160" y="240" text-anchor="middle" stroke="green" stroke-width="1px" onclick="clickCircVecto();">Vecto</text>
<path id="interLabelTicket"
      d="M 160 23.397460 a100,100 0 0,0 0,173.205081 a100,100 0 0,0 0,-173.205081 z"
      fill="white"
      stroke-width="3"
      onclick="clickInterLabelTicket();"
      />

<path id="interVectoLabel"
      d="M 60 196.602541 a100,100 0 0,0 150,-86.602540 a100,100 0 0,0 -150,86.602540 z"
      fill="white"
      stroke-width="3"
      onclick="clickInterVectoLabel();"
      />

<path id="interTicketVecto"
      d="M 260 196.602541 a100,100 0 0,0 -150,-86.602540 a100,100 0 0,0 150,86.602540 z"
      fill="white"
      stroke-width="3"
      onclick="clickInterTicketVecto();"
      />

<path id="interLabelTicketVecto"
      d="M 110 110 a100,100 0 0,1 100,0 a100,100 0 0,1 -50,86.602540 a100,100 0 0,1 -50,-86.602540 z"
      fill="none"
      stroke-width="3"
      onclick="clickInterLabelTicketVecto();"
      />

<circle cx="110" cy="110" r="100" stroke="red" stroke-width="3" fill="none" />
<circle cx="210" cy="110" r="100" stroke="blue" stroke-width="3" fill="none" />
<circle cx="160" cy="196.602541" r="100" stroke="green" stroke-width="3" fill="none"/>

</svg>
schlebe
  • 3,387
  • 5
  • 37
  • 50
  • 3
    It works for large circles, but what we don't see is that the text is not well vertically centered. You have to add `alignment-baseline="middle"` or `dominant-baseline="middle"`, as other answers say – Random Feb 09 '21 at 10:40
  • 1
    @random: thanks. I have added your proposal in code snipped for first circle with some comments in answer. – schlebe Feb 10 '21 at 09:17
21

Maybe, could be useful also alignment-baseline="middle", with text-anchor="middle":

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" viewBox="0 0 500 500">

  <g id="UrTavla">
      <circle style="fill:url(#toning);stroke:#010101;stroke-width:1.6871;stroke-miterlimit:10;" cx="250" cy="250" r="245" /> <!--self-closing tag-->

      <text x="50%" y="50%" stroke="#51c5cf" stroke-width="2px" dy=".3em" text-anchor="middle" alignment-baseline="middle"> Look, I’m centered!Look, I’m centered! </text>
  </g>
</svg>

Here's a good resource: http://apike.ca/prog_svg_text_style.html

bad_coder
  • 11,289
  • 20
  • 44
  • 72
Riccardo Volpe
  • 1,471
  • 1
  • 16
  • 30
  • 11
    If you use `alignment-baseline="central"`, you don't need the `dy` attribute – 4thex Jun 27 '18 at 03:19
  • 7
    And if you use `dominant-baseline="central"` instead of `alignment-baseline` it will also work in Firefox. (https://stackoverflow.com/a/21373135/3389196) – user3389196 Jun 29 '19 at 11:41
10

A simpler solution that works with non-centered circles is to put circles and texts inside translated groups.

That way you don't need to repeat the coordinates on the text.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Centered texts</title>
  </head>

  <body ng-controller="MainCtrl">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
      <g transform="translate(300, 300)" >
        <circle fill="none" stroke="black" stroke-width="1px" r="120"/>
        <text stroke="blue" stroke-width="1px" text-anchor="middle" alignment-baseline="central">Look, I’m centered!</text>
      </g>
      
      <g transform="translate(150, 150)" >
        <circle fill="none" stroke="black" stroke-width="1px" r="120"/>
        <text stroke="blue" stroke-width="1px" text-anchor="middle" alignment-baseline="central">Look, I’m also centered!</text>
      </g>
    </svg>
  </body>
</html>
Vinicius
  • 1,060
  • 1
  • 12
  • 21
8

The behaviors are not consistent across browsers using alignment-baseline="central". Notably, Chrome will position correctly but Firefox will not. If you use dominant-baseline="central" it will appear correctly in both.

FireFox:

Chrome:

svg { height:140px }
<h4>alignment-baseline AND dominant-baseline settings:</h4>
<svg viewBox="0 0 48 48">
    <circle stroke="darkgray" stroke-width="1px" 
            fill="lightgray"  cx="50%" cy="50%" r="48%"/>
    <line x1="0" y1="50%" x2="100%" y2="50%" stroke="blue" stroke-width="1"/>
    <text text-anchor="middle" alignment-baseline="central" x="50%" y="50%">central</text> 
</svg>
<svg viewBox="0 0 48 48">
    <circle stroke="darkgray" stroke-width="1px" 
            fill="lightgray"  cx="50%" cy="50%" r="48%"/>
    <line x1="0" y1="50%" x2="100%" y2="50%" stroke="blue" stroke-width="1"/>
    <text text-anchor="middle" alignment-baseline="middle" x="50%" y="50%">middle</text> 
</svg>
<svg viewBox="0 0 48 48">
    <circle stroke="darkgray" stroke-width="1px" 
            fill="lightgray"  cx="50%" cy="50%" r="48%"/>
    <line x1="0" y1="50%" x2="100%" y2="50%" stroke="blue" stroke-width="1"/>
    <text text-anchor="middle" dominant-baseline="central" x="50%" y="50%">central</text> 
</svg>
<svg viewBox="0 0 48 48">
    <circle stroke="darkgray" stroke-width="1px" 
            fill="lightgray"  cx="50%" cy="50%" r="48%"/>
    <line x1="0" y1="50%" x2="100%" y2="50%" stroke="blue" stroke-width="1"/>
    <text text-anchor="middle" dominant-baseline="middle" x="50%" y="50%">middle</text> 
</svg>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
smoore4
  • 4,520
  • 3
  • 36
  • 55
-1

it is super easy to make text center in SVG circle.

<svg height="300" width="300">
      <circle cx="120" cy="120" r="30%" fill="#117da9" />
      <text x="50" y="120" fill="white">Center Text in SVG Circle</text>
</svg>

All you need to do is change <text> tag x and y values until the given text align in the center of the circle. For example, here x and y values are x="50" y="120"

supritha
  • 1
  • 2
  • Nope. The text is not centred. Try it with just one word. You're making the huge assumption that the text never changes and therefore you can hardcode the x and y coordinates. – NickG Jul 19 '21 at 11:31