在没有删除线的情况下获取与 SVG 中的路径内联的文本

Getting text inline with a path in SVG without strikethrough

我正在尝试让文本显示在线条内,以便与线条保持一致,如下图所示。

仅仅使用该行作为图像的文本路径是行不通的,因为当我将文本设为内联时,该行会继续贯穿文本。

我有一个适用于直线路径的解决方案。我测量文本长度和完整路径长度,然后将其分成 3 个单独的路径。我在第 1 条和第 3 条路径上画了一笔,中间的路径用于 textPath。

除了直线路径之外,我无法使它适用于任何其他东西,因为我无法测量文本或算出每个部分的长度。

我正在使用 JointJS,但纯粹的 pseudocode/SVG 答案同样有用。

解决此问题的一种方法是创建一个与您的文本长度相同的文本块,该文本块只是 Unicode 块字符,填充等于您的背景颜色,并在绘制实际文本路径之前绘制它。 (您需要将膨胀过滤器添加到由 arcs/curves 引起的伪影上 - 您可能需要根据需要调整半径)。

<svg width="12cm" height="3.6cm" viewBox="0 0 1000 300" version="1.1"
 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
<path id="MyPath"
      d="M 100 200 
         C 200 100 300   0 400 100
         C 500 200 600 300 700 200
         C 800 100 900 100 900 100" />

<filter id="dilatethis">
  <feMorphology operator="dilate" radius="4"/>
</filter>
  </defs>

  <use xlink:href="#MyPath" fill="none" stroke="red" stroke-width="5" y="-15" />

  <text font-family="monospace" font-size="42.5" fill="white" filter="url(#dilatethis)" >
<textPath xlink:href="#MyPath" startOffset="155" >
  &#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;&#9608;
</textPath>
  </text>


  <text font-family="Verdana" font-size="42.5" fill="blue">
<textPath xlink:href="#MyPath" startOffset="155" >
  Just imagine that this  
</textPath>
  </text>

</svg>

最简单的解决方案就是在行的顶部和文本的下方放置一个白色(或任何背景颜色)矩形。

<svg width="400px" height="100px">
  <line x1="50" y1="50" x2="350" y2="50" stroke="blue" stroke-width="2"/>
  <rect x="125" y="40" width="150" height="20" fill="white"/>
  <text x="200" y="54" text-anchor="middle" fill="blue">Text inline with arrow</text>
</svg>

或者,不使用矩形,而是复制文本,但将其设为白色并用粗笔划。这种方法的优点是您不必为尝试计算出矩形的正确尺寸而大惊小怪。

<svg width="400px" height="100px">
  <line x1="50" y1="50" x2="350" y2="50" stroke="blue" stroke-width="2"/>
  <text x="200" y="54" text-anchor="middle" fill="white" stroke="white" stroke-width="5">Text inline with arrow</text>
  <text x="200" y="54" text-anchor="middle" fill="blue">Text inline with arrow</text>
</svg>

关于此方法的一点是,如果字母或单词之间的间隙足够大,线条就会显示出来。您可能会或可能不会发现它令人满意。

这只是两种可能的方法。还能想到几个,不过涉及的有点多。

根据 Michael Mullany 和 Paul LeBeau 的回答,我已经在 J​​ointJS 中实现了它,因此我发布了所需的更改。

定义一个扩展 joint.dia.Link 的新形状模型,例如:joint.shapes.gary.InlineLabelLink

使用两个新文本字段扩展形状模型中的标记。一种用于标签 (inlineText),一种用于位于标签后面的掩码 (inlineTextMask)。

添加一些属性以确保文本位于需要的位置。

添加一个名为 "caption" 的新 属性,它将保存内联标签文本。

例如:

joint.shapes.gary.InlineLabelLink = joint.dia.Link.extend({

    defaults: joint.util.deepSupplement({
    markup: [
        '<path class="connection" stroke="black" d="M 0 0 0 0"/>',
        '<path class="marker-source" fill="black" stroke="black" d="M 0 0 0 0"/>',
        '<path class="marker-target" fill="black" stroke="black" d="M 0 0 0 0"/>',
        '<path class="connection-wrap" d="M 0 0 0 0"/>',
        '<text class="inlineTextMask" />',
        '<text class="inlineText" />',
        '<g class="labels"/>',
        '<g class="marker-vertices"/>',
        '<g class="marker-arrowheads"/>',
        '<g class="link-tools"/>'
    ].join(''),
    attrs: {
        '.inlineText': {
            'dominant-baseline': 'central',
            'text-anchor': 'middle',
            'pointer-events': 'none',
            'font-size': 10
        },
        '.inlineTextMask': {
            'dominant-baseline': 'central',
            'text-anchor': 'middle',
            'pointer-events': 'none',
            'fill':'#ffffff',
            'stroke':'#ffffff',
            'stroke-linejoin':'smooth',
            'stroke-linecap':'round',
            'stroke-width': 8,
            'font-size': 10
        }
    },
    caption: 'TBD'
}, joint.dia.Link.prototype.defaults)});

然后创建一个从 joint.dia.LinkView

扩展的新视图 class

在 class 中,我们覆盖了 update 函数,以便它可以在正确的位置绘制内联标签。

joint.shapes.gary.InlineLabelLinkView =  joint.dia.LinkView.extend({

    update: function() {
        joint.dia.LinkView.prototype.update.apply(this, arguments);

        var inlinePath = this._V.connection.node.getAttribute('d');


        this._V.inlineText.text(this.model.get('caption'),
            {textPath: { d: inlinePath, startOffset:"50%" } });

        this._V.inlineTextMask.text(this.model.get('caption').replace(' ','\u9608'),
            {textPath: { d: inlinePath, startOffset:"50%" } });

        return this;
    }
});

更新后的视图 class 从 link 获取路径,然后将此路径应用于文本掩码和文本属性。它使用 Vectorizer 的文本方法创建适当的 defs、textPath 和 tspan 条目。

这确实有一个问题,即有时文本可能会颠倒,具体取决于 link 的方向。这可以通过反转路径的方向来解决。

我为此使用了定制函数(未显示),但那里有像 https://github.com/SmartArtsStudio/smart-svg-path and https://www.npmjs.com/package/svg-path-reverse

这样的库
    if (this.sourcePoint.x > this.targetPoint.x) {
        inlinePath = reversePath(inlinePath);
    }