Jak umieścić i wyśrodkować tekst w prostokącie SVG

179

Mam następujący prostokąt ...

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

Chciałbym umieścić w środku słowo „fikcja”. Czy w przypadku innych prostokątów zawijanie słów svg pozostaje w nich? Nie mogę znaleźć nic konkretnego na temat wstawiania tekstu do kształtów, które są wyśrodkowane zarówno w poziomie, jak iw pionie, oraz zawijania wyrazów. Ponadto tekst nie może opuścić prostokąta.

Spojrzenie na przykład http://www.w3.org/TR/SVG/text.html#TextElement nie pomaga, ponieważ xiy elementu tekstowego różnią się od x i y prostokąta. Wygląda na to, że elementy tekstowe nie mają szerokości ani wysokości. Nie jestem pewien matematyki tutaj.

(Moja tabela html po prostu nie będzie działać).

Lady Aleena
źródło
1
czy możesz przełączyć się na odpowiedź, używając tekstu kotwicy i dominującej linii bazowej? dzięki!
neaumusic
1
neaumusic, gotowe. 8)
Lady Aleena

Odpowiedzi:

310

Łatwe rozwiązanie do wyśrodkowania tekstu w pionie i poziomie w SVG:

  1. Ustaw położenie tekstu na bezwzględne centrum elementu, w którym chcesz go wyśrodkować:

    • Jeśli to rodzic, możesz to zrobić x="50%" y ="50%".
    • Jeśli jest to inny element, xbędzie to xtego elementu + połowa jego szerokości (i podobnie, yale z wysokością).
  2. Użyj text-anchorwłaściwości, aby wyśrodkować tekst w poziomie z wartością middle:

    środkowy

    Renderowane znaki są wyrównane w taki sposób, że geometryczny środek powstałego renderowanego tekstu znajduje się w początkowej bieżącej pozycji tekstu.

  3. Użyj dominant-baselinewłaściwości, aby wyśrodkować tekst w pionie z wartością middle(lub w zależności od tego, jak chcesz, aby wyglądał, możesz to zrobić central)

Oto proste demo:

<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
źródło
1
Wszędzie szukałem rozwiązania tak prostego - nic innego dla mnie nie działało Użyłem tego do wyśrodkowania liczby wewnątrz radialnego paska postępu
anthonytherockjohnson
7
W moim przypadku to dominant-baselinenieruchomość wykonuje pracę, a nie alignment-baseline.
pintoch
1
Jeśli masz zamiar to robić regularnie, można również dodać następujące: <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Dzięki za rozwiązanie.
Manngo,
1
Uruchom własny fragment kodu. To nie działa. Sprawdź to: to nawet nie działa w podglądzie dostarczonym przez sam stackoverflow. Jak stwierdzono wcześniej w komentarzu, dominant-baselinerzeczywiście należy go użyć. Przepraszam, ale ta odpowiedź jest w jakiś sposób myląca. Ponieważ wydaje się, że nie działa w Firefoksie ani Chromium, a dodatkowo musisz przestudiować komentarze, aby samodzielnie znaleźć prawidłowe rozwiązanie, pozwoliłem sobie dodać kolejną odpowiedź z kodem, który działa zgodnie z oczekiwaniami. (Do wiadomości: testowane z FF i Chromium w systemie Ubuntu Linux.)
Regis,
2
@RegisMay thanks za wskazanie tego. Jestem na Chrome na Mac i to działało w porządku z alignment-baseline, ale wydaje się, że może to być dziwaczne rzeczą, ponieważ przeglądając specyfikację, powinno być dominant-baselinedla text, i alignment-baselinena tspan, tref, altGlyph, i textPath. Odpowiednio zaktualizowałem odpowiedź.
Alvaro Montoro
38

SVG 1.2 Tiny dodał zawijanie tekstu, ale większość implementacji SVG, które znajdziesz w przeglądarce (z wyjątkiem Opery) nie ma tej funkcji. Zwykle do Ciebie, programisty, należy ręczne pozycjonowanie tekstu.

Specyfikacja SVG 1.1 zapewnia dobry przegląd tego ograniczenia i możliwych rozwiązań, aby je przezwyciężyć:

