Como colocar e centralizar o texto em um retângulo SVG

179

Eu tenho o seguinte retângulo ...

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

Eu gostaria de centralizar a palavra "ficção" dentro dela. Para outros retângulos, o svg word wrap fica dentro deles? Não consigo encontrar nada específico sobre a inserção de texto em formas centralizadas na horizontal e na vertical e na quebra de linha. Além disso, o texto não pode deixar o retângulo.

Examinar o exemplo http://www.w3.org/TR/SVG/text.html#TextElement não ajuda, pois os elementos x e y do elemento de texto são diferentes dos x e y do retângulo. Não parece haver largura e altura para os elementos de texto. Não tenho certeza da matemática aqui.

(Minha tabela html simplesmente não vai funcionar.)

Lady Aleena
fonte
1
você poderia mudar para a resposta usando âncora de texto e linha de base dominante? obrigado!
Neaumusic 27/08/19
1
neaumusic, feito. 8)
Lady Aleena

Respostas:

309

Uma solução fácil para centralizar o texto horizontal e verticalmente em SVG:

  1. Defina a posição do texto no centro absoluto do elemento no qual você deseja centralizá-lo:

    • Se é o pai, você poderia fazer x="50%" y ="50%".
    • Se for outro elemento, xseria o xdesse elemento + metade de sua largura (e semelhante para, ymas com a altura).
  2. Use a text-anchorpropriedade para centralizar o texto horizontalmente com o valor middle:

    meio

    Os caracteres renderizados são alinhados de forma que o meio geométrico do texto renderizado resultante esteja na posição inicial atual do texto.

  3. Use a dominant-baselinepropriedade para centralizar o texto verticalmente com o valor middle(ou dependendo da aparência que você deseja, você pode querer fazer central)

Aqui está uma demonstração simples:

<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
fonte
1
Eu tenho procurado em todos os lugares para uma solução tão simples - nada mais estava trabalhando para mim que eu usei isso para centralizar um número dentro de uma barra de progresso radial
anthonytherockjohnson
7
No meu caso, é a dominant-baselinepropriedade que faz o trabalho, não o alignment-baseline.
Pintoch
1
Se você estiver indo para fazer isso regularmente, você também pode adicionar o seguinte: <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Obrigado pela solução.
Manngo 5/08/19
1
Execute seu próprio snippet de código. Isso não funciona. Confira: ele ainda não funciona na visualização fornecida pelo próprio stackoverflow. Como afirmado anteriormente no comentário, dominant-baselinerealmente precisa ser usado. Sinto muito, mas esta resposta é de alguma forma enganosa. Como ele não parece funcionar no Firefox nem no Chromium, além disso, você deve estudar os comentários para encontrar a solução correta por conta própria. Tomei a liberdade de adicionar outra resposta com o código que funciona conforme o esperado. (Para os registros: testado com FF e Chromium no Ubuntu Linux).
Regis pode ser
2
@RegisMay obrigado por apontar. Estou no Chrome para Mac e ele funcionou bem com alignment-baseline, mas parece que ele pode ser uma coisa peculiar porque revendo as especificações, deve ser dominant-baselinede text, e alignment-baselinepara tspan, tref, altGlyphe textPath. Eu atualizei a resposta de acordo.
Alvaro Montoro
38

SVG 1.2 Minúsculo pacote de texto adicionado, mas a maioria das implementações do SVG que você encontrará no navegador (com exceção do Opera) não implementaram esse recurso. Normalmente, você é o desenvolvedor, para posicionar o texto manualmente.

A especificação SVG 1.1 fornece uma boa visão geral dessa limitação e as possíveis soluções para superá-la:

Cada elemento 'texto' faz com que uma única sequência de texto seja renderizada. O SVG não executa quebra automática de linha ou quebra de linha. Para obter o efeito de várias linhas de texto, use um dos seguintes métodos:

  • O autor ou o pacote de autoria precisa pré-calcular as quebras de linha e usar vários elementos de 'texto' (um para cada linha de texto).
  • O autor ou o pacote de autoria precisa pré-calcular as quebras de linha e usar um único elemento 'text' com um ou mais elementos filho 'tspan' com valores apropriados para os atributos 'x', 'y', 'dx' e 'dy' para definir novas posições iniciais para os caracteres que iniciam novas linhas. (Essa abordagem permite a seleção de texto do usuário em várias linhas de texto - consulte Seleção de texto e operações da área de transferência.)
  • Expresse o texto a ser renderizado em outro espaço de nome XML, como XHTML [XHTML] incorporado embutido em um elemento 'ForeignObject'. (Nota: a semântica exata dessa abordagem não está completamente definida no momento.)

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

Como primitivo, o agrupamento de texto pode ser simulado usando o dyatributo e os tspanelementos e, como mencionado na especificação, algumas ferramentas podem automatizar isso. Por exemplo, no Inkscape, selecione a forma desejada e o texto desejado e use Texto -> Fluir para o quadro. Isso permitirá que você escreva seu texto, com quebra automática, que será quebrada com base nos limites da forma. Além disso, siga estas instruções para instruir o Inkscape a manter a compatibilidade com o SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

Além disso, existem algumas bibliotecas JavaScript que podem ser usadas para automatizar dinamicamente a quebra de texto: http://www.carto.net/papers/svg/textFlow/

É interessante observar a solução do CSVG para agrupar uma forma em um elemento de texto (por exemplo, veja o exemplo "botão"), embora seja importante mencionar que a implementação não é utilizável em um navegador: http://www.csse.monash.edu .au / ~ clm / csvg / about.html

