Як розмістити і централізувати текст у прямокутнику SVG


179

У мене є такий прямокутник ...

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

Я хотів би зосередити слово "Художня література" всередині нього. На інших прямокутниках, чи не використовується текст SVG, щоб залишитися в них? Я, здається, не можу знайти нічого конкретно про вставку тексту у фігури, орієнтовані як по горизонталі, так і по вертикалі, а також загортання слова. Також текст не може залишити прямокутник.

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

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


1
ви можете, будь ласка, перейти до відповіді за допомогою тексту-якоря та домінанти-базової лінії? Дякую!
неавмузичний

1
неавмузний, зроблений. 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>. Дякую за рішення.
Манго

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 Дрібні додані текстові обгортки, але більшість реалізацій SVG, які ви знайдете у браузері (за винятком Opera), не реалізували цю функцію. Як правило, розробник розміщує текст вручну.

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

Кожен елемент 'текст' викликає виведення одного рядка тексту. SVG не виконує автоматичного розриву рядків чи перенесення слів. Для досягнення ефекту від декількох рядків тексту використовуйте один із наступних методів:

  • Автору або авторському пакету необхідно попередньо обчислити розриви рядків і використовувати кілька елементів "текст" (по одному для кожного рядка тексту).
  • Автору або авторовому пакету потрібно попередньо обчислити розриви рядків і використовувати один "текстовий" елемент з одним або декількома дочірніми елементами "tspan" з відповідними значеннями для атрибутів "x", "y", "dx" і "dy" встановити нові стартові позиції для тих символів, які починають нові рядки. (Цей підхід дозволяє вибирати текст користувача у кількох рядках тексту - див. Вибір тексту та операції буфера обміну.)
  • Висловіть текст, що буде наданий в іншому просторі імен XML, наприклад, XHTML [XHTML], вбудований в рядок, в елемент 'ForeignObject'. (Примітка: точна семантика цього підходу наразі не визначена повністю.)

http://www.w3.org/TR/SVG11/text.html#Вступ

Як примітив, обгортку тексту можна змоделювати за допомогою dyатрибута та tspanелементів, і як зазначено у специфікації, деякі інструменти можуть це автоматизувати. Наприклад, у Inkscape виберіть потрібну форму та потрібний текст та використовуйте Text -> Flow to Frame. Це дозволить писати текст із загортанням, яке буде обгортати виходячи з меж фігури. Також переконайтеся, що дотримуйтесь цих інструкцій, щоб повідомити 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, яка дозволяє вам робити подібні речі та працює в веб-браузерах, хоча я її ще не випустив.


Дякую за відповідь ... але ACK! Виявляється, мені доведеться потім скинути svg. Одне з перших, що повинні подумати розробники, - це прикріплення тексту (відформатованого так, як бажають користувачі) до форм! Я зачекаю, поки вони зберуться, перш ніж я з цим зайдусь. Я спробую перемалювати його в Windows Paint і побачити, чи це вдасться зробити. (Якби тільки я міг змусити браузери правильно відображати таблицю.)
Леді Алена

Щодо редагованого тексту у svg, Opera підтримує редагуваний атрибут від SVG 1.2 Tiny, завдяки якому можна отримати редагований текст, просто додавши атрибут editable="true"до тексту або елемента textArea.
Ерік Дальстрем

2
@Erik, знову Опера випереджає криву.
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в a, 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>


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-бітний), він працює. Чи можете ви згадати версію, для якої вона не працює для вас?
Сахіл Гупта

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призначений для сценаріїв Indic. Звичайно, якщо ви використовуєте суміш латинської, індійської, ідеографій чи будь-чого іншого, вам, ймовірно, потрібно буде прочитати документацію .

/* 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та дозволяти спускатися. Вибачте.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.