Każdy element „tekst” powoduje renderowanie pojedynczego ciągu tekstu. SVG nie wykonuje automatycznego dzielenia wierszy ani zawijania słów. Aby uzyskać efekt wielu wierszy tekstu, użyj jednej z następujących metod:

  • Autor lub pakiet autorski musi wstępnie obliczyć podziały wierszy i użyć wielu elementów „tekstowych” (po jednym na każdy wiersz tekstu).
  • Autor lub pakiet autorski musi wstępnie obliczyć podziały wierszy i użyć pojedynczego elementu „text” z co najmniej jednym elementem podrzędnym „tspan” z odpowiednimi wartościami atrybutów „x”, „y”, „dx” i „dy” aby ustawić nowe pozycje początkowe dla tych znaków, które rozpoczynają nowe linie. (Takie podejście umożliwia użytkownikowi zaznaczanie tekstu w wielu wierszach tekstu - zobacz Zaznaczanie tekstu i operacje na schowku).
  • Wyraź tekst, który ma być renderowany w innej przestrzeni nazw XML, takiej jak XHTML [XHTML] osadzony w linii w elemencie „ForeignObject”. (Uwaga: dokładna semantyka tego podejścia nie jest obecnie w pełni zdefiniowana).

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

Jako prymityw, zawijanie tekstu może być symulowane przy użyciu dyatrybutu i tspanelementów, a jak wspomniano w specyfikacji, niektóre narzędzia mogą to zautomatyzować. Na przykład w Inkscape wybierz żądany kształt i żądany tekst i użyj opcji Tekst -> Przepływ do ramki. Umożliwi to napisanie tekstu z zawijaniem, które będzie zawijane w oparciu o granice kształtu. Upewnij się również, że postępujesz zgodnie z tymi instrukcjami, aby poinformować Inkscape o zachowaniu zgodności z SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

Ponadto istnieje kilka bibliotek JavaScript, których można użyć do dynamicznego zautomatyzowania zawijania tekstu: http://www.carto.net/papers/svg/textFlow/

Warto zwrócić uwagę na rozwiązanie CSVG polegające na zawijaniu kształtu do elementu tekstowego (np. Zobacz przykład „przycisku”), chociaż ważne jest, aby wspomnieć, że ich implementacja nie jest użyteczna w przeglądarce: http://www.csse.monash.edu .au / ~ clm / csvg / about.html

Wspominam o tym, ponieważ opracowałem bibliotekę inspirowaną CSVG, która pozwala robić podobne rzeczy i działa w przeglądarkach internetowych, chociaż jeszcze jej nie wydałem.

jbeard4
źródło
Dzięki za odpowiedź ... ale ACK! Wygląda na to, że będę musiał wtedy zrzucić svg. Jedną z pierwszych rzeczy, o których programiści powinni pomyśleć, było dołączanie tekstu (sformatowanego zgodnie z życzeniem użytkowników) do kształtów! Poczekam, aż się zgrają, zanim to zrobię. Spróbuję przerysować go w Windows Paint i zobaczę, czy to wystarczy. (Gdybym tylko mógł sprawić, by przeglądarki poprawnie wyświetlały tabelę.)
Lady Aleena
Jeśli chodzi o tekst edytowalny w formacie svg, Opera obsługuje edytowalny atrybut z SVG 1.2 Tiny, który umożliwia uzyskanie edytowalnego tekstu przez proste dodanie atrybutu editable="true"do elementu text lub textArea.
Erik Dahlström,
2
@Erik, po raz kolejny Opera wyprzedza konkurencję.
jbeard4
@Lady Aleena, dlaczego nie skorzystać po prostu z rozwiązania Inkscape, o którym myślałem? Jeśli jest to tylko obraz statyczny (np. Brak dynamicznego zachowania, brak dynamicznego aktualizowania treści tekstowej), to powinno działać dobrze. Ponadto będzie skalowalny, czego nie zrobi Paint, ponieważ Paint tworzy grafikę rastrową. Wreszcie nie wierzę, że Paint obsługuje zawijanie tekstu na jakimkolwiek poziomie.
jbeard4
@ echo-flow Już dawno straciłem zaufanie do programów tworzących kod, odkąd użyłem (a następnie zrzuciłem) Microsoft Front Page do generowania kodu HTML. Zawsze obawiam się, że programy dodają własny zastrzeżony kod do tego, co piszę. Pierwsza strona naprawdę zepsuła mój HTML. Jeśli jesteś użytkownikiem Inkscape, czy możesz mi powiedzieć, czy Inkscape umieści swój własny zastrzeżony kod w pliku svg, do którego musiałbym wejść i usunąć po utworzeniu svg?
Lady Aleena,
23

