简单可扩展的SVG图表与笛卡尔坐标系

7

我对SVG完全不了解,请耐心等待我的翻译。我阅读了很多关于此主题的文章,每个人都指向像d3.js这样的解决方案,但在我看来,这对于我所需的简单任务来说过于复杂。

我需要制作一个带有笛卡尔坐标系的图表,其中(0,0)位于左下角。它需要具有宽度、高度和数据,以百分比表示,因此所有内容都可以随页面缩放。

所以,这是我的代码(为了简单起见,只有部分图表):

<style>
 .grid {stroke: white; stroke-width: 1; stroke-dasharray: 1 2}
 .label{font-family: courier new; fill: white; font-size: 14px}
 .data {stroke: white; stroke-width: 1}
</style>

<svg width="100%" height="100%">
 <g class="x grid">
  <line x1="0%"   x2="0%"   y1="80%" y2="100%"></line>
  <line x1="10%"  x2="10%"  y1="80%" y2="100%"></line>
  <line x1="20%"  x2="20%"  y1="80%" y2="100%"></line>
 </g>
 <g class="y grid">
  <line x1="0%" x2="20%" y1="80%"  y2="80%" ></line>
  <line x1="0%" x2="20%" y1="90%"  y2="90%" ></line>
  <line x1="0%" x2="20%" y1="100%" y2="100%"></line>
 </g>
 <g class="x label">
  <text x="10%"  y="100%"> 1 minute </text>
  <text x="20%"  y="100%"> 2 minutes</text>
 </g>
 <g class="y label">
  <text x="0%" y="80%"> 20% </text>
  <text x="0%" y="90%"> 10% </text>
 </g>
 <g class="data">
  <line x1="0%"  x2="10%"  y1="85%"  y2="92%"  ></line>
  <line x1="10%" x2="20%"  y1="92%"  y2="88%"  ></line>
 </g>
</svg>

我想使用polygonpath来处理数据,这样我就可以填充曲线下面的区域,但是它不支持以百分比为值。有人建议使用viewbox将百分比转换为像素,然后再使用像素,但这会破坏我的网格。我还希望在左下角有一个(0,0)点,这样我的CGI就不必对需要显示的所有点进行计算了。我尝试过transform="translate(0,100) scale(1,-1)",但它不支持百分比。我还尝试过transform="rotate(270)",但当你缩小窗口宽度时,图形高度也会减小...

所以,有人能够帮助我启动这个项目吗?帮我设置一个流体的、可调整大小的图形,原点位于左下角,并且曲线下方有着不同的颜色区域?

1个回答

12

你需要使用viewBox,因为如你所发现,转换组件不接受百分比。使用viewBox后,你仍然可以使用百分比来指定坐标。但是你需要选择一个aspect ratio类似于最终图形的viewBox。否则,页面上的对象可能会被压缩或拉伸得不理想。

<svg width="100%" height="100%" viewBox="0 0 500 500">
  <g id="cartesian" transform="translate(0,500) scale(1,-1)">
    <g class="data">
      <line x1="0%"  y1="75%" x2="50%"  y2="40%" ></line>
      <line x1="50%" y1="40%" x2="100%" y2="60%"></line>
    </g>
  </g>
</svg>

不幸的是,翻转坐标系统会产生副作用。它会翻转所有对象,包括文本,如果我们添加一些文本,您可以看到这一点:

<svg width="100%" height="100%" viewBox="0 0 500 500">
  <g id="cartesian" transform="translate(0,500) scale(1,-1)">
    <g class="data">
      <line x1="0%"  y1="75%" x2="50%"  y2="40%" ></line>
      <line x1="50%" y1="40%" x2="100%" y2="60%"></line>
    </g>
    <g class="y label">
      <text x="0%" y="50%"> 10% </text>
      <text x="0%" y="90%"> 20% </text>
    </g>
  </g>
</svg>

这里有演示

因此,您需要通过将文字翻转回正确的方向来解决这个问题。

<svg width="100%" height="100%" viewBox="0 0 500 500">
  <g id="cartesian" transform="translate(0,500) scale(1,-1)">
    <g class="data">
      <line x1="0%"  y1="75%" x2="50%"  y2="40%" ></line>
      <line x1="50%" y1="40%" x2="100%" y2="60%"></line>
    </g>
    <g class="y label">
      <text x="0%" y="50%" font-size="16"
            transform="translate(0,500) scale(1,-1)"> 10% </text>
      <text x="0%" y="90%" font-size="16"
            transform="translate(0,900) scale(1,-1)"> 20% </text>
    </g>
  </g>
</svg>

很不幸,正如您所看到的,这会影响我们使用百分比坐标来清晰地定位标签的能力。如果我们想在<text>元素上使用百分比坐标,则必须为每个标签调整变换。

解决此问题的最佳方法可能是将所有标签放在<defs>中,并使用<use>引用它们。这样,我们就可以将它们翻转到正确的方向并使用百分比坐标进行定位。

<svg width="100%" height="100%" viewBox="0 0 500 500">
  <defs>
    <text id="label1" font-size="16" transform="scale(1,-1)"> 10% </text>
    <text id="label2" font-size="16" transform="scale(1,-1)"> 20% </text>
  </defs>
  <g id="cartesian" transform="translate(0,500) scale(1,-1)">
    <g class="data">
      <line x1="0%"  y1="75%" x2="50%"  y2="40%" ></line>
      <line x1="50%" y1="40%" x2="100%" y2="60%"></line>
    </g>
    <g class="y label">
        <use xlink:href="#label1" x="0%" y="50%"/>
        <use xlink:href="#label2" x="0%" y="90%"/>
    </g>
  </g>
</svg>

演示在这里


谢谢,你的回答非常有帮助。我发现,如果网格和标签是静态的,它们不需要在笛卡尔 g 中,这样可以让代码更简单、更清晰。但还有一件事情我想要解决,但我不知道该怎么做。我不需要保持长宽比例。我想让图表填满页面(或父级 div)并且具有弹性。这能用 viewport 实现吗? - Ulrik
是的。使用 <svg width="100%" height="100%" preserveAspectRatio="none"> - Paul LeBeau
快要完成了(我一直在尝试自己完成,但似乎还需要最后一块拼图)- 有没有办法让标签保持比例? - Ulrik
很遗憾,你需要确保父容器的纵横比与 SVG 的纵横比不要相差太大。 - Paul LeBeau

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