Как разместить и отцентрировать текст в прямоугольнике SVG

179

У меня есть следующий прямоугольник ...

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

Я хотел бы сосредоточить слово «беллетристика» внутри него. Для других прямоугольников svg word wrap остается в них? Кажется, я не могу найти что-то конкретно о вставке текста в фигуры, которые центрированы как по горизонтали, так и по вертикали, и перенос слов. Также текст не может покинуть прямоугольник.

Просмотр примера http://www.w3.org/TR/SVG/text.html#TextElement не помогает, поскольку x и y текстового элемента отличаются от x и y прямоугольника. Кажется, нет ширины и высоты для текстовых элементов. Я не уверен в математике здесь.

(Моя HTML-таблица просто не будет работать.)

Леди алена
источник
1
Не могли бы вы перейти к ответу с помощью текстового якоря и доминантной базовой линии? Спасибо!
neaumusic
1
neaumusic, сделано. 8)
Леди Алина

Ответы:

310

Простое решение для центрирования текста по горизонтали и вертикали в SVG:

  1. Установите положение текста в абсолютный центр элемента, в котором вы хотите центрировать его:

    • Если это родитель, вы могли бы просто сделать x="50%" y ="50%".
    • Если это еще один элемент, xбудет xэтот элемент + половина его ширины (и аналогично для, yно с высотой).
  2. Используйте text-anchorсвойство для центрирования текста по горизонтали со значением middle:

    средний

    Визуализированные символы выровнены так, что геометрическая середина результирующего визуализированного текста находится в начальной текущей позиции текста.

  3. Используйте dominant-baselineсвойство для центрирования текста по вертикали со значением middle(или в зависимости от того, как вы хотите, чтобы он выглядел, вы можете захотеть сделатьcentral )

Вот простая демонстрация:

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

Альваро Монторо
источник
1
Я всюду искал решение, такое простое - у меня больше ничего не
получалось,
7
В моем случае, это dominant-baselineсвойство, которое делает работу, а не alignment-baseline.
pintoch
1
Если вы собираетесь делать это регулярно, вы можете также добавить следующее: <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Спасибо за решение.
Manngo
1
Запустите свой собственный фрагмент кода. Не работает Проверьте это: он даже не работает в предварительном просмотре, предоставляемом самим stackoverflow. Как указано ранее в комментарии dominant-baselineдействительно нужно использовать. Извините, но этот ответ как-то вводит в заблуждение. Поскольку он не работает ни в Firefox, ни в Chromium, и, кроме того, вы должны изучить комментарии, чтобы самостоятельно найти правильное решение, я позволил себе добавить еще один ответ с кодом, который работает, как и ожидалось. (Для записей: протестировано с FF и Chromium на Ubuntu Linux.)
Regis, май
2
@RegisMay спасибо за указание на это. Я на Chrome на Mac , и она работала отлично с alignment-baseline, но мне кажется , что это может быть изворотливой вещь , потому что обзор спецификации, это должно быть dominant-baselineдля text, и alignment-baselineдля tspan, tref, altGlyph, и textPath. Я обновил ответ соответственно.
Альваро Монторо
38

SVG 1.2 Tiny добавил перенос текста, но большинство реализаций SVG, которые вы найдете в браузере (за исключением Opera), не реализовали эту функцию. Как правило, вам, разработчику, нужно размещать текст вручную.

Спецификация SVG 1.1 предоставляет хороший обзор этого ограничения и возможные решения для его преодоления:

Каждый текстовый элемент вызывает визуализацию одной строки текста. SVG не выполняет автоматического разрыва строки или переноса слов. Чтобы добиться эффекта нескольких строк текста, используйте один из следующих методов:

  • Автору или авторскому пакету необходимо предварительно рассчитать разрывы строк и использовать несколько текстовых элементов (по одному на каждую строку текста).
  • Авторский или авторский пакет должен предварительно рассчитать разрывы строк и использовать один текстовый элемент с одним или несколькими дочерними элементами «tspan» с соответствующими значениями для атрибутов «x», «y», «dx» и «dy» установить новые стартовые позиции для тех персонажей, которые начинают новые строки. (Этот подход позволяет пользователю выбирать текст из нескольких строк текста - см. Выбор текста и операции с буфером обмена.)
  • Выразите текст, который будет отображаться в другом пространстве имен XML, например, в XHTML [XHTML], встроенном в элементе foreignObject. (Примечание: точная семантика этого подхода не полностью определена в настоящее время.)

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

В качестве примитива обтекание текстом может быть смоделировано с использованием dyатрибута и tspanэлементов, и, как указано в спецификации, некоторые инструменты могут автоматизировать это. Например, в Inkscape выберите нужную фигуру и текст, который вам нужен, и используйте «Текст» -> «Переместить в рамку». Это позволит вам написать текст с переносом, который будет переноситься на основе границ фигуры. Кроме того, обязательно следуйте этим инструкциям, чтобы сообщить Inkscape о совместимости с SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

Кроме того, есть несколько библиотек JavaScript, которые можно использовать для динамической автоматизации переноса текста: http://www.carto.net/papers/svg/textFlow/

Интересно отметить решение CSVG обернуть фигуру в текстовый элемент (например, см. Их пример «кнопки»), хотя важно отметить, что их реализация не может использоваться в браузере: http://www.csse.monash.edu .au / ~ CLM / csvg / about.html

Я упоминаю об этом, потому что я разработал библиотеку в стиле CSVG, которая позволяет вам делать подобные вещи и работает в веб-браузерах, хотя я еще не выпустил ее.

