一、d3中的交互操作
与图表的交互,指的是在图形元素上设置一个或多个监听器,当事件发生时,做出相应的反应。
什么是交互
交互
: 指的是用户输入了某种指令,程序接受到指令之后必须做出某种响应。对可视化图表来说,交互能使图表更加生动,能表现更多内容。
例如,拖动图表中某些图形、鼠标滑到图形上出现提示框、用触屏放大或缩小图形等等。
用户用于交互的工具一般有三种:鼠标
、键盘
、触屏
。
如何添加交互?
对某一元素添加交互操作十分简单,代码如下:
var circle = svg.append("circle");
circle.on("click", function(){
//在这里添加交互内容
});
在 D3 中,每一个选择集都有 on() 函数,用于添加事件监听器。on() 的第一个参数是监听的事件,第二个参数是监听到事件后响应的内容,是一个函数。
-
鼠标常用的事件有:
click
:鼠标单击某元素时触发。
mouseover
:光标放在某元素上。
mouseout
:光标从某元素上移出来时。
mousemove
:鼠标被移动的时候。
mousedown
:鼠标按钮被按下。
mouseup
:鼠标按钮被松开。
dblclick
:鼠标双击。 -
键盘事件:
keydown
:当用户按下任意键时触发,按住不放会重复触发此事件。该事件不会区分字母的大小写。
keypress
:当用户按下任意字符键时触发,按住不放会重复触发此事件。该事件区分字母的大小写。
keyup
:当用户释放键时触发,不区分字母的大小写。 -
触屏事件:
touchstart
: 当触摸点被放在触摸屏上时。
touchmove
: 当触摸点在触摸屏上移动时。
touchend
: 当触摸点从触摸屏上拿开时。
可视化图形
层级结构:树, 最直接、最直观的可视化方案。 数据格式为JSON格式,节点可以包含“属性”。
绘制树状图
树状图用于表示层级、上下级、包含与被包含关系。
绘制一个树状图主要分为四步:
- 选中页面设置的svg绘制区域的宽高
- 生成树状布局,设置尺寸
- 对角线生成器
- 请求数据,渲染数据
- 获取节点数组和连线数组
- 绘制连线和节点
- 给节点添加圆圈设置半径
- 给节点添加文本 设置文本样式 位置
一、使用SVG绘制区域的宽高,设置元素的位置
<svg id="mainsvg" class="svgs" width="1000" height="900"></svg>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
// 获得画布
const svg = d3.select('#mainsvg');
// 定义比例尺
const width = +svg.attr('width');
const height = +svg.attr('height');
// 用来控制表格边框线的位置
const margin = {
top: 50,
right: 150,
bottom: 50,
left: 60
};
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
let root;//全局变量root
let color;
二、设置数据,并且读取数据
-
由于层级的数据本质就是JSON,而且D3支持从web服务读取json数据,或者从外部文件如.json, .csv文件中直接读取。因此这里我们把数据放在data文件夹下的province.json中,数据格式如下:
-
使用第三方的json来读取数据。使用d3.hierarchy进行层级数据的预处理,它会保持数据的原始结构,并将输入层级数据转换成D3中的hierarchy对象,同时引入:height、depth、data等。d3.hierarchy可作为一个“中间结果”,继续输入到更多D3.js提供的数据预处理接口中。
d3.json('./data/province.json').then(data => {
//使用hierarchy将数据转换成d3中自己的数据格式
root = d3.hierarchy(data);
//调用d3.tree()对层级结构进行空间上的划分,此时每个节点就会多出来一个x和y的值,这两个值就是每个节点对应到画布上的位置
root = d3.tree().size([innerHeight, innerWidth])(root);
render()
})
重点强调 :d3.tree().size([innerHeight, innerWidth])
这个代码会返回一个函数,函数接受的参数为d3.hierarchy(data),函数会根据设置的size将树形结构的每个节点映射到空间中“合适”的位置。
- 有了x和y的值就可以进行渲染,首先先渲染出线条关系图。我们使用d3.linkHorizontal()来添加线条,这条线的本质上是一个path。
const render = function (data) {
g.selectAll('path')
// root.links 它返回一个数组,包括source和target,这就是两个根结点
.data(root.links())
.join('path')
//填充设置为空
.attr('fill', 'none')
//定义边框颜色为黑色的
.attr('stroke', 'black')
//边框的宽度为1.5
.attr('stroke-width', 1.5)
// x和y表示要对source和target的每一个x和y要如何取值,这里由于是横向显示,所以把x和y的位置反过来写
.attr('d', d3.linkHorizontal().x(d => d.y).y(d => d.x))
}
4. 绘画小圆点,data().join(‘circle’)中data()接收的是一个list。d3提供给我们一个root.descendants(),它可以把包括root节点在内的其他的所有子节点压平成一个list,返回给data,然后就可以进行join()
g.selectAll('circle').data(root.descendants()).join('circle')
// 设置圆的位置
.attr('cx', d => d.y)
.attr('cy', d => d.x)
//填充一个颜色
.attr('fill', 'red')
.attr('stroke-width', 3)
//设置半径
.attr('r', 6)
- 但是这样设置的颜色都是一样的,我们可以将red修改为一个参数,然后在上面去定义它。
const fill = d => {
//如果是根结点
if (d.dept === 0) {
return color(d.data.name)
}
//如果不是根结点
while (d.dept > 1) {
d = d.parent;
}
return color(d.data.name)
}
然后我们就去render中定义color:
color = d3.scaleOrdinal(d3.schemeCategory10)
定义比例尺的时候我们就要设置domain()和range(),在定义scaleOrdinal的时候range(d3.schemeCategory10),这个是d3提供的默认的配色方案。domain的设置是很麻烦的,在这里就可以省略,scaleOrdinal看到这里没有设置domain,它就会从Category里面找一个颜色进行渲染,就是自动配色。
- 进行字体的渲染,这里要注意的是,放置文字的位置。我们可以使用text-anchor来调整,如果设置成middle的时候,文字就会剧中显示。如果设置为end,就是从右向左扩张。如果是
g.selectAll('text').data(root.descendants()).join('text')
.attr("font-size", '1em')
// 用三元运算符去判断,它如果有子节点的话,就是true,显示形式就用end,会显示在圆点的左边。这样的话根结点就在左边,子节点在右边。
.attr("text-anchor", d => d.children ? "end" : "start")
//设置节点和圆点的距离,默认是以圆心来放置
.attr('x', d => (d.children ? -7 : 7) + d.y)
.attr('y', d => d.x + 5)
.text(d => d.data.name)
- 在渲染文字和圆圈的时候添加交互效果,改变圆圈的大小,颜色的文字
.on("mouseover", function (d) { //交互
d3.select(this)
.attr("r", 8)
})
.on("mouseout", function () {
d3.select(this)
.attr("r", 6)
})
绑定数据的三个“状态”
Update、Enter、Exit 是 D3 中三个非常重要的概念,它处理的是当选择集和数据的数量关系不确定的情况。
之前写的例子中,反复出现了类似以下的代码:
svg.selectAll("rect") //选择svg内所有的矩形
.data(dataset) //绑定数组
.enter() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形元素
- update()
当对应的元素正好满足时 ( 绑定数据数量 = 对应元素 ),图元和数据条目相同。 - enter()
数据的条目多于图元甚至没有图元,常用于第一次绑定数据。D3会自动搞清楚哪些数据是新增的,根据新增的数据生成相应的图元。占位的内容可以使用append来添加。
const p = main.selectAll('.class').data(data).enter().append('rect').attr()
//.enter给不存在数据的图源一个占位符
- exit()
数据的条目少于图元甚至没有数据。D3会自动搞清楚哪些图元是不绑定数据的。占位的内容可以使用remove来删除。
const p = main.selectAll('.class').data(data).exit().remove()