保持SVG文本的纵横比

9

这是https://stackoverflow.com/questions/29105120/preserve-aspect-ratio-for-svg-text-and-react-to-javascript-touch-events的编辑副本,我将删除它,因为它提出了两个相关但技术上不同的问题。

正如我在上一个问题中已经解释的那样,我正在尝试制作一个导航div,其中包括4个按钮,一个向左,一个向右,另一个向下,还有一个向上。此外,中间需要有一个确定按钮。

这个解释非常好,可以使用CSS和HTML5创建导航按钮,如https://www.w3schools.com/howto/howto_css_buttons.asp所述。

我创建了SVG:

<div class="function height3x svg-container" style="height: 112px; width: 200px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="mySVG" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none" style="background-color: whitesmoke">
    <g class="function" id="keyboard_btn_24">
        <polygon id="ok" points="25,25 75,25 75,75 25,75"></polygon>
        <text id="ok_text" x="39" y="55">OK</text>
    </g>
    <g class="function" id="keyboard_btn_25">
        <polygon id="up" stroke="black" stroke-width="0.1" points="0,0 100,0 65,35 35,35"></polygon>
        <text x="42" y="20"></text>
    </g>
    <g class="function" id="keyboard_btn_26">
        <polygon id="right" stroke="black" stroke-width="0.1" points="100,0 100,100 65,65 65,35"></polygon>
        <text x="81" y="53"></text>
    </g>
    <g class="function" id="keyboard_btn_27">
        <polygon id="down" stroke="black" stroke-width="0.1" points="0,100 35,65 65,65 100,100"></polygon>
        <text x="42" y="91"></text>
    </g>
    <g class="function" id="keyboard_btn_28">
        <polygon id="left" stroke="black" stroke-width="0.1" points="0,0 35,35 35,65 0,100"></polygon>
        <text x="5" y="53"></text>
    </g>
</svg>
</div>

但我有两个问题似乎无法解决。
首先:虽然我希望SVG具有响应性,但我不想在保持字体纵横比的情况下缩放文本。

Text that was scaled vs. non-scaled

我已经尝试过(不成功的)preserveAspectRatio,它似乎对文本没有什么作用。

问题:如何使<text>标签保持其纵横比,同时改变SVG的纵横比?

您可以查看和编辑最小示例:jsFiddle

Paulie_D-评论了我的旧问题:

至于纵横比,您应该删除宽度和高度100%的值。它们并不是真正需要的。 SVG将根据div大小缩放到所需大小。-jsfiddle.net/2qqrL7ng/1–

这不是一个选项,因为SVG元素需要响应无法保持纵横比的大小更改。只有文本需要保持比例,其他所有内容都应尽可能具有响应性。

编辑

将perserveAspectRatio的svg参数从“none”切换到“xMidYMid”可以保持SVG的纵横比,但期望的效果是SVG本身不保持其纵横比,而是-tags保持。这意味着以下内容是解决方案:
<div class="function height3x svg-container" style="height: 112px; width: 200px;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="mySVG" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style="background-color: whitesmoke">
        <g class="function" id="keyboard_btn_24">
            <polygon id="ok" points="25,25 75,25 75,75 25,75"></polygon>
            <text id="ok_text" x="39" y="55">OK</text>
        </g>
        <g class="function" id="keyboard_btn_25" preserveAspectRatio="xMidYMid">
            <polygon id="up" stroke="black" stroke-width="0.1" points="0,0 100,0 65,35 35,35"></polygon>
            <text x="42" y="20"></text>
        </g>
        <g class="function" id="keyboard_btn_26">
            <polygon id="right" stroke="black" stroke-width="0.1" points="100,0 100,100 65,65 65,35"></polygon>
            <text x="81" y="53"></text>
        </g>
        <g class="function" id="keyboard_btn_27">
            <polygon id="down" stroke="black" stroke-width="0.1" points="0,100 35,65 65,65 100,100"></polygon>
            <text x="42" y="91"></text>
        </g>
        <g class="function" id="keyboard_btn_28">
            <polygon id="left" stroke="black" stroke-width="0.1" points="0,0 35,35 35,65 0,100"></polygon>
            <text x="5" y="53"></text>
        </g>
    </svg>
</div>

/编辑

提前致谢。


