Skip to content

Latest commit

 

History

History
331 lines (254 loc) · 14.3 KB

10-the-svg-primer.md

File metadata and controls

331 lines (254 loc) · 14.3 KB
layout title permalink
default
SVG初步
the-svg-primer.html

更新时间: 2012-12-30


D3最擅长于生成和操纵SVG图像来进行可视化。绘制div或其它原生HTML元素也是可以的,但是考虑到不同浏览器之间总会存在一些标准上的差异,使用SVG会更可靠更稳定,也会更快一些。

类似于Illustrator的矢量绘图软件可以生成SVG文件,但是我们这里讨论的是如何用代码来生成。

SVG元素

可扩展矢量图形(Scalable Vector Graphics, SVG)是一种基于文本的图像格式。每个SVG图像都由类似于HTML的标签代码来定义。SVG代码可以直接用于HTML文档中。每个网页浏览器都是支持SVG的(除了IE8及之前的版本)。SVG是基于XML的,因此你会发现没有结束标签的元素必须是自封闭的。比如

{% highlight xml %} {% endhighlight %}

在你开始画之前,你必须先生成一个初始的SVG元素。将此元素视为一个画布,你将要在此画布上大展身手(从这个意义看,SVG的这个元素类似于HTML的canvas元素)。即使在一个最小的示例中,你最好也指定一下widthheight。如果你没有指定宽度和高度,它会占用svg所在元素中的尽可能多的空间。

{% highlight html %} {% endhighlight %}

下面给出效果图

没看到? 尝试在空白位置点击右键,再选择"Inspect Element",你的浏览器应该得到类似于下面的结果

注意,这里确实有一个svg元素,而它刚好就占用了宽为500像素高为50像素的一个区域。只不过,空白的图像确实没什么好看的。

还需注意的是,浏览器默认像素为数值单位。我们用的是50050,而无需用500px50px。当然你也可以显式地指定px,或任意其它浏览器支持的单位,比如em, pt, in, cmmm

简单形状

svg标签中,你可以使用大量的图形元素,包括rectcircleellipse, linetextpath

如果你熟悉计算机图形编程,你会了解通常的基于像素的坐标系统的原点0,0位于绘制区域的左上角。增加x值会导致右移,增加y值会导致下移。

0,0 100,20 200,40

rect元素会画出一个矩形。可以用xy来指定它的左上角的位置,而用widthheight指定其大小。下面的这个矩形填充了我们的整个SVG画布。

{% highlight html %} {% endhighlight %}

circle元素会画出一个圆。可以用cxcy来指定圆心位置,而用r来指定其半径。下面的这个圆位于我们的500个像素宽的画布的中心,所以它的cx值为250。

{% highlight html %} {% endhighlight %}

ellipse元素是类似的,只不过它在每个轴上的半径可以不一样。因此,半径r变成了rxry

{% highlight html %} {% endhighlight %}

line元素用来画一条线段。它使用x1y1指定线段的一个端点位置,而用x2y2来指定另外一端的位置。笔画stroke的颜色也必须指定,不然就混在背景里看不见了。

{% highlight html %} {% endhighlight %}

text用于渲染文本。它使用x指定文本左边界的位置,用y来指定基线(baseline)的垂直坐标。

{% highlight html %} Easy-peasy {% endhighlight %} Easy-peasy

除非显式地指定(后面马上会详细介绍如何指定文本样式),text会继承父元素中的CSS指定的字体样式。注意,上面例子中的文本与周围的段落的格式是保持一致的。我们可以用下面的代码修改其格式。

{% highlight html %} Easy-peasy {% endhighlight %} Easy-peasy

另外,需要提醒的是,任意到达SVG画布边界的图形元素都会被裁剪掉。所以,使用text时要小心,确保里面的字符不要被裁掉。如果将基线坐标(y)修改为50,也就是SVG的高度,你就能体会到这一点了。

{% highlight html %} Easy-peasy {% endhighlight %} Easy-peasy

path用于绘制更复杂的形状(比如地图中的国境线),将来我会单独介绍。但现在,我们只讨论简单形状。

SVG元素的样式

