Подтвердить что ты не робот

Чистый SVG способ подгонки текста к коробке

Известен размер коробки. Длина текстовой строки неизвестна. Установите текст в поле, не разрушая его соотношение сторон.

enter image description here

После вечера поиска в Google и чтения спецификации SVG, я уверен, что это невозможно без javascript. Самое близкое, что я мог получить, это использовать текстовые атрибуты textLength и lengthAdjust, но это растягивает текст только по одной оси.

<svg width="436" height="180"
    style="border:solid 6px"
    xmlns="http://www.w3.org/2000/svg">
    <text y="50%" textLength="436" lengthAdjust="spacingAndGlyphs">UGLY TEXT</text>
</svg>

enter image description here

Я знаю текст масштабирования SVG, чтобы он соответствовал контейнеру и вставляя текст в поле

4b9b3361

Ответ 1

Я не нашел способа сделать это напрямую без Javascript, но я нашел JS довольно простое решение, без циклов и без изменения размера шрифта и хорошо вписывается во все измерения, то есть текст увеличивается до предела самая короткая сторона.

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

Это код:

<?xml version="1.0" encoding="UTF-8" ?>
<svg version="1.2" viewBox="0 0 1000 1000" width="1000" height="1000" xmlns="http://www.w3.org/2000/svg" >
 <text id="t1" y="50" >MY UGLY TEXT</text>
 <script type="application/ecmascript"> 

    var width=500, height=500;

    var textNode = document.getElementById("t1");
    var bb = textNode.getBBox();
    var widthTransform = width / bb.width;
    var heightTransform = height / bb.height;
    var value = widthTransform < heightTransform ? widthTransform : heightTransform;
    textNode.setAttribute("transform", "matrix("+value+", 0, 0, "+value+", 0,0)");

 </script>
</svg>

Ответ 2

Прежде всего: только что увидел, что ответ не совсем соответствует вашим потребностям - он все еще может быть вариантом, так что здесь мы идем:

вы правильно заметили, что svg не поддерживает перенос слов. однако, вы могли бы извлечь выгоду из элементов foreignObject служащих оболочкой для фрагментов xhtml, где доступна перенос слов.

взгляните на это автономное демо (доступно онлайн):

<?xml version="1.0" encoding="utf-8"?>
<!-- SO: http://stackoverflow.com/questions/15430189/pure-svg-way-to-fit-text-to-a-box  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
   xmlns:xhtml="http://www.w3.org/1999/xhtml"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   version="1.1"
   width="20cm" height="20cm"
   viewBox="0 0 500 500"
   preserveAspectRatio="xMinYMin"
   style="background-color:white; border: solid 1px black;"
>
  <title>simulated wrapping in svg</title>
  <desc>A foreignObject container</desc>

   <!-- Text-Elemente -->
   <foreignObject
      x="100" y="100" width="200" height="150"
      transform="translate(0,0)"
   >
      <xhtml:div style="display: table; height: 150px; overflow: hidden;">
         <xhtml:div style="display: table-cell; vertical-align: middle;">
            <xhtml:div style="color:black; text-align:center;">Demo test that is supposed to be word-wrapped somewhere along the line to show that it is indeed possible to simulate ordinary text containers in svg.</xhtml:div>
         </xhtml:div>
      </xhtml:div>
   </foreignObject>

  <rect x="100" y="100" width="200" height="150" fill="transparent" stroke="red" stroke-width="3"/>
</svg>

Ответ 3

Я разработал ответ @Roberto, но вместо преобразования (масштабирования) textNode мы просто:

  • начните с font-size 1em
  • рассчитать масштаб на основе getBBox
  • установить font-size в этом масштабе

(Вы также можете использовать 1px и т.д.)

Вот React HOC, который делает это:

import React from 'react';
import TextBox from './TextBox';

const AutoFitTextBox = TextBoxComponent =>
  class extends React.Component {
    constructor(props) {
      super(props);
      this.svgTextNode = React.createRef();
      this.state = { scale: 1 };
    }

    componentDidMount() {
      const { width, height } = this.props;
      const textBBox = this.getTextBBox();
      const widthScale = width / textBBox.width;
      const heightScale = height / textBBox.height;
      const scale = Math.min(widthScale, heightScale);

      this.setState({ scale });
    }

    getTextBBox() {
      const svgTextNode = this.svgTextNode.current;
      return svgTextNode.getBBox();
    }

    render() {
      const { scale } = this.state;
      return (
        <TextBoxComponent
          forwardRef={this.svgTextNode}
          fontSize={'${scale}em'}
          {...this.props}
        />
      );
    }
  };

export default AutoFitTextBox(TextBox);

Ответ 4

Я не думаю, что это решение для того, что вы хотите сделать, но вы можете использовать textlenght с процентом ="100%" для полной ширины.

<svg width="436" height="180"
    style="border:solid 6px"
    xmlns="http://www.w3.org/2000/svg">
    <text x="0%" y="50%" textLength="100%">blabla</text>
</svg>

вы также можете добавить textanchor="middle" и изменить положение x, чтобы полностью отделить текст

это не изменит шрифт, и у вас будет странная пространственная ширина пробелов...

JSFIDDLE DEMO