D3最擅长于生成和操纵SVG图像来进行可视化。绘制div
或其它原生HTML元素也是可以的,但是考虑到不同浏览器之间总会存在一些标准上的差异,使用SVG会更可靠更稳定,也会更快一些。
类似于Illustrator的矢量绘图软件可以生成SVG文件,但是我们这里讨论的是如何用代码来生成。
可扩展矢量图形(Scalable Vector Graphics, SVG)是一种基于文本的图像格式。每个SVG图像都由类似于HTML的标签代码来定义。SVG代码可以直接用于HTML文档中。每个网页浏览器都是支持SVG的(除了IE8及之前的版本)。SVG是基于XML的,因此你会发现没有结束标签的元素必须是自封闭的。比如
<element></element> <!-- Uses closing tag -->
<element/> <!-- Self-closing tag -->
在你开始画之前,你必须先生成一个初始的SVG元素。将此元素视为一个画布,你将要在此画布上大展身手(从这个意义看,SVG的这个元素类似于HTML的canvas
元素)。即使在一个最小的示例中,你最好也指定一下width
和height
。如果你没有指定宽度和高度,它会占用svg
所在元素中的尽可能多的空间。
<svg width="500" height="50">
</svg>
下面给出效果图
没看到? 尝试在空白位置点击右键,再选择”Inspect Element”,你的浏览器应该得到类似于下面的结果
注意,这里确实有一个svg
元素,而它刚好就占用了宽为500像素高为50像素的一个区域。只不过,空白的图像确实没什么好看的。
还需注意的是,浏览器默认像素
为数值单位。我们用的是500
和50
,而无需用500px
和50px
。当然你也可以显式地指定px
,或任意其它浏览器支持的单位,比如em
, pt
, in
, cm
和mm
。
在svg
标签中,你可以使用大量的图形元素,包括rect
,circle
,ellipse
, line
,text
和path
。
如果你熟悉计算机图形编程,你会了解通常的基于像素的坐标系统的原点0,0
位于绘制区域的左上角。增加x
值会导致右移,增加y
值会导致下移。
rect
元素会画出一个矩形。可以用x
和y
来指定它的左上角的位置,而用width
和height
指定其大小。下面的这个矩形填充了我们的整个SVG画布。
<rect x="0" y="0" width="500" height="50"/>
circle
元素会画出一个圆。可以用cx
和cy
来指定圆心位置,而用r
来指定其半径。下面的这个圆位于我们的500个像素宽的画布的中心,所以它的cx
值为250。
<circle cx="250" cy="25" r="25"/>
ellipse
元素是类似的,只不过它在每个轴上的半径可以不一样。因此,半径r
变成了rx
和ry
。
<ellipse cx="250" cy="25" rx="100" ry="25"/>
line
元素用来画一条线段。它使用x1
和y1
指定线段的一个端点位置,而用x2
和y2
来指定另外一端的位置。笔画stroke
的颜色也必须指定,不然就混在背景里看不见了。
<line x1="0" y1="0" x2="500" y2="50" stroke="black"/>
text
用于渲染文本。它使用x
指定文本左边界的位置,用y
来指定基线(baseline)的垂直坐标。
<text x="250" y="25">Easy-peasy</text>
除非显式地指定(后面马上会详细介绍如何指定文本样式),text
会继承父元素中的CSS指定的字体样式。注意,上面例子中的文本与周围的段落的格式是保持一致的。我们可以用下面的代码修改其格式。
<text x="250" y="25" font-family="sans-serif"
font-size="25" fill="gray">Easy-peasy</text>
另外,需要提醒的是,任意到达SVG画布边界的图形元素都会被裁剪掉。所以,使用text
时要小心,确保里面的字符不要被裁掉。如果将基线坐标(y
)修改为50,也就是SVG的高度,你就能体会到这一点了。
<text x="250" y="50" font-family="sans-serif"
font-size="25" fill="gray">Easy-peasy</text>
path
用于绘制更复杂的形状(比如地图中的国境线),将来我会单独介绍。但现在,我们只讨论简单形状。
SVG的默认样式是黑色填充而不画线。如果你想修改样式,你需要将样式应用到元素上。常用的SVG属性有:
fill
— 一个颜色值。和CSS一样,颜色可以有几种指定方式
orange
#3388aa
或#38a
rgb(10,150,20)
rgba(10,150,20,0.5)
stroke
— 也是一个颜色值,即画线时的颜色stroke-width
— 一个数值(一般是以像素为单位)opacity
— 0到1之间的一个数值,0表示完全透明,1表示完全不透明 通过text
,你可以使用下面这些属性,它们含义和CSS是保持一致的。
font-family
font-size
和CSS一样,为SVG元素指定样式也有两种方式:直接作为元素属性,或使用CSS样式规则。下面是直接将样式属性应用于circle
的代码:
<circle cx="25" cy="25" r="22"
fill="yellow" stroke="orange" stroke-width="5"/>
当然,我们可以不直接用这些样式属性,而是为circle
指定一个样式类(就像为HTML元素指定类一样)。
<circle cx="25" cy="25" r="22" class="pumpkin"/>
然后,把fill
,stroke
和stroke-width
这些规则放到一个CSS样式中,这样就构成了一个新类。
.pumpkin {
fill: yellow;
stroke: orange;
stroke-width: 5;
}
使用CSS方法有一些明显的好处:
不过,使用CSS来应用SVG样式还是会让一些人心里不舒服。因为fill
,stroke
和stroke-width
其实都不是CSS属性(最接近的CSS属性是background-color
和border
)。如果你想将SVG专用的规则标记出来,你可以在选择器中加上svg
关键字。
svg .pumpkin {
/* ... */
}
SVG中没有”层”和深度的概念。SVG也不支持CSS的z-index
属性。所以,形状都只能是x/y平面上的二维图形。
但是,多个形状之间又常常会出现重叠。
<rect x="0" y="0" width="30" height="30" fill="purple"/>
<rect x="20" y="5" width="30" height="30" fill="blue"/>
<rect x="40" y="10" width="30" height="30" fill="green"/>
<rect x="60" y="15" width="30" height="30" fill="yellow"/>
<rect x="80" y="20" width="30" height="30" fill="red"/>
元素的编码顺序决定了它们的深度顺序。紫色的矩形最先出现在代码中,因此它被首先画出来。然后蓝色矩形覆盖了紫色矩形,之后是绿色,依此类推。
将SVG视为画布就很好理解了。先画的总是被后画的给掩盖,因而后画的形状表现为最上面。
因此,如果有些形状不能被遮挡,则绘制顺序就很重要了。比如,坐标轴和散点图上的标签。坐标轴和标签总是应该出现在SVG代码的最后面,这样才能保证它们始终出现在其它元素的上面。
如果在可视化中出现重叠,而你又想让被遮挡的元素可见,或者你想强调一些元素而弱化其它一些,那么透明度就有用武之地了。
使用透明度有两种方法:使用带不透明度的RGB,或单独设置不透明度opacity
。
你可以在任意需要颜色的地方使用rgba()
,比如fill
,或stroke.rgba()
都接受3个0至255之间的颜色值(分别表示红绿蓝)和1个0.0至1.0的不透明度值。
<circle cx="25" cy="25" r="20" fill="rgba(128, 0, 128, 1.0)"/>
<circle cx="50" cy="25" r="20" fill="rgba(0, 0, 255, 0.75)"/>
<circle cx="75" cy="25" r="20" fill="rgba(0, 255, 0, 0.5)"/>
<circle cx="100" cy="25" r="20" fill="rgba(255, 255, 0, 0.25)"/>
<circle cx="125" cy="25" r="20" fill="rgba(255, 0, 0, 0.1)"/>
注意,在用rgba()
时,fill
和stroke
的不透明度是独立的。下面这个例子中,圆在填充时fill
的不透明度是75%,而stroke
的不透明度是25%。
<circle cx="25" cy="25" r="20" fill="rgba(128, 0, 128, 1.0)"/>
<circle cx="50" cy="25" r="20" fill="rgba(0, 0, 255, 0.75)"/>
<circle cx="75" cy="25" r="20" fill="rgba(0, 255, 0, 0.5)"/>
<circle cx="100" cy="25" r="20" fill="rgba(255, 255, 0, 0.25)"/>
<circle cx="125" cy="25" r="20" fill="rgba(255, 0, 0, 0.1)"/>
如果要为整个元素设置不透明度,可以使用opacity
属性。下面的例子是完全不透明的圆。
同样是这些圆,设置不同的opacity
之后,结果就会变化。
<circle cx="25" cy="25" r="20" fill="purple"
stroke="green" stroke-width="10"
opacity="0.9"/>
<circle cx="65" cy="25" r="20" fill="green"
stroke="blue" stroke-width="10"
opacity="0.5"/>
<circle cx="105" cy="25" r="20" fill="yellow"
stroke="red" stroke-width="10"
opacity="0.1"/>
你也可以在使用rgba()
的同时再设置元素的opacity
。这时,不透明度会相乘。下面例子中的圆对于fill
和stroke
使用同样的RGBA值,第一个圆没有设置opacity
,而后两个设置了。
注意,第3个圆的opacity
为0.2,即20%。而它的紫色填充已经有了不透明值0.75(或75%)。因此,紫色区域最终的不透明度为0.2乘以0.75,结果为0.15,或15%。
来源:http://www.pkuwwt.tk/d3-tutorial-cn/the-svg-primer.html