Estou mencionando isso porque desenvolvi uma biblioteca inspirada em CSVG que permite fazer coisas semelhantes e funciona em navegadores da web, embora ainda não a tenha lançado.

jbeard4
fonte
Obrigado pela resposta ... mas ACK! Parece que terei que despejar svg então. Uma das primeiras coisas que os desenvolvedores deveriam ter pensado foi anexar texto (formatado como os usuários desejam) às formas! Vou esperar até que eles ajam juntos antes que eu faça mais com isso. Vou tentar redesenhá-lo no Windows Paint e ver se isso vai acontecer. (Se eu pudesse obter os navegadores para exibir a tabela corretamente.)
Lady Aleena
Sobre o texto editável no svg, o Opera suporta o atributo editável do SVG 1.2 Tiny, que torna possível obter texto editável simplesmente adicionando um atributo editable="true"ao elemento text ou textArea.
Erik Dahlström
2
@Erik, mais uma vez o Opera está à frente da curva.
precisa saber é o seguinte
@ Lady Aleena, por que não usar apenas a solução Inkscape que eu mencionei? Se for apenas uma imagem estática (por exemplo, sem comportamento dinâmico, sem atualização dinâmica do conteúdo do texto), isso deve funcionar bem. Além disso, será escalável, o que o Paint não fará, pois o Paint produz gráficos rasterizados. Por fim, não acredito que o Paint suporte a quebra de texto em nenhum nível.
precisa saber é o seguinte
@ echo-flow Eu perdi toda a confiança nos programas de produção de código há muito tempo, desde que usei (e depois despejo) o Microsoft Front Page para gerar html. Sempre sou cauteloso com os programas que adicionam seu próprio código proprietário ao que estou escrevendo. Front Page realmente bagunçou meu html. Se você é um usuário do Inkscape, pode me dizer se o Inkscape coloca seu próprio código proprietário no svg no qual eu precisaria entrar e remover após a criação do svg?
quer
23

As respostas anteriores deram maus resultados ao usar cantos arredondados ou stroke-widthé> 1. Por exemplo, você esperaria que o código a seguir produzisse um retângulo arredondado, mas os cantos são cortados pelo svgcomponente pai :

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

Em vez disso, recomendo agrupar o textem um svge aninhar o novo svge o rectjunto dentro de um gelemento, como no exemplo a seguir:

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

Isso corrige o problema de recorte que ocorre nas respostas acima. Também traduzi o grupo rect / text usando o transform="translate(x,y)"atributo para demonstrar que isso fornece uma abordagem mais intuitiva para posicionar o rect / text na tela.

Austin D
fonte
16

Se você estiver criando o SVG programaticamente, poderá simplificá-lo e fazer algo assim:

  <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
fonte
3
Não deveria ser dominant-baselinee text-anchor, não dominantBaselinee textAnchor?
Nathaniel M. Beaver
1
@ NathanielM.Beaver Sim, deveria ser. Boa pegada. O código acima é do React, que infelizmente exige que os atributos sejam renomeados usando camelCase.
Brennan Cheung
13

Você pode usar diretamente a text-anchor = "middle"propriedade Aconselho a criar um elemento svg do wrapper sobre seu retângulo e texto. Dessa forma, você pode usar o elemento inteiro usando um seletor de css. Coloque as propriedades 'x' e 'y' do 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
fonte
7

Blog completo: 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
fonte
Não está centrado no firefox enquanto outras soluções estão. codepen.io/anon/pen/oEByYr
TGF
Eu testei no Firefox 58.0.1 (64 bits), está funcionando. Você pode mencionar a versão para a qual não está funcionando?
Sahil Gupta
Firefox 58.0.2 64bit. O texto é um pouco mais alto do que deveria. Pode não ser facilmente perceptível a princípio, mas se sua caixa se encaixar muito bem, é mais óbvio; tente reduzir a altura.
tgf 21/02
6

alignment-baselinenão é o atributo certo para usar aqui. A resposta correta é usar uma combinação de dominant-baseline="central"e 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
fonte
2

Uma maneira de inserir texto dentro de um retângulo é inserir um objeto estranho, que é um DIV, dentro de um objeto retângulo.

Dessa forma, o texto respeitará os limites do 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
fonte
1

Eu tive um tempo para conseguir algo centralizado usando SVG, então rolei minha própria função. espero que ajude você. Observe que ele funciona apenas 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
fonte
1

Para alinhamento horizontal e vertical de texto em gráficos, convém usar os seguintes estilos CSS. Em particular, observe que dominant-baseline:middleprovavelmente está errado, já que este é (geralmente) a meio caminho entre a parte superior e a linha de base, e não a meio caminho entre a parte superior e a inferior. Além disso, algumas fontes (por exemplo, Mozilla ) usam em dominant-baseline:hangingvez de dominant-baseline:text-before-edge. Provavelmente, isso também está errado, pois hangingfoi projetado para scripts Indic. Obviamente, se você estiver usando uma mistura de latim, índico, ideógrafo ou qualquer outra coisa, provavelmente precisará ler a documentação .

/* 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: Acabei de notar que o Mozilla também usa o dominant-baseline:baselineque está definitivamente errado: nem sequer é um valor reconhecido! Eu suponho que seja o padrão para a fonte padrão, que éalphabetic que eles tiveram sorte.

Mais edição: o Safari (11.1.2) entende, text-before-edgemas não text-after-edge. Também falha ideographic. Ótimas coisas, Apple. Portanto, você pode ser forçado a usar alphabetice permitir descendentes, afinal. Desculpe.

Adam Chalcraft
fonte