SVG的默认样式是黑色填充而不画线。如果你想修改样式,你需要将样式应用到元素上。常用的SVG属性有:

  • fill --- 一个颜色值。和CSS一样,颜色可以有几种指定方式
    • 颜色名称。比如orange
    • 十六进制数。比如#3388aa#38a
    • RGB值。比如rgb(10,150,20)
    • RGB值加上不透明度。rgba(10,150,20,0.5)
  • stroke --- 也是一个颜色值,即画线时的颜色
  • stroke-width --- 一个数值(一般是以像素为单位)
  • opacity --- 0到1之间的一个数值,0表示完全透明,1表示完全不透明

通过text,你可以使用下面这些属性,它们含义和CSS是保持一致的。

  • font-family
  • font-size

和CSS一样,为SVG元素指定样式也有两种方式:直接作为元素属性,或使用CSS样式规则。下面是直接将样式属性应用于circle的代码:

{% highlight javascript %} {% endhighlight %}

当然,我们可以不直接用这些样式属性,而是为circle指定一个样式类(就像为HTML元素指定类一样)。

{% highlight javascript %} {% endhighlight %}

然后,把fillstrokestroke-width这些规则放到一个CSS样式中,这样就构成了一个新类。

{% highlight css %} .pumpkin { fill: yellow; stroke: orange; stroke-width: 5; } {% endhighlight %}

使用CSS方法有一些明显的好处:

  • 样式只需定义一次,然后应用到多个元素上
  • 一般,CSS代码比元素属性代码更易懂
  • CSS方法更易控制,从而让设计更高效

不过,使用CSS来应用SVG样式还是会让一些人心里不舒服。因为fillstrokestroke-width其实都不是CSS属性(最接近的CSS属性是background-colorborder)。如果你想将SVG专用的规则标记出来,你可以在选择器中加上svg关键字。

{% highlight css %} svg .pumpkin { /* ... */ } {% endhighlight %}

图层和绘制顺序

SVG中没有"层"和深度的概念。SVG也不支持CSS的z-index属性。所以,形状都只能是x/y平面上的二维图形。

但是,多个形状之间又常常会出现重叠。

{% highlight javascript %} {% endhighlight %}

元素的编码顺序决定了它们的深度顺序。紫色的矩形最先出现在代码中,因此它被首先画出来。然后蓝色矩形覆盖了紫色矩形,之后是绿色,依此类推。

将SVG视为画布就很好理解了。先画的总是被后画的给掩盖,因而后画的形状表现为最上面。

因此,如果有些形状不能被遮挡,则绘制顺序就很重要了。比如,坐标轴和散点图上的标签。坐标轴和标签总是应该出现在SVG代码的最后面,这样才能保证它们始终出现在其它元素的上面。

透明度

如果在可视化中出现重叠,而你又想让被遮挡的元素可见,或者你想强调一些元素而弱化其它一些,那么透明度就有用武之地了。

使用透明度有两种方法:使用带不透明度的RGB,或单独设置不透明度opacity

你可以在任意需要颜色的地方使用rgba(),比如fill,或stroke.rgba()都接受3个0至255之间的颜色值(分别表示红绿蓝)和1个0.0至1.0的不透明度值。

{% highlight html %} {% endhighlight %}

注意,在用rgba()时,fillstroke的不透明度是独立的。下面这个例子中,圆在填充时fill的不透明度是75%,而stroke的不透明度是25%。

{% highlight html %} {% endhighlight %}

如果要为整个元素设置不透明度,可以使用opacity属性。下面的例子是完全不透明的圆。

同样是这些圆,设置不同的opacity之后,结果就会变化。

{% highlight html %} {% endhighlight %}

你也可以在使用rgba()的同时再设置元素的opacity。这时,不透明度会相乘。下面例子中的圆对于fillstroke使用同样的RGBA值,第一个圆没有设置opacity,而后两个设置了。

{% highlight html %} {% endhighlight %}

注意,第3个圆的opacity为0.2,即20%。而它的紫色填充已经有了不透明值0.75(或75%)。因此,紫色区域最终的不透明度为0.2乘以0.75,结果为0.15,或15%。