手绘 SVG 条形图(包含一些 SVG 定位技巧)

Avatar of Robin Rendle
Robin Rendle

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

让我们看看在本周早些时候制作一个(看似)简单的条形图时,我了解到的一些关于在 SVG 中定位元素的知识。

在 SVG 中,你没有太多选择,只能定位元素。SVG 是一种声明式图形格式,而图形只不过是定位的绘图指令。不过,其中有很多陷阱和可能令人沮丧的情况,所以让我们开始吧。

这是我们想要构建的条形图

我们可以使用我们选择的图形编辑器创建它,并将其作为 标签(甚至作为 `.svg`)转储到我们的标记中,但这有什么乐趣呢?我还想手动制作这个 SVG 图表,因为我知道这样比仅仅导出 Sketch 或 Illustrator 文件能学到更多关于语法的知识。

为了开始,让我们创建一个 来容纳我们的子元素

<svg width='100%' height='65px'>

</svg>

现在让我们创建两个条形。第一个将位于背景中,第二个将位于其顶部,它将表示我们图形的数据

<svg width='100%' height='65px'>
  <g class='bars'>
    <rect fill='#3d5599' width='100%' height='25'></rect>;
    <rect fill='#cb4d3e' width='45%' height='25'></rect>
  </g>
</svg>

(如果没有为 提供 xy 属性,则假定它们为 0。)

在下面的演示中,我让它们进行了动画处理,这样您就可以看到第二个矩形放置在第一个矩形的顶部(这就像在 Sketch 中绘制两个矩形,一个叠加在另一个上面)

接下来,我们可以添加充当标记的线条,以便更轻松地读取 0、25、50、75 和 100% 的数据。我们只需创建一个新组并为每个标记添加一个矩形即可,对吧?当然,但您会发现我们很快就会遇到一个小问题。

在 SVG 内部,以及我们为图表数据设置样式的 下方,我们可以编写以下内容

<g class='markers'>
  <rect fill='red' x='50%' y='0' width='2' height='35'></rect>
</g>

这应该看起来像这样

不错!让我们添加所有其他标记,并在我们处理它们的同时修复颜色

<g class='markers'>
  <rect fill='#001f3f' x='0%' y='0' width='2' height='35'></rect>
  <rect fill='#001f3f' x='25%' y='0' width='2' height='35'></rect>
  <rect fill='#001f3f' x='50%' y='0' width='2' height='35'></rect>
  <rect fill='#001f3f' x='75%' y='0' width='2' height='35'></rect>
  <rect fill='#001f3f' x='100%' y='0' width='2' height='35'></rect>
</g>

我们为每个标记添加了一个 rect,添加了一个填充以设置颜色,并使用 x 属性对其进行定位。让我们看看它在浏览器中呈现的结果

最后一个去哪里了?好吧,我们确实告诉它定位在 100% 处,所以它实际上被定位到了屏幕右侧。我们需要考虑它的宽度并将其向左移动 2 个单位。我们可以通过多种方法解决此问题。

1) 我们可以应用内联变换将其移回

<rect fill='#001f3f' x='100%' y='0' width='2' height='35' transform="translate(-2, 0)"></rect>

2) 我们可以通过 CSS 应用相同的变换

rect:last-of-type {
  transform: translateX(-2px); /* Remember this isn't really "pixels", it's a length of 2 in the SVG coordinate system */
}

3) 或者我们可以不使用百分比,而是在 X 轴上的精确位置进行放置。由于 viewBox 属性,我们知道 SVG 的精确坐标系。在 Practical SVG 的第 6 章中,Chris 解释道

viewBoxsvg 的一个属性,用于确定坐标系和纵横比。四个值分别是 x、y、宽度和高度。

假设我们的 viewBox 是……

<svg viewBox='0 0 1000 65'>
  <!-- the rest of our svg code goes here -->
</svg>

所以它是 1000 个单位宽。我们的标记是 2 个单位宽。因此,要将最后一个标记放置在右边缘,我们将将其放置在 998 处!(1000 – 2)。这将成为我们的 x 属性

<svg viewBox='0 0 1000 65'>
  ...
  <rect fill='#001f3f' x='998' y='0' width='2' height='35'></rect>
  ...
</svg>

这将导致我们的标记定位在 SVG 的最右边缘,即使我们调整其大小也是如此

耶!我们不必在这里添加 % 或像素值,因为它使用的是由 viewBox 设置的坐标系。

在所有这些最终排序后,我们可以转到下一个问题:在每个标记下方添加 % 标签文本以表示 25、50% 等。为此,我们将在 内部创建一个新的 并添加我们的 元素

<g>
  <text fill='#0074d9' x='0' y='60'>0%</text>
  <text fill='#0074d9' x='25%' y='60'>25%</text>
  <text fill='#0074d9' x='50%' y='60'>50%</text>
  <text fill='#0074d9' x='75%' y='60'>75%</text>
  <text fill='#0074d9' x='100%' y='60'>100%</text>
</g>

因为我们手动执行此操作,所以我想为 x 值使用 %,但不幸的是,最终看起来会像这样

同样,我们遇到了最后一个元素没有按预期对齐的问题。中间的标签也放错了位置;理想情况下,它们应该居中在其标记下方。我以为我可能必须使用正确的 x 值将每个元素调整到适当位置,然后 Chris 指导我使用 text-anchor 属性,我以前从未听说过。

使用此属性,我们可以像 CSS 中的 text-align 属性一样操作文本。此属性是自然继承的,因此我们可以在 g 上设置一次,然后定位第一个和最后一个元素

<g text-anchor='middle'>
  <text text-anchor='start' fill='#0074d9' x='0' y='60'>0%</text>
  <text fill='#0074d9' x='25%' y='60'>25%</text>
  <text fill='#0074d9' x='50%' y='60'>50%</text>
  <text fill='#0074d9' x='75%' y='60'>75%</text>
  <text text-anchor='end' fill='#0074d9' x='100%' y='60'>100%</text>
</g>

这将看起来像

就是这样!通过了解 viewBox 的工作原理以及 xy 坐标和 text-anchor 等属性,我们几乎可以使用 SVG 内部的任何元素。

通过手动制作这些图表,感觉我们拥有很多控制权。不难想象我们如何可以发挥更大的设计控制权或使用 JavaScript 传输数据。

只需稍微多做一点工作,我们就可以为这些图表制作动画,使它们真正脱颖而出。例如,尝试将鼠标悬停在此图表版本上

很漂亮,对吧?而这一切都只是通过 SVG 和 CSS 实现的。如果您有兴趣了解更多信息,那么我之前写过一篇关于 如何使用 SVG 制作图表 的文章,其中更详细地解释了这些内容。

现在让我们去制作一些酷炫的图表吧!