jbeard4
источник
Спасибо за ответ ... но ACK! Похоже, тогда мне придется сбросить svg. Одной из первых вещей, о которых должны были подумать разработчики, было прикрепление текста (отформатированного по желанию пользователя) к фигурам! Я подожду, пока они соберутся, прежде чем я сделаю это. Я попытаюсь перерисовать его в Windows Paint и посмотреть, удастся ли это сделать. (Если бы я только мог заставить браузеры правильно отображать таблицу.)
Lady Aleena,
Что касается редактируемого текста в svg, Opera поддерживает редактируемый атрибут из SVG 1.2 Tiny, который позволяет получить редактируемый текст, просто добавив атрибут editable="true"к элементу text или textArea.
Эрик Дальстрём,
2
@ Эрик, еще раз Opera опережает кривую.
jbeard4
@Lady Aleena, почему бы просто не использовать решение Inkscape, которое я наделал? Если это просто статичное изображение (например, без динамического поведения, без динамического обновления текстового содержимого), то это должно работать нормально. Кроме того, он будет масштабируемым, чего не может Paint, поскольку Paint создает растровую графику. Наконец, я не верю, что Paint поддерживает перенос текста на любом уровне.
jbeard4
@ echo-flow Я давно потерял доверие к программам, создающим код, с тех пор, как использовал (и впоследствии сбросил) Microsoft Front Page для генерации html. Я всегда настороженно отношусь к программам, добавляющим собственный проприетарный код к тому, что я пишу. Главная страница действительно испортила мой HTML. Если вы являетесь пользователем Inkscape, можете ли вы сказать мне, если Inkscape помещает свой собственный проприетарный код в SVG, в который мне нужно было бы перейти и удалить после создания SVG?
Леди Алина
23

Предыдущие ответы дали плохие результаты при использовании закругленных углов или stroke-widthэто> 1. Например, вы могли бы ожидать, что следующий код создаст скругленный прямоугольник, но углы обрезаются родительским 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>

Вместо этого я рекомендую оборачивать textв svgи затем вложенности , что новый svgи в rectтусовке внутри gэлемента, как показано в следующем примере:

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

Это устраняет проблему отсечения, возникающую в ответах выше. Я также перевел группу rect / text, используя transform="translate(x,y)"атрибут, чтобы продемонстрировать, что это обеспечивает более интуитивный подход к размещению прямоугольника / текста на экране.

Остин Д
источник
16

Если вы создаете SVG программно, вы можете упростить его и сделать что-то вроде этого:

  <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>
Бреннан Чунг
источник
3
Разве это не должно быть, dominant-baselineа text-anchorне dominantBaselineи textAnchor?
Натаниэль М. Бивер
1
@ NathanielM.Beaver Да, так и должно быть. Хороший улов. Приведенный выше код взят из React, который, к сожалению, требует переименования атрибутов с помощью camelCase.
Бреннан Чунг
13

Вы можете напрямую использовать text-anchor = "middle"собственность. Я советую создать элемент обертки SVG над вашим прямоугольником и текстом. Таким образом, вы можете использовать весь элемент, используя один селектор CSS. Убедитесь, что для свойства x и y указано 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
источник
7

Подробный блог: 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>

Сахил Гупта
источник
Не в центре Firefox, в то время как другие решения. codepen.io/anon/pen/oEByYr
TGF
Я тестировал на Firefox 58.0.1 (64-bit), он работает. Можете ли вы назвать версию, для которой он не работает?
Сахил Гупта
Firefox 58.0.2 64bit. Текст немного выше, чем должен быть. Поначалу это может быть не так легко заметить, но если ваша коробка плотно прилегает, это более очевидно; попробуйте уменьшить высоту.
TGF
6

alignment-baselineэто неправильный атрибут для использования здесь. Правильный ответ - использовать комбинацию dominant-baseline="central"и 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

Один из способов вставить текст в прямоугольник - это вставить посторонний объект, который является DIV, внутри прямоугольного объекта.

Таким образом, текст будет ограничивать пределы 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>

Бруно Бастос
источник
1

У меня было много времени, когда все было сосредоточено с помощью SVG, поэтому я развернул свою маленькую функцию. надеюсь, это должно помочь вам. Обратите внимание, что это работает только для элементов 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

Для горизонтального и вертикального выравнивания текста в графике вы можете использовать следующие стили CSS. В частности, обратите внимание, что dominant-baseline:middleэто, вероятно, неправильно, поскольку это (обычно) на полпути между вершиной и базовой линией, а не на полпути между вершиной и основанием. Кроме того, некоторые некоторые источники (например, Mozilla ) используют dominant-baseline:hangingвместо dominant-baseline:text-before-edge. Это также, вероятно, неправильно, так hangingкак разработано для индийских скриптов. Конечно, если вы используете смесь латинского, индийского, иероглифов или чего-то еще, вам, вероятно, придется прочитать документацию .

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

Редактировать: я только что заметил, что Mozilla также использует, dominant-baseline:baselineчто, безусловно, неправильно: это даже не признанная ценность! Я предполагаю, что по умолчанию используется шрифт по умолчанию alphabetic, поэтому им повезло.

Подробнее: Safari (11.1.2) понимает, text-before-edgeно не понимает text-after-edge. Это также не удается ideographic. Отличная штука, Apple. Таким образом, вы можете быть вынуждены использовать alphabeticи разрешить спуски в конце концов. Сожалею.

Адам Чалкрафт
источник