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

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

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

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

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

(Моя таблица html просто не работает.)

Ответ 1

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

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

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

    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>

Ответ 2

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

Ответ 3

Предыдущие ответы дали плохие результаты при использовании закругленных углов или 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>

Ответ 4

Вы можете напрямую использовать свойство 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>

Ответ 5

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

  <g>
      <rect x={x} y={y} width={width} height={height} />
      <text
          x={x + width / 2}
          y={y + height / 2}
          dominantBaseline="middle"
          textAnchor="middle"
      >
          {label}
      </text>
  </g>

Ответ 6

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

Ответ 7

Один способ вставки текста внутри прямоугольника - вставить посторонний объект, являющийся 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>

Ответ 8

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>

Ответ 9

У меня было время от времени подбирать что-нибудь с помощью 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);
}

Ответ 10

Для горизонтального и вертикального выравнивания текста в графике вы можете использовать следующие стили 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 и в конце концов разрешить спуск. Сожалею.