Poprzednie odpowiedzi dały słabe wyniki, gdy używano zaokrąglonych rogów lub stroke-widthże jest to> 1. Na przykład można oczekiwać, że poniższy kod utworzy zaokrąglony prostokąt, ale rogi są przycinane przez svgskładnik nadrzędny :

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

Zamiast tego zalecam zawijanie textin a, svga następnie zagnieżdżanie tego nowego svgi rectrazem w gelemencie, jak w poniższym przykładzie:

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

To rozwiązuje problem z przycinaniem, który występuje w powyższych odpowiedziach. Przetłumaczyłem również grupę prostokąt / tekst przy użyciu transform="translate(x,y)"atrybutu, aby zademonstrować, że zapewnia to bardziej intuicyjne podejście do pozycjonowania prostokąta / tekstu na ekranie.

Austin D.
źródło
16

Jeśli tworzysz SVG programowo, możesz to uprościć i zrobić coś takiego:

  <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
źródło
3
Czy nie powinno tak być, dominant-baselinea text-anchornie dominantBaselinei textAnchor?
Nathaniel M. Beaver
1
@ NathanielM.Beaver Tak, powinno być. Dobry chwyt. Powyższy kod pochodzi z Reacta, który niestety wymaga zmiany nazw atrybutów za pomocą camelCase.
Brennan Cheung
13

Możesz bezpośrednio korzystać z text-anchor = "middle"właściwości. Radzę utworzyć element otoki svg nad prostokątem i tekstem. W ten sposób możesz użyć całego elementu używając jednego selektora css. Upewnij się, że właściwość „x” i „y” tekstu jest ustawiona na 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
źródło
7

Blog ze wszystkimi szczegółami: 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
źródło
Nie jest wyśrodkowany w przeglądarce Firefox, podczas gdy inne rozwiązania są. codepen.io/anon/pen/oEByYr
tgf
Testowałem na Firefoksie 58.0.1 (64-bitowym), działa. Czy możesz wspomnieć o wersji, dla której to nie działa?
Sahil Gupta,
Firefox 58.0.2 64-bitowy Tekst jest nieco wyższy niż powinien. Na początku może nie być łatwo zauważalne, ale jeśli twoje pudełko pasuje bardzo blisko, jest to bardziej oczywiste; spróbuj zmniejszyć wysokość.
tgf
6

alignment-baselinenie jest właściwym atrybutem do użycia w tym miejscu. Prawidłowa odpowiedź to użycie kombinacji dominant-baseline="central"i 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
źródło
2

Jednym ze sposobów wstawienia tekstu do prostokąta jest wstawienie obcego obiektu, którym jest DIV, wewnątrz prostokąta.

W ten sposób tekst będzie uwzględniał ograniczenia 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
źródło
1

Miałem problem z wycentrowaniem czegokolwiek za pomocą SVG, więc stworzyłem własną małą funkcję. miejmy nadzieję, że to powinno ci pomóc. Zauważ, że działa tylko dla elementów 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
źródło
1

Do poziomego i pionowego wyrównania tekstu w grafice możesz użyć następujących stylów CSS. W szczególności zwróć uwagę, że dominant-baseline:middlejest to prawdopodobnie błędne, ponieważ jest to (zwykle) w połowie odległości między górą a linią bazową, a nie w połowie odległości między górą a dołem. Również niektóre źródła (np. Mozilla ) używają dominant-baseline:hangingzamiast dominant-baseline:text-before-edge. Jest to również prawdopodobnie błędne, ponieważ hangingjest przeznaczone dla skryptów indyjskich. Oczywiście, jeśli używasz mieszanki łaciny, indyjskiego, ideogramów lub czegokolwiek innego, prawdopodobnie będziesz musiał przeczytać dokumentację .

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

Edycja: Właśnie zauważyłem, że Mozilla również używa, dominant-baseline:baselineco jest zdecydowanie błędne: nie jest to nawet rozpoznawalna wartość! Zakładam, że domyślnie ustawia czcionkę na domyślną, czyli alphabetic, więc mieli szczęście.

Więcej edycji: Safari (11.1.2) rozumie, text-before-edgeale nie text-after-edge. To również zawodzi ideographic. Świetna rzecz, Apple. Więc alphabeticmimo wszystko możesz być zmuszony do używania i zezwalania na zjazdy. Przepraszam.

Adam Chalcraft
źródło