Cómo colocar y centrar texto en un rectángulo SVG

179

Tengo el siguiente rectángulo ...

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

Me gustaría centrar la palabra "Ficción" dentro de ella. Para otros rectángulos, ¿la palabra svg se ajusta para permanecer dentro de ellos? Parece que no puedo encontrar nada específicamente sobre insertar texto dentro de formas que estén centradas tanto horizontal como verticalmente y con ajuste de texto. Además, el texto no puede salir del rectángulo.

Mirar el http://www.w3.org/TR/SVG/text.html#TextElement ejemplo no ayuda ya que los elementos de texto x e y son diferentes de los rectángulo x e y. No parece haber ancho y alto para los elementos de texto. No estoy seguro de las matemáticas aquí.

(Mi tabla html simplemente no va a funcionar).

Lady aleena
fuente
1
¿podría cambiar a la respuesta usando text-anchor y dominante-baseline? ¡Gracias!
neaumusic
1
Neaumusic, hecho. 8)
Lady Aleena

Respuestas:

309

Una solución fácil para centrar texto horizontal y verticalmente en SVG:

  1. Establezca la posición del texto en el centro absoluto del elemento en el que desea centrarlo:

    • Si es el padre, podrías hacerlo x="50%" y ="50%".
    • Si es otro elemento, xsería el xde ese elemento + la mitad de su ancho (y similar para ypero con la altura).
  2. Use la text-anchorpropiedad para centrar el texto horizontalmente con el valor middle:

    medio

    Los caracteres representados se alinean de modo que el centro geométrico del texto procesado resultante se encuentre en la posición inicial del texto actual.

  3. Use la dominant-baselinepropiedad para centrar el texto verticalmente con el valor middle(o dependiendo de cómo desee que se vea, es posible que desee hacerlo central)

Aquí hay una demostración 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
fuente
1
He estado buscando por todas partes para una solución de este sencillo - nada más estaba trabajando para mí He utilizado este para centrar un número dentro de una barra de progreso radial
anthonytherockjohnson
77
En mi caso, es la dominant-baselinepropiedad la que hace el trabajo, no la alignment-baseline.
pintoch
1
Si vas a hacer esto regularmente, también se puede añadir la siguiente: <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Gracias por la solucion.
Manngo
1
Ejecute su propio fragmento de código. No funciona Compruébalo: incluso no funciona en la vista previa proporcionada por stackoverflow. Como se indicó anteriormente en el comentario, de dominant-baselinehecho, debe ser utilizado. Lo siento, pero esta respuesta es de alguna manera engañosa. Como no parece funcionar en Firefox ni en Chromium y además debe estudiar los comentarios para encontrar la solución correcta por su cuenta, me tomé la libertad de agregar otra respuesta con el código que funciona como se esperaba. (Para los registros: Probado con FF y Chromium en Ubuntu Linux.)
Regis
2
@RegisMay gracias por señalarlo. Estoy en Chrome en Mac y funcionó bien con alignment-baseline, pero parece que puede ser una cosa peculiar debido a la revisión de las especificaciones, debe ser dominant-baselinede text, y alignment-baselinepara tspan, tref, altGlyph, y textPath. Actualicé la respuesta en consecuencia.
Alvaro Montoro
38

SVG 1.2 Tiny agregó un ajuste de texto, pero la mayoría de las implementaciones de SVG que encontrará en el navegador (con la excepción de Opera) no han implementado esta función. Normalmente depende de usted, el desarrollador, posicionar el texto manualmente.

La especificación SVG 1.1 proporciona una buena visión general de esta limitación y las posibles soluciones para superarla:

Cada elemento 'texto' hace que se represente una sola cadena de texto. SVG no realiza saltos de línea automáticos ni ajuste de palabras. Para lograr el efecto de varias líneas de texto, use uno de los siguientes métodos:

  • El autor o paquete de autoría necesita calcular previamente los saltos de línea y utilizar múltiples elementos de "texto" (uno para cada línea de texto).
  • El autor o paquete de autoría necesita calcular previamente los saltos de línea y usar un solo elemento 'texto' con uno o más elementos secundarios 'tspan' con valores apropiados para los atributos 'x', 'y', 'dx' y 'dy' para establecer nuevas posiciones de inicio para aquellos personajes que comienzan nuevas líneas. (Este enfoque permite la selección de texto del usuario en varias líneas de texto; consulte Selección de texto y operaciones del portapapeles).
  • Exprese el texto para que se represente en otro espacio de nombres XML, como XHTML [XHTML] incrustado en línea dentro de un elemento 'foreignObject'. (Nota: la semántica exacta de este enfoque no está completamente definida en este momento).

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

Como primitivo, el ajuste de texto se puede simular usando el dyatributo y los tspanelementos, y como se menciona en la especificación, algunas herramientas pueden automatizar esto. Por ejemplo, en Inkscape, seleccione la forma que desee y el texto que desee, y use Texto -> Flujo en marco. Esto le permitirá escribir su texto, con ajuste, que se ajustará según los límites de la forma. Además, asegúrese de seguir estas instrucciones para decirle a Inkscape que mantenga la compatibilidad con SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

Además, hay algunas bibliotecas de JavaScript que se pueden usar para automatizar dinámicamente el ajuste de texto: http://www.carto.net/papers/svg/textFlow/

Es interesante observar la solución de CSVG para ajustar una forma a un elemento de texto (por ejemplo, ver su ejemplo de "botón"), aunque es importante mencionar que su implementación no se puede usar en un navegador: http://www.csse.monash.edu .au / ~ clm / csvg / about.html

