让我们看看在本周早些时候制作一个(看似)简单的条形图时,我了解到的一些关于在 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>
(如果没有为 提供
x
和 y
属性,则假定它们为 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 解释道
viewBox
是svg
的一个属性,用于确定坐标系和纵横比。四个值分别是 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
的工作原理以及 x
、y
坐标和 text-anchor
等属性,我们几乎可以使用 SVG 内部的任何元素。
通过手动制作这些图表,感觉我们拥有很多控制权。不难想象我们如何可以发挥更大的设计控制权或使用 JavaScript 传输数据。
只需稍微多做一点工作,我们就可以为这些图表制作动画,使它们真正脱颖而出。例如,尝试将鼠标悬停在此图表版本上
很漂亮,对吧?而这一切都只是通过 SVG 和 CSS 实现的。如果您有兴趣了解更多信息,那么我之前写过一篇关于 如何使用 SVG 制作图表 的文章,其中更详细地解释了这些内容。
现在让我们去制作一些酷炫的图表吧!
我想我也尝试一下!
查看 Chris Coyier 在 CodePen 上创作的笔 MjNapx(@chriscoyier)。
我想尝试三件事
preserveAspectRatio="none"
的嵌套 SVG)<line>
作为标记而不是矩形我还想在这些线条上使用
vector-effect: non-scaling-stroke;
,但我无法在流体环境中弄清楚。意思是您在
viewBox
中使用的固定偏移不再与笔划宽度匹配?简单的解决方法是不使用固定偏移位置。在 0% / 100% 处精确绘制线条,并让 SVG 溢出,使用(非缩放)CSS 填充腾出空间http://codepen.io/AmeliaBR/pen/a0a4b796d92d4acb9f76e6c316e66b8e/
但当然,如果您不希望 SVG 笔划进行缩放,或者不希望保留纵横比,并且由于高度是固定的并且所有水平位置都使用百分比,因此您根本不需要
viewBox
http://codepen.io/AmeliaBR/pen/c9ec3eb3d0b35481a74d86c0321d073e/
做得好!那个 text-anchor 技巧非常巧妙。因此,我将继续制作一些漂亮的条形图 :)