preserveAspectRatio="xMidYMid" 对我有效,但“不做太多”是什么意思? - Robert Longson
你不能在<g>元素上使用preserveAspectRatio="xMidYMid"。你想要做到的唯一方法是在javascript中计算纵横比并对文本元素应用反向变换。 - Robert Longson
或者尝试在<div>中使用两个单独的SVG子元素,一个带有文本,其preserveAspectRatio="xMidYMid",另一个则没有。 - Robert Longson
@RobertLongson,我该如何在JS中自己保持纵横比?您能在答案中概述一下吗? - Sebastian van Wickern
钩入调整大小事件,获取宽度和高度并进行除法运算。尝试一下,如果卡住了我会帮忙的。 - Robert Longson
显示剩余3条评论
3个回答

11

我知道这是一个老问题,但我找到了一种在纯SVG中实现所需功能的方法,无需JS,并且我已经在生产环境中使用它而没有出现任何问题!

诀窍是定义一个没有viewBox的父级<svg>元素,以便它采用容器的尺寸,然后为您想要的不同类型的preserveAspectRatio值定义子<svg>元素!

在以下代码段中,我只是将问题中使用的SVG放在一个<svg viewBox="0 0 100 100" preserveAspectRatio="none">中,将所有<polygon>分开。

div {
  height: 112px;
  width: 200px;
}

div > svg {
  height: 100%;
  width: 100%;
  background-color: whitesmoke;
}

polygon {
  fill: none;
  stroke: black;
  stroke-width: 0.1;
}
<div>
  
  <!-- SVG wrapper without viewBox to take parent's dimensions -->
  <svg>
  
    <!-- sub SVG that will be resized -->
    <svg viewBox="0 0 100 100" preserveAspectRatio="none">
      <g>
        <polygon points="0,0 100,0 65,35 35,35"></polygon>
        <text x="42" y="20"></text>
      </g>
      <g>
        <polygon points="100,0 100,100 65,65 65,35"></polygon>
        <text x="81" y="53"></text>
      </g>
      <g>
        <polygon points="0,100 35,65 65,65 100,100"></polygon>
        <text x="42" y="91"></text>
      </g>
      <g>
        <polygon points="0,0 35,35 35,65 0,100"></polygon>
        <text x="5" y="53"></text>
      </g>
    </svg>
    
    <!-- regular SVG elements that won't be resized -->
    <text id="ok_text" 
          x="50%" y="50%" 
          text-anchor="middle" 
          alignment-baseline="middle">
      OK
    </text>
    
  </svg>
</div>

PS:最终结果甚至可以用作常规图像的src <img src="./my-weird-responsive-svg.svg" />,这使得这个技巧非常健壮!


5
问题:如何使标签保持其纵横比,同时改变svg的纵横比?
您不能这样做。如果文本是SVG的一部分,则会随着SVG进行缩放。无法使SVG的一部分免于缩放。
可能的解决方案:
(1) 从SVG中删除文本,并将其定位在顶部。例如,在HTML中使用一个定位的<div>或其他东西。
(2) 使用JS计算SVG的纵横比,并对<text>元素应用反向缩放变换。

我在它之前添加了一个div,并用js编写了定位逻辑。谢谢你的回答。 - Sebastian van Wickern
@S.vanWickern:你能否请发一下你的解决方案,它对你起作用了吗? - Prateek
如果你在svg外面套一个div,你的情况就很简单了,因为你只需要让“OK”文本居中。你可以使用CSS定位它,不需要使用JS。 - wizbcn
我认为我实际上找到了一种以纯 SVG 方式进行的清晰方式。随时欢迎查看我的回答 ;) - Sheraff

0
如果您可以控制当前尺寸的Javascript(例如全屏显示,或者仅仅是检查用户正在使用的视口大小),则可以在SVG之外的父级div中添加一个简单的类。
然后,为两种情况(当父div具有该类时(它告诉您svg已经缩放),以及当父级div没有这样的类时(即SVG未缩放))设置SVG文本的字体大小。 如果这是您的情况,则此简单解决方案有效。
.svg_text {font-size: 12px;} /* normal non-scaled SVG */
.case_for_big_screen_thus_SVG_scaled .svg_text {font-size: 6px;} /* this parent class only is added when viewport dimensions make your SVG to be scaled */

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