【问题标题】:Pure SVG way to fit text to a box将文本放入框的纯 SVG 方式
【发布时间】:2013-03-04 00:52:52
【问题描述】:

已知盒子大小。文本字符串长度未知。在不破坏其纵横比的情况下使文本适合框。

经过一晚上的谷歌搜索和阅读 SVG 规范,我很确定如果没有 JavaScript,这是不可能的。我能得到的最接近的方法是使用 textLengthlengthAdjust 文本属性,但这只会沿一个轴拉伸文本。

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

我知道SVG Scaling Text to fit containerfitting text into the box

【问题讨论】:

  • 我最终在 javascript 中创建了一个循环来增加字体大小,直到 getBBox 显示它不再适合。丑,所以还是希望有别的办法。
  • 我也一直在尝试让同样的功能发挥作用。我找到的最好的方法和你做的一样。循环浏览 JS 并更改字体直到适合。但即使在字体中,上下仍有一些空白,因此您似乎无法正确使用它。
  • 这太糟糕了,这似乎是一个基本的东西,但规范很清楚它只向一个方向延伸。在玩了之后,我可以通过使用变换仅修改 Y 比例来接近它,又名:transform="scale(0,5)" - jsfiddle.net/G5L8W
  • 我正在尝试考虑是否有一种方法可以计算应用于 x 轴上的文本的比例并将其应用于 y 轴,但这也意味着添加检查确保你没有过度缩放 Y,然后你又回到使用 JS。我敢打赌你可以更好地使用 JS 的变换/缩放。

标签: svg


【解决方案1】:

首先:刚刚看到答案并不能完全满足您的需求 - 它可能仍然是一种选择,所以我们开始吧:

您正确地观察到 svg 不直接支持自动换行。但是,您可能会受益于 foreignObject 元素作为 xhtml 片段的包装器,其中可以使用自动换行。

看看这个独立的演示(available online):

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

【讨论】:

  • 谢谢,但对自动换行不感兴趣。
  • @collapsar,您的演示链接已损坏
【解决方案2】:

我没有找到不使用 Javascript 直接执行此操作的方法,但我找到了一个非常简单的 JS 解决方案,无需 for 循环且无需修改字体大小并且适合所有尺寸,也就是说,文本会增长到最短边的极限。

基本上,我使用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>

在前面的示例中,文本会增长到width == 500,但如果我使用width = 500height = 30 的框大小,那么文本会增长到height == 30

【讨论】:

  • 或者干脆... textNode.setAttribute("transform", "scale("+value+")");
  • 快速注意 - scale 属性也会影响当前项目的坐标系,因此如果您希望元素位于同一位置,则需要将 x 和 y 位置除以标量获得相同的相对位置
  • 或者更简单...将文本font-size设置为1(在我们的例子中为1em);那么一旦你有了比例因子(这个答案中的value),只需将font-size设置为它。请参阅下面的示例。
  • 我还希望文本垂直居中,所以我将结尾(var value = ... 之后)改为textNode.style.transform = "translateY(50%) scale(" + value + ")"; textNode.style.transformOrigin = '0px 0px'; textNode.style.dominantBaseline = 'central';
  • @Krunal,我会尝试使用 Inkscape CLI:stackoverflow.com/a/34725966/661140
【解决方案3】:

我认为它不是您想做的解决方案,但您可以使用textLength 全宽百分比为="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

【讨论】:

  • 它只是扩大了字母间距。 :(
  • 是的,只是为了清楚文本居中 - 属性是“text-anchor”,您还需要 x=50%,如 &lt;text x="50%" y="50%" textLength="100%" text-anchor="middle"&gt;AB&lt;/text&gt;
【解决方案4】:

我已经开发了@Roberto 答案,但我们没有转换(缩放)textNode,而是简单地:

  • font-size1em 开头
  • 根据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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多