[英]Is there a way to draw a rectangle around single letters on a SVG textpath
Is there a possiblity to draw rectangles behind single letters of a SVG textpath? 是否有可能在SVG文本路径的单个字母后面绘制矩形? I have tried to draw an image of what I am trying to do .
我试图画出我想要做的事情 。 The SVG is part of a HTML Page.
SVG是HTML页面的一部分。 There seems to be no way to get at the textpath with CSS, but maybe there is a way with javascript?
似乎没有办法用CSS获取文本路径,但也许有一种方法与JavaScript?
This is my html/svg code: 这是我的html / svg代码:
<svg id="foo" width="100%" height="100%" viewBox="-30 -220 1000 800"
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" />
</defs>
<use xlink:href="#MyPath" fill="none" stroke="black" />
<text font-family="arial" font-size="140" >
<textPath xlink:href="#MyPath" startOffset="20" dy="10">
Lorem ipsum
</textPath>
</text>
</svg>
How about this... It uses the SVG DOM to get the character boxes and then draws a rectangle behind the character under the mouse. 这个怎么样......它使用SVG DOM获取字符框,然后在鼠标下面的字符后面绘制一个矩形。
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width='400' height='400'>
<script><![CDATA[
function details(evt) {
var letters='Move mouse over letters...';
var pathLetters='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var svgdoc=evt.target.ownerDocument;
var xm=evt.clientX;
var ym=evt.clientY;
var text=svgdoc.getElementById('text');
var d=text.getStartPositionOfChar(1);
d.x=xm;
d.y=ym;
var p=text.getCharNumAtPosition(d);
if (p >= 0) {
var f=text.getExtentOfChar(p);
var node=document.createTextNode('You are on character '+letters.substring(p,p+1));
var letter=svgdoc.getElementById('letter');
letter.replaceChild(node,letter.firstChild);
var outline=svgdoc.getElementById('outline');
outline.setAttribute('x',f.x);
outline.setAttribute('y',f.y);
outline.setAttribute('width',f.width);
outline.setAttribute('height',f.height)
}
var textPath=svgdoc.getElementById('textPath');
p=textPath.getCharNumAtPosition(d);
if (p >= 0) {
var f=textPath.getExtentOfChar(p);
var node=document.createTextNode('You are on character '+pathLetters.substring(p,p+1));
var letter=svgdoc.getElementById('letter');
letter.replaceChild(node,letter.firstChild);
var outline=svgdoc.getElementById('outline');
outline.setAttribute('x',f.x);
outline.setAttribute('y',f.y);
outline.setAttribute('width',f.width);
outline.setAttribute('height',f.height)
}
}
function zero(evt)
{
var svgdoc=evt.target.ownerDocument;
var outline=svgdoc.getElementById('outline');
outline.setAttribute('x',0);
outline.setAttribute('y',0);
outline.setAttribute('width',0);
outline.setAttribute('height',0);
var letter=svgdoc.getElementById('letter');
node=document.createTextNode('You are on character ');
letter.replaceChild(node,letter.firstChild);
}
]]></script>
<defs>
<path id="s3" d="M 10,200 Q 100,125 200,180 Q 340,260 400,140" />
</defs>
<rect id='outline' x='0' y='0' width='0' height='0' style='stroke:green;fill:yellow'/>
<g>
<text onmousemove='details(evt)' onmouseout='zero(evt)' id='text' x='200' y='100' style='text-anchor:middle;font-size:24pt;font-family:Arial;fill:red'>Move mouse over letters...</text>
<text style='font-size:20pt;font-family:Arial;fill:red'><textPath onmousemove='details(evt)' onmouseout='zero(evt)' id='textPath' xlink:href="#s3">ABCDEFGHIJKLMNOPQRSTUVWXYZ</textPath>
</text>
<text id='letter' x='20' y='250' style='text-anchor:start;font-size:16pt;fill:blue'>You are on character </text>
</g>
</svg>
You'll need Javascript, but it's not quite as complicated as the previous examples. 你需要使用Javascript,但那样复杂前面的例子中它不是相当 。
There are a set of relevant interface functions on all text elements to locate each character: 所有文本元素都有一组相关的接口函数来定位每个字符:
http://www.w3.org/TR/SVG11/text.html#InterfaceSVGTextContentElement http://www.w3.org/TR/SVG11/text.html#InterfaceSVGTextContentElement
The simplest approach is to use getExtentOfChar(i)
to find the bounding-box rectangle for each character glyph. 最简单的方法是使用
getExtentOfChar(i)
查找每个字符字形的边界框矩形。 This is the approach used in @Robert Longson's example. 这是@Robert Longson的例子中使用的方法。 Without all the extra event-handling code, it can be simplified to:
没有所有额外的事件处理代码,它可以简化为:
var texts = document.getElementsByClassName("backgroundRect");
var svgNS ="http://www.w3.org/2000/svg";
for (var i=0, max= texts.length; i<max; i++) {
var t = texts[i];
var g = document.createElementNS(svgNS, "g");
for (var j=0, nchar=t.getNumberOfChars(); j<nchar; j++) {
var r = t.getExtentOfChar(j);
var re = document.createElementNS(svgNS, "rect");
re.setAttribute("width", r.width);
re.setAttribute("height", r.height);
re.setAttribute("x", r.x);
re.setAttribute("y", r.y);
g.insertBefore(re, null);
}
t.parentNode.insertBefore(g, t);
}
http://fiddle.jshell.net/T5qWb/1/ http://fiddle.jshell.net/T5qWb/1/
The limitation is that the bounding box is the tightest rectangle that will contain the letter within the original horizontal and vertical coordinates , not a rotated rectangle, and so the rectangles are larger than the letters and overlapping. 限制是边界框是最紧密的矩形,它将包含原始水平和垂直坐标内的字母,而不是旋转的矩形,因此矩形大于字母和重叠。
To figure out a tight-bounding rectangle, you'll need to use .getStartPositionOfChar(i)
, .getEndPositionOfChar(i)
, and some geometry: 要想出一个紧
.getStartPositionOfChar(i)
矩形,你需要使用.getStartPositionOfChar(i)
.getEndPositionOfChar(i)
和一些几何:
var texts = document.getElementsByClassName("backgroundRect");
var svgNS ="http://www.w3.org/2000/svg";
for (var i=0, max= texts.length; i<max; i++) {
var t = texts[i];
var g = document.createElementNS(svgNS, "g");
g.setAttribute("class", "textBackground");
for (var j=0, nchar=t.getNumberOfChars(); j<nchar; j++) {
var p = document.createElementNS(svgNS, "path");
var start = t.getStartPositionOfChar(j),
end = t.getEndPositionOfChar(j),
height = parseFloat(getComputedStyle(t)["fontSize"]),
vector = [(end.x - start.x), (end.y - start.y)],
aspect = height /
Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1]),
normal = [vector[1]*aspect, -vector[0]*aspect];
var d = ["M", [start.x, start.y],
"l", normal, vector, [-normal[0], -normal[1]],
"z"
].join(" ");
p.setAttribute("d", d);
g.insertBefore(p, null);
}
t.parentNode.insertBefore(g, t);
}
http://fiddle.jshell.net/T5qWb/2/ http://fiddle.jshell.net/T5qWb/2/
I'm using a <path>
instead of a <rect>
this time, using relative coordinates to draw straight lines along the baseline of the character and then 1em up at 90degrees to that line. 我这次使用的是
<path>
而不是<rect>
,使用相对坐标沿着角色的基线绘制直线,然后在90度的距离内向上行1em。 This positions each rectangle's base on your text path, but it doesn't cover the "descenders" of the letters. 这会将每个矩形的基础定位在文本路径上,但它不会覆盖字母的“下降”。
To do that, I decided it would be easier to switch back to <rect>
elements, and use transforms to position the rectangles. 为此,我决定切换回
<rect>
元素更容易,并使用变换来定位矩形。 I translate the rectangle to the start point, rotate it based on .getRotationOfChar(i)
, and then translate it away from the baseline. 我将矩形转换为起点,基于
.getRotationOfChar(i)
旋转它,然后将其从基线转换出来。 The only limitation is that I had to hard-code in an estimate of what proportion of the character's height should be below the baseline, as I couldn't figure out any method to calculate that for a given font. 唯一的限制是我必须硬编码估计角色高度应该低于基线的比例,因为我无法找出任何计算给定字体的方法。
var texts = document.getElementsByClassName("backgroundRect");
var svgNS ="http://www.w3.org/2000/svg";
for (var i=0, max= texts.length; i<max; i++) {
var t = texts[i];
var g = document.createElementNS(svgNS, "g");
g.setAttribute("class", "textBackground");
for (var j=0, nchar=t.getNumberOfChars(); j<nchar; j++) {
var re = document.createElementNS(svgNS, "rect");
var start = t.getStartPositionOfChar(j),
end = t.getEndPositionOfChar(j),
angle = t.getRotationOfChar(j),
height = parseFloat(getComputedStyle(t)["fontSize"]),
vector = [(end.x - start.x), (end.y - start.y)],
width = Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1]),
aspect = height / width,
normal = [vector[1]*aspect, -vector[0]*aspect],
baseline = 0.2;
re.setAttribute("height", height);
re.setAttribute("width", width);
re.setAttribute("transform",
["translate(", [start.x, start.y], ")",
"rotate(", angle, ")",
"translate(0", -height*(1-baseline), ")"
].join(" ")
);
g.insertBefore(re, null);
}
t.parentNode.insertBefore(g, t);
}
http://fiddle.jshell.net/T5qWb/3/ http://fiddle.jshell.net/T5qWb/3/
Tested and all the interface methods are implemented and working as expected in the latest Chrome & Firefox, and IE11/10/9 (via the developer's emulator). 测试和所有界面方法在最新的Chrome和Firefox以及IE11 / 10/9(通过开发人员的模拟器)中实现并按预期工作。
If you truly, truly want to do this;).... It can accomplished by using a 'background' textPath with unicode character #96xx series. 如果你真的,真的想要这样做;)....它可以通过使用带有unicode字符#96xx系列的'background'textPath来实现。 However, to get this to align with the parent characters, you would have to develop a table that would choose the correct character and size to match the parent.
但是,要使其与父字符对齐,您必须开发一个表,该表将选择与父字符匹配的正确字符和大小。 This would require a bit of javascript, some patience with defining bounding boxes for the characters, and a understanding of a text alignments.
这需要一些javascript,一些耐心定义角色的边界框,以及理解文本对齐。 And, of course, the way different browsers render it will challenge your sanity.
当然,不同浏览器呈现它的方式将挑战你的理智。
Hmm... I guess if you can create this, you will have something to brag about. 嗯......我想如果你能创造这个,你会有一些东西要吹嘘。
Below is a rough example of the concept using your textPath. 下面是使用textPath的概念的粗略示例。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Unicode textPath character background</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>Unicode characters as textPath character background</h4>
#96xx series
<div style='color:blue;width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
▅
▅
▅
▅
▅
▅
▆
▆
▆
▆
▆
▆
▇
▇
▇
▇
▇
▇
█
█
█
█
█
█
▉
▉
▉
▉
▉
▉
▊
▊
▊
▊
▊
▋
▋
▋
▋
▋
▌
▌
▌
▌
▌
</div>
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;'>
<svg id="foo" width="100%" height="100%" viewBox="-30 -220 1000 800"
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" />
</defs>
<use xlink:href="#MyPath" fill="none" stroke="black" />
<text fill="blue" font-size="140" >
<textPath xlink:href="#MyPath" startOffset="20" dy="10">
▇▇▇▇▇ ▇▇▇▇▇
</textPath>
</text>
<text font-family="arial" font-size="140" >
<textPath xlink:href="#MyPath" startOffset="20" dy="10">
Lorem ipsum
</textPath>
</text>
</svg>
</div>
</center>
</body>
</html>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.