【问题标题】:Is there a way to draw a rectangle around single letters on a SVG textpath有没有办法在 SVG 文本路径上的单个字母周围绘制一个矩形
【发布时间】:2014-03-29 21:13:04
【问题描述】:

是否可以在 SVG 文本路径的单个字母后面绘制矩形?我试过draw an image of what I am trying to do。 SVG 是 HTML 页面的一部分。似乎没有办法使用 CSS 获取文本路径,但也许有办法使用 javascript?

这是我的 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>

【问题讨论】:

  • @JohanVandenRym 什么?这是同一个问题的链接。这是其中一个递归笑话吗?
  • 到目前为止我认为没有 svg 解决方案
  • 不是重复的,这是一个更复杂的问题。

标签: javascript html css svg


【解决方案1】:

this... 怎么样?它使用 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>

【讨论】:

  • 谢谢!这不是我想要做的,但它很棒!
【解决方案2】:

您将需要 Javascript,但它不像前面的示例那样相当复杂。

所有文本元素都有一组相关的接口函数来定位每个字符:

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

最简单的方法是使用getExtentOfChar(i) 来查找每个字符字形的边界框矩形。这是@Robert Longson 的示例中使用的方法。没有所有额外的事件处理代码,它可以简化为:

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/

限制是边界框是包含字母在原始水平和垂直坐标内的最紧凑的矩形,而不是旋转的矩形,因此矩形比字母大并且重叠.

要计算出一个紧密边界矩形,您需要使用.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/

这次我使用&lt;path&gt; 而不是&lt;rect&gt;,使用相对坐标沿角色的基线绘制直线,然后在与该线成90 度的位置向上1em。这会将每个矩形的底部定位在您的文本路径上,但它不会覆盖字母的“降序”。

为此,我决定切换回&lt;rect&gt; 元素并使用变换来定位矩形会更容易。我将矩形平移到起点,根据.getRotationOfChar(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 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/

经过测试,所有接口方法均已在最新的 Chrome 和 Firefox 以及 IE11/10/9(通过开发者的模拟器)中按预期实现和工作。

【讨论】:

  • 这正是我所需要的,非常非常感谢!
  • 很好的答案。对 OP:检查它作为答案?
【解决方案3】:

如果你真的,真的想要这样做;).... 这可以通过使用带有 unicode 字符#96xx 系列的'背景' textPath 来完成。但是,要使其与父字符对齐,您必须开发一个表格来选择正确的字符和大小以匹配父字符。这将需要一点 javascript,对定义字符的边界框有一定的耐心,以及对文本对齐方式的理解。当然,不同的浏览器呈现它的方式会挑战你的理智。

嗯……我想如果你能创造出这个,你就会有一些值得吹嘘的东西。

以下是使用您的 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;'>
  &#9605;
  &#9605;
  &#9605;
  &#9605;
  &#9605;
  &#9605;
 &nbsp;
 &nbsp;
 &nbsp;
 &nbsp;
  &#9606;
  &#9606;
  &#9606;
  &#9606;
  &#9606;
  &#9606;
 &nbsp;
 &nbsp;
 &nbsp;
 &nbsp;

  &#9607;
  &#9607;
  &#9607;
  &#9607;
  &#9607;
  &#9607;
 &nbsp;
 &nbsp;
 &nbsp;
 &nbsp;

  &#9608;
  &#9608;
  &#9608;
  &#9608;
  &#9608;
  &#9608;
 &nbsp;
 &nbsp;
 &nbsp;
 &nbsp;
  &#9609;
  &#9609;
  &#9609;
  &#9609;
  &#9609;
  &#9609;
 &nbsp;
 &nbsp;
 &nbsp;
 &nbsp;

  &#9610;
  &#9610;
  &#9610;
  &#9610;
  &#9610;
 &nbsp;
 &nbsp;
 &nbsp;
 &nbsp;

  &#9611;
  &#9611;
  &#9611;
  &#9611;
  &#9611;
 &nbsp;
 &nbsp;
 &nbsp;
 &nbsp;

  &#9612;
  &#9612;
  &#9612;
  &#9612;
  &#9612;
</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">
    &#9607;&#9607;&#9607;&#9607;&#9607;  &#9607;&#9607;&#9607;&#9607;&#9607;
    </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>

【讨论】:

    猜你喜欢
    • 2013-04-16
    • 1970-01-01
    • 2013-10-16
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多