svg介绍
svg是用xml描述的矢量图,放大不会失真,可以通过直接编写svg标签属性描述图形,也可以通过js操作svg的dom结构,设置修改属性来定义图形,当然svg作为dom标签,也可以通过css进行属性设置,而它具有一些不同于我们平时使用的css属性。
下面介绍关系图谱中用到的基础图形
矩形 rect
x
: 矩形左上角x轴坐标y
: 矩形左上角y轴坐标width
: 矩形宽度height
: 矩形高度rx
: 圆角,x轴的半径,的最大值是矩形宽度的一半,ry
: 圆角,y轴的半径,的最大值是矩形高度的一半。
<svg width="300" height="300" style="border: 1px solid red;">
<rect width="200" height="100"></rect>
</svg>
想要一个规则的圆角,只设置rx或者ry其中一个就可以了,设置x、y可以让矩形以svg左上角为起始点进行移位。
圆形 circle
cx
: 圆心在x轴的坐标cy
: 圆心在y轴的坐标r
: 半径
路径 path
path可以说是svg中最核心灵活的标签了,可以灵活绘制各种各样的图形,而它最核心的属性d
就是用来设置绘制路径的。
M
: 移动画笔到指定坐标位置,moveto
的意思。每个路径都必须以M
开始。M
传入x
和y
坐标,用逗号或者空格隔开。L
: 轮廓坐标,lineto
的意思。L
是跟在M
后面的。它也是可以传入一个或多个坐标。大写的L
是一个绝对位置。l
: 这是小写L
,和L
的作用差不多,但l
是一个相对位置。H
: 和上一个点的Y坐标相等,是horizontal lineto
的意思。它是一个绝对位置。h
: 和H
差不多,但h
使用的是相对定位。V
: 和上一个点的X坐标相等,是vertical lineto
的意思。它是一个绝对位置。v
: 这是一个小写的v
,和大写V
的差不多,但小写v
是一个相对定位。Z
: 关闭当前路径,closepath
的意思。它会绘制一条直线回到当前子路径的起点。
椭圆弧路径 path
椭圆弧曲线顾名思义,画出来的弧线其实就是一个椭圆的一部分,我们需要通过几个参数,根据椭圆上两个坐标点、长半轴、短半轴,就可以确定出两个椭圆,四条弧形、而通过下面的参数可以筛选出我们想要的那一条弧线
弧线参数 A rx ry x-axis-rotation large-arc-flag sweep-flag x y
rx
x半径ry
y半径x-axis-rotation
x轴旋转角度laf即large-arc-flag
值为1表示大角弧度,大于180度;0表示小角弧度,小于180度。sf即sweep-flag
值为1表示从起点到终点绕中心顺时针方向;0表示逆时针方向。x
弧线终点x坐标y
弧线终点y坐标
画一个圆弧,可以自己调参数试试效果
<svg width="1500" height="1500">
<path d="M 100 100 A 100 200 0 1 0 200 300" fill="none" stroke="red"></path>
</svg>
d3.js
d3.js是一个面向过程的JavaScript库,d3提供了跟多操作dom的api,这点功能类似于jQuery,比如d3.select(css选择器)
,功能类似document.querySelector()
,可以通过css选择器选中第一个选中的元素,如果想要选中所有的选中元素,可以用document.selectAll(css选择器)
,类似于 document.querySelectorAll
。选中之后可以通过链式调用设置属性,比如要给一个类名为test的标签设置属性。通过下面的属性设置,可以给这个标签添加width
和height
属性。
const testDom = d3.select('.test');
testDom
.attr('width', 100)
.attr('height', 100);
d3是如何工作的
数据图元绑定
官方api: github.com/d3/d3-selec…
d3-selection是d3非常重要和实用的机制,可以根据数据绑定图元,比如我们有这样一段数据,意思是选中类名为container下的所有p标签。返回值update是一个Pt
对象(selection),每个Pt
对象的原型上会有很多用于操作节点的方法,比如append
、remove
、attr
、style
、text
,调用这些方法会返回的也是Pt
对象,所以可以进行链式调用
。
<div class="container">
<p>1</p>
<p>2</p>
</div>
const data = ['h', 'e', 'l', 'l', 'o', 'w'];
const p = d3.select('.container').selectAll('p')
const updateSelection = p.data(data);
console.log(updateSelection);
在这个
updateSelection
对象中,会有三个状态的节点(选中、新增、多余),_group
这里放的是可以操作的节点,在接下来链式调用d3的方法操作节点属性的时候,d3会对_groups
中的每个元素进行更新。
enter
enter
enter中是数据数量大于选中标签数量的时候,因为container
下只有两个p标签,而data数据的数量有六个,所以enter中前两个元素是空节点,后面有四个rt
节点(一个描述对象,用于根据数据新增节点)。
当调用updateSelection.enter()
方法的时候,d3会根据updateSelection
对象中的_enter
返回一个新的Pt
对象,并在新对象中把_enter
作为_groups
const data = ['h', 'e', 'l', 'l', 'o', 'w'];
const p = d3.select('.container').selectAll('p')
const updateSelection = p.data(data);
const enterSelection = updateSelection.enter()
console.log(enterSelection)
这时候这个新的enter对象拥有了_group
属性,我们就可以操作它了,比如,我们想将enter
中每个rt
节点变成div节点,只需要对enter
调用append
方法。
const divSelecton = enterSelection.append('div')
console.log(divSelecton)
并且此时的div已经插入到了container容器中
当然如果想设置div的属性,也可以链式设置
enterSelection
.append('div')
.style('width', 100)
.style('height', 100)
.style('background-color', 'red');
可以看到通过设置后,属性已经添加上去了。
exit
exit
是选中标签数量大于数据数量的时候。
而调用exit()
方法也是同理,会返回一个新的Pt
对象,在新的对象中会将_exit
(多余节点)变成_group
,然后就可以对多余节点进行下一步操作了,比如调用remove()
方法将多余的节点从dom结构中删除。
merge
每个Pt
对象中还会有一个merge
方法,这个方法可以将两个Pt
对象中的_group
进行合并,然后统一操作,举个🌰。
<div class="container">
<p>1</p>
<p>2</p>
</div>
const data = ['h', 'e', 'l', 'l', 'o', 'w'];
const p = d3.select('.container').selectAll('p')
// updateSelection中具有_groups属性存放了选中的container下的两个p标签
const updateSelection = p.data(data);
// 调用enter方法取出_enter中的属性作为新Pt对象的_groups
// 再链式操作新Pt对象,将rt节点变成div插入到container中
const enterSelection = updateSelection.enter().append('div');
// 调用merge方法,合并两个Pt对象的_groups并返回新的Pt对象
const mergeSelection = updateSelection.merge(enterSelection)
console.log(mergeSelection);
通过上面的图可以看出,新的Pt对象中的_groups
合并了两个Pt对象的_groups
,接下来就可以统一处理了,比如将每个p和div加上宽高属性。
mergeSelection.style('color', 'red')
.style('width', "200px")
.style('height', "200px")
函数属性
当我们用data
进行数据-图元
绑定以后,在接下来的属性设置中,属性值可以是一个函数,这个通过这个函数的参数可以获取到当前图元对应的数据以及获取到当前dom
节点,这也是我们在d3
中最常用的属性设置方式。
const data = ['h', 'e', 'l', 'l', 'o', 'w'];
const p = d3.select('.container').selectAll('p')
const updateSelection = p.data(data);
const enterSelection = updateSelection.enter().append('div');
const mergeSelection = updateSelection.merge(enterSelection)
mergeSelection
.style('color', 'red')
.text(d => {
// 这里获取到的就是data中的每一项数据
console.log(d);
// 函数返回值作为属性值
return d;
})
通过
text
方法中函数返回值,可以看到p标签和div标签的值已经变成了data中的每一项数据。当然如果设置的属性具有key,比如width就可以这样设置style('width',d=>{ return ... })
。