Menciono esto porque he desarrollado una biblioteca inspirada en CSVG que le permite hacer cosas similares y funciona en navegadores web, aunque aún no la he lanzado.

jbeard4
fuente
Gracias por la respuesta ... pero ACK! Parece que tendré que volcar svg entonces. ¡Una de las primeras cosas en las que los desarrolladores deberían haber pensado fue adjuntar texto (formateado como lo deseen los usuarios) a las formas! Esperaré hasta que actúen juntos antes de seguir con esto. Intentaré volver a dibujarlo en Windows Paint y ver si eso lo hará. (Ojalá pudiera hacer que los navegadores muestren la tabla correctamente.)
Lady Aleena
Acerca del texto editable en svg, Opera admite el atributo editable de SVG 1.2 Tiny, lo que hace posible obtener texto editable simplemente agregando un atributo editable="true"al texto o al elemento textArea.
Erik Dahlström
2
@Erik, una vez más, Opera está por delante de la curva.
jbeard4
@Lady Aleena, ¿por qué no usar la solución Inkscape que mentiné? Si es solo una imagen estática (por ejemplo, sin comportamiento dinámico, sin actualizar el contenido de texto dinámicamente), entonces esto debería funcionar bien. Además, será escalable, lo que Paint no será, ya que Paint produce gráficos de trama. Finalmente, no creo que Paint admita el ajuste de texto en ningún nivel.
jbeard4
@ echo-flow Perdí toda confianza en los programas de producción de código hace mucho tiempo desde que utilicé (y posteriormente abandoné) Microsoft Front Page para generar html. Siempre desconfío de los programas que agregan su propio código propietario a lo que estoy escribiendo. Front Page realmente estropeó mi html. Si usted es un usuario de Inkscape, ¿puede decirme si Inkscape coloca su propio código propietario en el svg en el que tendría que entrar y retirarme después de la creación del svg?
Lady Aleena el
23

Las respuestas anteriores dieron malos resultados al usar esquinas redondeadas o stroke-widtheso es> 1. Por ejemplo, esperaría que el siguiente código produzca un rectángulo redondeado, pero el svgcomponente principal recorta las esquinas :

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

En cambio, recomiendo envolver el texten svgay luego anidar ese nuevo svgy el rectdentro de un gelemento, como en el siguiente ejemplo:

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

Esto soluciona el problema de recorte que se produce en las respuestas anteriores. También traduje el grupo rect / text usando el transform="translate(x,y)"atributo para demostrar que esto proporciona un enfoque más intuitivo para posicionar el rect / text en la pantalla.

Austin D
fuente
16

Si está creando el SVG mediante programación, puede simplificarlo y hacer algo como esto:

  <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
fuente
3
¿No debería ser dominant-baseliney text-anchor, no dominantBaseliney textAnchor?
Nathaniel M. Beaver
1
@ NathanielM.Beaver Sí, debería serlo. Buena atrapada. El código anterior es de React, que desafortunadamente requiere que los atributos sean renombrados usando camelCase.
Brennan Cheung
13

Puede usar directamente la text-anchor = "middle"propiedad. Aconsejo crear un elemento de envoltura svg sobre su rectángulo y texto. De esa manera puede usar todo el elemento usando un selector css. Asegúrese de colocar la propiedad 'x' e 'y' del texto como 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
fuente
7

Blog de detalles completos: 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
fuente
No está centrado en Firefox mientras que otras soluciones sí. codepen.io/anon/pen/oEByYr
tgf
Probé en Firefox 58.0.1 (64 bits), está funcionando. ¿Puedes mencionar la versión para la que no te está funcionando?
Sahil Gupta
Firefox 58.0.2 64 bits. El texto es ligeramente más alto de lo que debería ser. Puede que al principio no se note fácilmente, pero si su caja encaja muy de cerca es más obvio; Intenta reducir la altura.
tgf
6

alignment-baselineno es el atributo correcto para usar aquí. La respuesta correcta es usar una combinación de dominant-baseline="central"y 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
fuente
2

Una forma de insertar texto dentro de un rectángulo es insertar un objeto extraño, que es un DIV, dentro de un objeto rectángulo.

De esta manera, el texto reflejará los límites 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>

Bruno Bastos
fuente
1

Me costó mucho centrarme en SVG, así que desarrollé mi propia pequeña función. espero que te ayude. Tenga en cuenta que solo funciona para elementos 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
fuente
1

Para la alineación horizontal y vertical del texto en gráficos, es posible que desee utilizar los siguientes estilos CSS. En particular, tenga en cuenta que dominant-baseline:middleprobablemente sea incorrecto, ya que esto está (generalmente) a medio camino entre la parte superior y la línea de base, en lugar de a medio camino entre la parte superior y la parte inferior. Además, algunas fuentes (por ejemplo, Mozilla ) usan en dominant-baseline:hanginglugar de dominant-baseline:text-before-edge. Esto probablemente también sea incorrecto, ya que hangingestá diseñado para scripts índicos. Por supuesto, si está utilizando una mezcla de latín, índico, ideógrafos o lo que sea, probablemente necesitará leer la documentación .

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

Editar: Acabo de notar que Mozilla también usa lo dominant-baseline:baselineque definitivamente está mal: ¡ni siquiera es un valor reconocido! Supongo que está predeterminado en la fuente predeterminada, que es alphabetic, por lo que tuvieron suerte.

Más edición: Safari (11.1.2) entiende text-before-edgepero no text-after-edge. También falla en ideographic. Grandes cosas, Apple. Por lo tanto, podría verse obligado a usar alphabeticy permitir descendientes después de todo. Lo siento.

Adam Chalcraft
fuente