在没有删除线的情况下获取与 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" >
████████████████████
</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 的回答,我已经在 JointJS 中实现了它,因此我发布了所需的更改。
定义一个扩展 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);
}
我正在尝试让文本显示在线条内,以便与线条保持一致,如下图所示。
仅仅使用该行作为图像的文本路径是行不通的,因为当我将文本设为内联时,该行会继续贯穿文本。
我有一个适用于直线路径的解决方案。我测量文本长度和完整路径长度,然后将其分成 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" >
████████████████████
</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 的回答,我已经在 JointJS 中实现了它,因此我发布了所需的更改。
定义一个扩展 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 中,我们覆盖了 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);
}