如何在SVG矩形中放置和居中文本

我有如下矩形:

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

我想把“小说”这个词放在中间。在里面。对于其他矩形,SVG换行是否保持在其中?我似乎找不到任何关于在水平和垂直居中的形状和换行中插入文本的具体内容。此外,文本不能离开矩形。

查看https://www.w3.org/TR/SVG/text.html#TextElement示例并没有帮助,因为文本元素的x和y与矩形的x和y不同。文本元素似乎没有宽度和高度。我不确定这里的数学。

(我的HTML表就是不能工作。)

295987 次浏览

SVG 1.2 Tiny添加了文本换行,但是您在浏览器中看到的大多数SVG实现(Opera除外)都没有实现这个特性。这通常取决于您(开发人员)手动定位文本。

SVG 1.1规范很好地概述了这一限制,以及克服它的可能解决方案:

每个' text '元素导致一个单一的 要呈现的文本字符串。SVG 不执行自动换行或 包装。为了达到效果 在多行文本中,使用其中之一 方法如下:

  • 作者或编写包需要 来预先计算换行符并使用 多个“文本”元素(每个元素一个
  • 作者或作者 包需要预先计算行 中断并使用单个“text”元素 带着一个或多个“tspan”孩子 具有适当值的元素 属性' x ', ' y ', ' dx '和' dy '到 为这些设置新的开始位置 字符开始新的行。 (这种方法允许用户文本 的多行选择 文本—参见文本选择和 李剪贴板操作。)< / > <李>表达 以另一种XML呈现的文本 命名空间,例如XHTML [XHTML] 内嵌在 “foreignObject”元素。(注意: 这种方法的确切语义是 目前还没有完全定义)

http://www.w3.org/TR/SVG11/text.html#Introduction

作为一个原语,文本换行可以通过使用dy属性和tspan元素来模拟,正如规范中提到的,一些工具可以自动执行此操作。例如,在Inkscape中,选择你想要的形状和你想要的文本,并使用text ->流进框架。这将允许您使用换行来编写文本,换行将基于形状的边界。另外,确保你按照这些说明告诉Inkscape保持与SVG 1.1的兼容性: https://wiki.inkscape.org/wiki/Frequently_asked_questions#What_about_flowed_text.3F < / p > 此外,还有一些JavaScript库可以用来动态自动化文本包装: https://old.carto.net/papers/svg/textFlow/ < / p > 注意到CSVG将形状包装到文本元素的解决方案很有趣(例如,看到他们的"button"),尽管重要的是要提到它们的实现在浏览器中是可用的: https://users.monash.edu/~clm/csvg/about.html < / p >

我提到这一点是因为我已经开发了一个csvg启发的库,它允许您做类似的事情,并且可以在web浏览器中工作,尽管我还没有发布它。

我花了很长时间使用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);
}

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

如果你想把它应用到很多元素上,你也可以把它用于CSS。例如:

svg text{
text-anchor: middle;
dominant-baseline: middle;
}

可以直接使用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>

在矩形中插入文本的一种方法是插入一个外部对象,它是一个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>

前面的答案在使用圆角或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包装在svg中,然后将新的svgrect嵌套在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>

这修复了上面的答案中出现的剪辑问题。我还使用transform="translate(x,y)"属性翻译了rect/text组,以证明这提供了一种更直观的方法来在屏幕上定位rect/text。

完整细节博客:https://web.archive.org/web/20180717015233/http://blog.techhysahil.com:80/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>

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>

如果你是用编程的方式创建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>

对于图形中的文本的水平和垂直对齐, 你可能想要使用下面的CSS样式。 特别要注意,dominant-baseline:middle可能是错误的, 因为这(通常)是顶部和基线之间的中间, 而不是在顶部和底部的中间。 此外,一些某些源(例如Mozilla)使用dominant-baseline:hanging代替 dominant-baseline:text-before-edge。这也可能是错误的,因为 hanging是为印度脚本设计的。 当然,如果你用的是拉丁语,印度语,表意文字 你可能需要读取的文档.

/* 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上也失败。好东西,苹果。因此,你可能会被迫使用alphabetic,并允许下降。对不起。

11年太晚了…使用路径长

x="50%" y ="50%"不能在复杂的svg中工作

没有人提到pathLength(这里设为2,所以1是1 / 2)和textPath:

< img src = " https://i.imgur.com/8wuf7r7.png " alt = " / >

<svg viewbox="0 0 200 50">
<path id="P" pathLength="2" d="M0 25h200" stroke="blue"/>
<text>
<textPath href="#P"
startoffset="1" text-anchor="middle"
dominant-baseline="middle"
fill="green" font-size="14px">aligned in middle</textPath>
</text>
</svg>