将文本适配到SVG路径中

11

我有一个绘制运动衫轮廓的SVG路径,并希望将球员的姓氏居中在衣服内。此外,由于我将各种长度的名称放入球衣中,我希望文本大小能够动态缩放以适合球衣。

我使用getBoundingClientRect()进行了一些数学计算来居中文本,但我正在解决文本的动态大小调整问题。起初看起来还好,但当我调整窗口大小时,文本大小并不与球衣大小成比例。

我在下面包含了一段代码片段以演示。请帮助我理解为什么会出现这种大小调整问题,以及我可以做些什么来实现正确的文本调整大小。

编辑:我现在拥有足够的声望点来添加图片(万岁!),因此以下是一些图片,用于演示我所指的调整大小问题。当我放大/缩小时,文本大小不会与球衣大小成比例。但是,如果我在放大/缩小后刷新页面,即以缩放版本开始,文本大小将重新调整为当前运动衫大小并适合运动衫。

原始大小 缩小 放大

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Jersey</title>
</head>

<body>
  <svg id="jersey">
    <switch>
      <g>
        <g id="jerseyShape">
          <path id="outline" fill="green" transform="translate(-40,0)" opacity="0.35" fill="none" stroke="#000000" stroke-width="1.5" d="M116.462,113.911V39.01
                           c0,0-18.493-5.977-15.317-30.633c0,0-8.033-2.616-8.78-3.363S91.617,9.311,79.29,9.124h-1.305
                           C65.656,9.311,65.656,4.268,64.909,5.015s-8.778,3.363-8.778,3.363C59.305,33.034,40.813,39.01,40.813,39.01v74.901
                           C40.813,113.911,74.434,126.427,116.462,113.911z" />
        </g>
        <g id="jerseyContent">
          <svg>
            <text id="name" font-family="Verdana" text-anchor="middle" dominant-baseline="central">
              Jordan
            </text>
            <!-- Dynamically re-size name to fit jersey -->
            <script type="application/ecmascript">
              //Get path's bounding rectangle
              var pathNode = document.getElementById("outline");
              var pRect = pathNode.getBoundingClientRect();

              //Get text's bounding rectangle
              var textNode = document.getElementById("name");
              var tRect = textNode.getBBox();

              //Calculate the necessary scale to make the text fit within the jersey width while
              //not exceeding a height of 30
              var widthRatio = (pRect.width * 0.85) / tRect.width;
              var heightRatio = (pRect.height*0.3) / tRect.height;
              var textScale = Math.min(widthRatio, heightRatio);

              //Translate text to center of jersey and scale to fit
              textNode.setAttribute("transform", "translate(" + (1 + pRect.width / 2) + " " + (pRect.top + pRect.height / 2) + ")scale(" + textScale + ")");
            </script>
          </svg>
        </g>
      </g>
    </switch>
  </svg>
</body>

</html>


我无法使用你的代码使得运动衫随着窗口大小调整而改变尺寸。 - Jon Edwards
1
什么浏览器?最新的Chrome/Windows看起来没问题。 - Michael Mullany
谢谢你提出这个问题,我很难为情地说我还没有测试过不同的浏览器(我对 web 开发还比较新!)。你是对的,在 Chrome 上我看不到问题,而且在 Firefox 上也看不到问题(尽管 Firefox 似乎有完全不同的对齐问题)。我只看了 Safari。我有点惊讶/担忧/沮丧地看到每个浏览器都有如此不同的行为-这必定会使以后的事情变得困难! - celts_fan822
2个回答

1
你还应该使用getBBox()来获取路径的大小。这样,即使进行缩放,尺寸也将在相同的坐标空间中。

谢谢Paul,我一定会深入了解getBoundingClientRect()和getBBox()之间的区别。但是对于网页缩放(即ctrl+或ctrl-),这两个函数似乎都不能给我正确的缩放行为,即文本不会与球衣成比例地增长。看起来问题在于这些函数不会在网页缩放后重新计算,因为当我缩放然后刷新页面时,一切似乎都被重新计算,缩放效果很完美。这些函数能否在网页缩放后重新计算边界框? - celts_fan822

0
问题是因为您正在计算包含文本节点的组的比例。
我通过在具有id =“full”的整个g元素上提供比例来解决了您的问题。
现在,您可以更改 id =“full”上的比例值,并获得所需的结果,而无需标签超出范围。 我已更新代码,以考虑缩放时的比例值(ctrl + / ctrl-)。

$(window).on("resize", function(){ 
  var scale = "scale("+window.devicePixelRatio+")"
  $("#full").attr("transform", scale)
  console.log($("#full").attr("transform"));
  
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="utf-8">
      <title>Jersey</title>
    </head>
    
    <body style="width:100%; height:100%">
      <svg style="    height: 602px;    width: 100%;">
          <g id="full" transform="scale(1)">
            <g id="jerseyShape" transform="translate(-40,0)">
              <path id="outline" fill="green" opacity="0.35" fill="none" stroke="#000000" stroke-width="1.5" d="M116.462,113.911V39.01
                               c0,0-18.493-5.977-15.317-30.633c0,0-8.033-2.616-8.78-3.363S91.617,9.311,79.29,9.124h-1.305
                               C65.656,9.311,65.656,4.268,64.909,5.015s-8.778,3.363-8.778,3.363C59.305,33.034,40.813,39.01,40.813,39.01v74.901
                               C40.813,113.911,74.434,126.427,116.462,113.911z" />
                <text id="name" transform="translate(80,80)" font-family="Verdana" text-anchor="middle" dominant-baseline="central">
                  Jordan
                </text>                           
            </g>
          </g>
      </svg>
    </body>
    
    </html>


谢谢你的回复 Cyril。但是,当我运行代码片段时,似乎还是有与之前一样的文本调整大小问题 -- 当我放大时,文本会溢出球衣的边缘。我错过了什么吗?另外,比例值2.7是从哪里来的? - celts_fan822
哦,现在我明白你在这里的意思了。2.7当然是任意的,通过改变该值,球衣和文本将一起缩放。感谢您指出这一点,这对我也有帮助,但并没有解决我的原始问题。我的意思是,如果我放大页面(例如,通过ctrl+或ctrl-实际缩放网页,而不仅仅是缩放组),则文本不会与球衣成比例地增长/缩小。(此外,我故意包含了文本节点的缩放逻辑,因为名称可以是任意长度,我需要文本高度缩小,以使完整名称适合球衣内。) - celts_fan822
是的,正确的,2.7只是一个任意数字,您可以更改它。 好的,您可以监听缩放事件并相应地计算比例尺。这应该可以解决您的问题。 - Cyril Cherian
1
好的,我已经更新了我的代码。 为了解决ctrl +和ctrl-的问题。 我相信现在它会解决你所有的疑虑 :) - Cyril Cherian
1
感谢更新!Michael Mullany 在我的原始问题下发表的一条评论让我意识到我的问题实际上是针对 Safari 的,所以你的代码在 Chrome 和 Firefox 中运行得很好,但如果你恰好安装了 Safari,则可能仍会观察到文本缩放行为异常。我意识到跨浏览器支持将是一个巨大的挑战,也许最好的方式是我将来只专注于 Chrome 和 Firefox... - celts_fan822
1
哦,好的,在Safari中的问题是window.devicePixelRatio始终为1,因此缩放不起作用...:(需要特殊处理才能在Safari上获取像素比,这样就可以解决了。 - Cyril Cherian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接