安装
//当前d3.v7.js
npm install d3
<script src="https://d3js.org/d3.v5.js"></script>
D3.js 是一个JavaScript库,用于使用Web标准可视化数据。 D3帮助您使用SVG,Canvas和HTML使数据栩栩如生。 D3将强大的可视化和交互技术与数据驱动的DOM操作方法相结合,为您提供现代浏览器的全部功能,并为您的数据设计正确的可视界面提供了自由。
选择元素和绑定数据
//如何选择元素
var body = d3.select("body"); //选择文档中的body元素
var svg = body.select("svg"); //选择body中的svg元素
var p = body.selectAll("p"); //选择body中所有的p元素
var p1 = body.select("p"); //选择body中第一个p元素
var rects = svg.selectAll("rect"); //选择svg中所有的rect元素
var p2 = body.select("#pear-id");//id选择
var p = body.selectAll(".myclass");//class选择
//如何绑定数据
//datum():绑定一个数据到选择集上
var str = "World";
var body = d3.select("body");
var p = body.selectAll("p");
p.datum(str);
p.text(function(d, i){
return "第 "+ i + " 个元素绑定的数据是 " + d;
});
//data():绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定
var dataset = ["I like dog","I like cat","I like snake"];
var body = d3.select("body");
var p = body.selectAll("p");
p.data(dataset)
.text(function(d, i){
return d;
});
插入、删除元素
// append():在选择集末尾插入元素
body.append("p")
.text("append p element");
// insert():在选择集前面插入元素
//在 body 中 id 为 myid 的元素前添加一个段落元素
body.insert("p","#pear-id")
.text("insert p element");
// 删除元素
var p = body.select("#pear-id");
p.remove();
简单的图表
柱形图是一种最简单的可视化图标,主要有矩形、文字标签、坐标轴组成。本文为简单起见,只绘制矩形的部分,用以讲解如何使用 D3 在 SVG 画布中绘图。
//添加画布
var width = 300; //画布的宽度
var height = 300; //画布的高度
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
var dataset = [ 250 , 210 , 170 , 130 , 90 ]; //数据(表示矩形的宽度)
var rectHeight = 25; //每个矩形所占的像素高度(包括空白)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x",20)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return d;
})
.attr("height",rectHeight-2)
.attr("fill","steelblue");
比例尺的使用
比例尺是 D3 中很重要的一个概念,上一章里曾经提到过直接用数值的大小来代表像素不是一种好方法,如果数值过大或过小,非常具有局限性。将某一区域的值映射到另一区域,其大小关系不变。这就是比例尺(Scale)。 在数学中,x 的范围被称为定义域,y 的范围被称为值域。D3 中的比例尺,也有定义域和值域,分别被称为 domain 和 range。开发者需要指定 domain 和 range 的范围。
//线性比例尺,最小的值,映射成 0;将最大的值,映射成 300
var min = d3.min(dataset); //得到最小值
var max = d3.max(dataset); //得到最大值
var scaleLinear = d3.scaleLinear()
.domain([min, max])
.range([0, 300]);
scaleLinear(0.9); //返回 0
scaleLinear(2.3); //返回 175
scaleLinear(3.3); //返回 300
//序数比例尺,我们希望 0 对应颜色 red,1 对应 blue,依次类推。
var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];
var scaleOrdinal = d3.scaleOrdinal()
.domain(index)
.range(color);
scaleOrdinal (0); //返回 red
scaleOrdinal (2); //返回 green
scaleOrdinal (4); //返回 black
坐标轴
坐标轴,是可视化图表中经常出现的一种图形,由一些列线段和刻度组成。坐标轴在 SVG 中是没有现成的图形元素的,需要用其他的元素组合构成。D3 提供了坐标轴的组件。
//画布大小
var width = 400;
var height = 400;
//在 body 里添加一个 SVG 画布
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//画布周边的空白
var padding = {left:30, right:30, top:20, bottom:20};
//定义数据和比例尺////////////////////////////////////////////////
//定义一个数组
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
//x轴的比例尺(创建一个序列化比例尺)
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width - padding.left - padding.right]);
//y轴的比例尺
var yScale = d3.scaleLinear()
.domain([0,d3.max(dataset)])
.range([height - padding.top - padding.bottom, 0]);
//定义坐标轴//////////////////////////////////////////////
var xAxis = d3.axisBottom(xScale); //定义x轴
var yAxis = d3.axisLeft(yScale); //定义y轴
//添加矩形和文字元素////////////////////////////////
//矩形之间的空白
var rectPadding = 4;
//添加矩形元素
var rects = svg.selectAll(".MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class", "MyRect")
.attr(
"transform",
"translate(" + padding.left + "," + padding.top + ")"
)
.attr("x", function(d, i) {
return xScale(i) + rectPadding / 2;
})
.attr("y", function(d) {
return yScale(d);
})
.attr("width", xScale.step() - rectPadding)
.attr("height", function(d) {
return height - padding.top - padding.bottom - yScale(d);
})
.attr("fill","blue");
//添加文字元素
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class", "MyText")
.attr(
"transform",
"translate(" + padding.left + "," + padding.top + ")"
)
.attr("x", function(d, i) {
return xScale(i) + rectPadding / 2;
})
.attr("y", function(d) {
return yScale(d);
})
.attr("dx", function() {
return (xScale.step() - rectPadding) / 2;
})
.attr("dy", function(d) {
return 20;
})
.text(function(d) {
return d;
});
//添加坐标轴的元素////////////////////////////////////
//添加x轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
//添加y轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
//样式//////////////////////////
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
过渡效果
D3 支持制作动态的图表。有时候,图表的变化需要缓慢的发生,以便于让用户看清楚变化的过程。
//transition()启动过渡效果
.attr("fill","red") //初始颜色为红色
.transition() //启动过渡
.attr("fill","steelblue") //终止颜色为铁蓝色
//duration()指定过渡的持续时间,单位为毫秒。
//ease()指定过渡的方式,常用的有
// delay()指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒
.transition()
.duration(1000)
.delay(500)
// 给柱形图加上动态效果
.attr("y",function(d){
var min = yScale.domain()[0];
return yScale(min);
})
.transition()
.delay(function(d,i){
return i * 200;
})
.duration(2000)
.ease(d3.easeBounce)
.attr("y",function(d){
return yScale(d);
});
//文字元素的过渡前后,发生变化的是 y 坐标。其起始状态是在 y 轴等于 0 的位置(但要注意,不能在起始状态直接返回 0,要应用比例尺计算画布中的位置)。终止状态是目标值。
理解 Update、Enter、Exit
Update、Enter、Exit 是 D3 中三个非常重要的概念,它处理的是当选择集和数据的数量关系不确定的情况。
//有数据,而没有足够图形元素的时候,使用此方法可以添加足够的元素。
svg.selectAll("rect") //选择svg内所有的矩形
.data(dataset) //绑定数组
.enter() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形元素
// update 部分的处理办法一般是:更新属性值
// enter 部分的处理办法一般是:添加元素后,赋予属性值
// exit 部分的处理办法一般是:删除元素(remove)
交互式操作
与图表的交互,指在图形元素上设置一个或多个监听器,当事件发生时,做出相应的反应。
var circle = svg.append("circle");
circle.on("click", function(e){
//在这里添加交互内容
});
// 鼠标常用的事件有:
// click:鼠标单击某元素时,相当于 mousedown 和 mouseup 组合在一起。
// mouseover:光标放在某元素上。
// mouseout:光标从某元素上移出来时。
// mousemove:鼠标被移动的时候。
// mousedown:鼠标按钮被按下。
// mouseup:鼠标按钮被松开。
// dblclick:鼠标双击。
// 键盘常用的事件有三个:
// keydown:当用户按下任意键时触发,按住不放会重复触发此事件。该事件不会区分字母的大小写,例如“A”和“a”被视为一致。
// keypress:当用户按下字符键(大小写字母、数字、加号、等号、回车等)时触发,按住不放会重复触发此事件。该事件区分字母的大小写。
// keyup:当用户释放键时触发,不区分字母的大小写。 触屏常用的事件有三个:
// touchstart:当触摸点被放在触摸屏上时。
// touchmove:当触摸点在触摸屏上移动时。
// touchend:当触摸点从触摸屏上拿开时。
//带有交互的柱形图///////////////////
var rects = svg.selectAll(".MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect") //把类里的 fill 属性清空
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("width", xScale.step() - rectPadding )
.attr("height", function(d){
return height - padding.top - padding.bottom - yScale(d);
})
.attr("fill","steelblue") //填充颜色不要写在CSS里
.on("mouseover",function(d,i){
d3.select(this)
.attr("fill","yellow");
})
.on("mouseout",function(d,i){
d3.select(this)
.transition()
.duration(500)
.attr("fill","steelblue");
});
官网一个坐标轴的例子
var mFun = ()=>{
// Declare the chart dimensions and margins.
const width = 640;
const height = 400;
const marginTop = 20;
const marginRight = 20;
const marginBottom = 30;
const marginLeft = 40;
// Declare the x (horizontal position) scale.
const x = d3.scaleUtc()
.domain([new Date("2023-01-01"), new Date("2024-01-01")])
.range([marginLeft, width - marginRight]);
// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0, 100])
.range([height - marginBottom, marginTop]);
// Create the SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
// Add the x-axis.
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x));
// Add the y-axis.
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y));
// Return the SVG element.
return svg.node();
}
var mNode = mFun();
document.body.appendChild(mNode);
鼠标事件,改变子节点的颜色
var width = 300; //画布的宽度
var height = 300; //画布的高度
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
var mG = svg.append("g")
.attr("transform", "translate(50,50)")
// 鼠标移入事件
mG.on("mouseover", function() {
d3.select(this).select("rect").style("fill", "red");
d3.select(this).select("line").style("stroke", "yellow");
d3.select(this).select("text").style("fill", "white");
});
// 鼠标移出事件
mG.on("mouseout", function() {
d3.select(this).select("rect").style("fill", "steelblue");
d3.select(this).select("line").style("stroke", "black");
d3.select(this).select("text").style("fill", "red");
});
// 添加矩形
mG.append("rect")
.attr("x",20)
.attr("y",20)
.attr("width",100)
.attr("height",100)
.attr("fill","steelblue");
// 添加线
mG.append('line')
.attr('x1', 20)
.attr('y1', 20)
.attr('x2', 180)
.attr('y2', 180)
.attr('stroke', 'black');
// 添加文本
mG.append('text')
.text('Hello, D3.js!')
.attr('x', 40)
.attr('y', 50)
.attr('fill', 'red');
布局
布局,可以理解成 “制作常见图形的函数”,有了它制作各种相对复杂的图表就方便多了。 布局的作用是:将不适合用于绘图的数据转换成了适合用于绘图的数据。 D3 总共提供了 12 个布局:饼状图(Pie)、力导向图(Force)、弦图(Chord)、树状图(Tree)、集群图(Cluster)、捆图(Bundle)、打包图(Pack)、直方图(Histogram)、分区图(Partition)、堆栈图(Stack)、矩阵树图(Treemap)、层级图(Hierarchy)。
饼状图
在布局的应用中,最简单的就是饼状图
// 1、数据准备
var marge = {top:60,bottom:60,left:60,right:60}
var svg = d3.select("svg")
var width = svg.attr("width")
var height = svg.attr("height")
var g = svg.append("g")
.attr("transform","translate("+marge.top+","+marge.left+")");
var dataset = [ 30 , 10 , 43 , 55 , 13 ];//需要将这些数据变成饼状图的数据
// 2、设置一个颜色比例尺
//设置一个color的颜色比例尺,为了让不同的扇形呈现不同的颜色
var colorScale = d3.scaleOrdinal()
.domain(d3.range(dataset.length))
.range(d3.schemeCategory10);
// 3、新建一个饼状图
var pie = d3.pie();
// 4、新建一个弧形生成器
//新建一个弧形生成器
var innerRadius = 0;//内半径
var outerRadius = 100;//外半径
var arc_generator = d3.arc()
.innerRadius(0)
.outerRadius(100);
// 5、利用饼状图生成器转换数据
//将原始数据变成可以绘制饼状图的数据,
var pieData = pie(dataset);
//在浏览器的控制台打印pieData
console.log(pieData);
// 6、开始绘制,老规矩,先为每一个扇形及其对应的文字建立一个分组
var gs = g.selectAll(".g")
.data(pieData)
.enter()
.append("g")
.attr("transform","translate("+width/2+","+height/2+")")//位置信息
// 7、绘制扇形
//绘制饼状图的各个扇形
gs.append("path")
.attr("d",function(d){
return arc_generator(d);//往弧形生成器中出入数据
})
.attr("fill",function(d,i){
return colorScale(i);//设置颜色
});
// 8、文字
//绘制饼状图上面的文字信息
gs.append("text")
.attr("transform",function(d){//位置设在中心处
return "translate("+arc_generator.centroid(d)+")";
})
.attr("text-anchor","middle")
.text(function(d){
return d.data;
})
力导向图
力导向图(Force-Directed Graph),是绘图的一种算法。在二维或三维空间里配置节点,节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。
- d3.forceSimulation() ,新建一个力导向图,
- d3.forceSimulation().force(),添加或者移除一个力;对于d3.forceSimulation().force(name),也就是当force中只有一个参数,这个参数是某个力的名称,那么这段代码返回的是某个具体的力,(根据上面图片官方对force的说明也可以知道),例如d3.forceSimulation().force(“link”),则返回的是d3.forceLink()这个力,注意对照上面的图片,这个用法在下面会经常被用到
- d3.forceSimulation().nodes(),输入是一个数组,然后将这个输入的数组进行一定的数据转换,例如添加坐标什么的
- d3.forceLink.links(),这里输入的也是一个数组(边集),然后对输入的边集进行转换,等一下我们会看到,它到底被转换成什么样子的 tick函数,这个函数对于力导向图来说非常重要,因为力导向图是不断运动的,每一时刻都在发生更新,所以需要不断更新节点和连线的位置
- d3.drag(),是力导向图可以被拖动
// 1、数据准备
var marge = {top:60,bottom:60,left:60,right:60}
var svg = d3.select("svg")
var width = svg.attr("width")
var height = svg.attr("height")
var g = svg.append("g")
.attr("transform","translate("+marge.top+","+marge.left+")");
//准备数据
var nodes = [//节点集
{name:"湖南邵阳"},
{name:"山东莱州"},
{name:"广东阳江"},
{name:"山东枣庄"},
{name:"泽"},
{name:"恒"},
{name:"鑫"},
{name:"明山"},
{name:"班长"}
];
var edges = [//边集
{source:0,target:4,relation:"籍贯",value:1.3},
{source:4,target:5,relation:"舍友",value:1},
{source:4,target:6,relation:"舍友",value:1},
{source:4,target:7,relation:"舍友",value:1},
{source:1,target:6,relation:"籍贯",value:2},
{source:2,target:5,relation:"籍贯",value:0.9},
{source:3,target:7,relation:"籍贯",value:1},
{source:5,target:6,relation:"同学",value:1.6},
{source:6,target:7,relation:"朋友",value:0.7},
{source:6,target:8,relation:"职责",value:2}
];
// 2、设置一个颜色比例尺
//设置一个color的颜色比例尺,为了让不同的扇形呈现不同的颜色
var colorScale = d3.scaleOrdinal()
.domain(d3.range(nodes.length))
.range(d3.schemeCategory10);
// 3、新建一个力导向图
var forceSimulation = d3.forceSimulation()
.force("link",d3.forceLink())
.force("charge",d3.forceManyBody())
.force("center",d3.forceCenter());
// 4、生成节点数据
//生成节点数据
forceSimulation.nodes(nodes)
.on("tick",ticked);//这个函数很重要,后面给出具体实现和说明
// 5、生成边集数据
//生成边数据
forceSimulation.force("link")
.links(edges)
.distance(function(d){//每一边的长度
return d.value*100;
})
// 6、设置图形中心位置
//设置图形的中心位置
forceSimulation.force("center")
.x(width/2)
.y(height/2);
// 7、输出顶点集和边集
console.log(nodes);
console.log(edges);
// 8、绘制边
//绘制边
var links = g.append("g")
.selectAll("line")
.data(edges)
.enter()
.append("line")
.attr("stroke",function(d,i){
return colorScale(i);
})
.attr("stroke-width",1);
// 9、边上的文字
var linksText = g.append("g")
.selectAll("text")
.data(edges)
.enter()
.append("text")
.text(function(d){
return d.relation;
})
// 10、老规矩,先建立用来放在每个节点和对应文字的分组
var gs = g.selectAll(".circleText")
.data(nodes)
.enter()
.append("g")
.attr("transform",function(d,i){
var cirX = d.x;
var cirY = d.y;
return "translate("+cirX+","+cirY+")";
})
.call(d3.drag()
.on("start",started)
.on("drag",dragged)
.on("end",ended)
);
// 11、节点和文字
//绘制节点
gs.append("circle")
.attr("r",10)
.attr("fill",function(d,i){
return colorScale(i);
})
//文字
gs.append("text")
.attr("x",-10)
.attr("y",-20)
.attr("dy",10)
.text(function(d){
return d.name;
})
// 12、ticked函数的实现
// 这里写的都是位置信息,所以你在绘制相应的图形元素的时候,位置信息就不那么重要的,而且,建议先写完这个函数后,在进行测试
function ticked(){
links.attr("x1",function(d){return d.source.x;})
.attr("y1",function(d){return d.source.y;})
.attr("x2",function(d){return d.target.x;})
.attr("y2",function(d){return d.target.y;});
linksText.attr("x",function(d){
return (d.source.x+d.target.x)/2;
}).attr("y",function(d){
return (d.source.y+d.target.y)/2;
});
gs.attr("transform",function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
// 13、drag
function started(e,d){
if(e.active){
forceSimulation.alphaTarget(0.8).restart();//设置衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0,1]
}
d.fx = d.x;
d.fy = d.y;
}
function dragged(e,d){
d.fx = e.x;
d.fy = e.y;
}
function ended(e,d){
if(!e.active){
forceSimulation.alphaTarget(0);
}
d.fx = null;
d.fy = null;
}
树状图
树状图,可表示节点之间的包含与被包含关系
- d3.hierarchy(),层级布局,需要和tree生成器一起使用,来得到绘制树所需要的节点数据和边数据;
- d3.hierarchy().sum() ,后序遍历;
- d3.tree(),创建一个树状图生成器;
- d3.tree().size(),定义树的大小;
- d3.tree().separation(),定义邻居节点的距离;
- node.descendants()得到所有的节点,已经经过转换的数据;
- node.links(),得到所有的边,已经经过转换的数据;
- d3.linkHorizontal(),创建一个贝塞尔生成曲线生成器,当然不止只有水平的,还有垂直的(d3.linkVertical())
d3.hierarchy
- node.ancestors - 从当前节点开始返回其祖先节点数组.
- node.leaves - 返回当前节点为根节点的子树的叶节点.
- node.find - 查找指定节点.
- node.path - 返回从当前节点到指定目标节点的最短路径.
- node.sum - 评估和汇总定量值.
- node.sort - 排序所有的后代兄弟节点.
- node.count - 统计叶节点的个数.
- node.each - 广度优先遍历当前子树.
- node.eachAfter - 后续遍历当前子树.
- node.eachBefore - 前序遍历当前子树.
- node.copy - 拷贝一个当前节点为根节点的子树的副本.
- node.data - 关联的数据
- node.depth - 当前节点的深度, 根节点为 0.
- node.height - 当前节点的高度, 叶节点为 0.
- node.parent - 当前节点的父节点, 根节点为 null.
- node.children - 当前节点的孩子节点(如果有的话); 叶节点为 undefined.
- d3.hierarchy默认子节点取得是children属性,当然也可以自定义: d3.hierarchy(data, d => d.child)
d3.stratify
对于扁平数据,我们可以用d3.stratify来实现数据的层次化
let data = [
{"name": "Eve", "parent": ""},
{"name": "Cain", "parent": "Eve"},
{"name": "Seth", "parent": "Eve"},
{"name": "Enos", "parent": "Seth"},
{"name": "Noam", "parent": "Seth"},
{"name": "Abel", "parent": "Eve"},
{"name": "Awan", "parent": "Eve"},
{"name": "Enoch", "parent": "Awan"},
{"name": "Azura", "parent": "Eve"}
]
const dataSet = d3.stratify()
.id(function(d) { return d.name; })
.parentId(function(d) { return d.parent; })
(data)
//画布大小
var width = 1000;
var height = 700;
//在 body 里添加一个 SVG 画布
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// 1、数据准备
//定义边界
var marge = {top:50, bottom:0, left:10, right:0};
var svg = d3.select("svg");
var width = svg.attr("width");
var height = svg.attr("height");
var g = svg.append("g")
.attr("transform","translate("+marge.top+","+marge.left+")");
var scale = svg.append("g")
.attr("transform","translate("+marge.top+","+marge.left+")");
//指定缩放范围,缩放功能
const zoom = d3
.zoom()
.scaleExtent([0.2, 4])
.on("zoom", (e) => {
g.attr("transform", `translate(${e.transform.x},${e.transform.y}) scale(${e.transform.k})`);
});
//动画持续时间
g.transition()
.duration(this.duration)
.call(zoom.transform, d3.zoomIdentity);
svg.call(zoom);
//数据
var dataset = {
name:"中国",
children:[
{
name:"浙江",
children:[
{name:"杭州" ,value:100},
{name:"宁波",value:100},
{name:"温州",value:100},
{name:"绍兴",value:100}
]
},
{
name:"广西",
children:[
{
name:"桂林",
children:[
{name:"秀峰区",value:100},
{name:"叠彩区",value:100},
{name:"象山区",value:100},
{name:"七星区",value:100}
]
},
{name:"南宁",value:100},
{name:"柳州",value:100},
{name:"防城港",value:100}
]
},
{
name:"黑龙江",
children:[
{name:"哈尔滨",value:100},
{name:"齐齐哈尔",value:100},
{name:"牡丹江",value:100},
{name:"大庆",value:100}
]
},
{
name:"新疆" ,
children:
[
{name:"乌鲁木齐"},
{name:"克拉玛依"},
{name:"吐鲁番"},
{name:"哈密"}
]
}
]
};
// 2、创建一个层级布局
var hierarchyData = d3.hierarchy(dataset)
.sum(function(d){
return d.value;
});
// 3、创建一个树状图
//创建一个树状图
var tree = d3.tree()
.size([width-400,height-200])
.separation(function(a,b){
return (a.parent==b.parent?1:2)/a.depth;
})
// 4、初始化树状图,也就是传入数据,并得到绘制树基本数据
var treeData = tree(hierarchyData);
// 5、得到边和节点(已经完成转换的)
var nodes = treeData.descendants();
var links = treeData.links();
// 6、输出边和节点
console.log(nodes);
console.log(links);
// 7、创建一个贝塞尔生成曲线生成器
var Bézier_curve_generator = d3.linkHorizontal()
.x(function(d) { return d.y; })
.y(function(d) { return d.x; });
// 直角连接线 by wushengyuan
var drawLink = ({ source, target }) =>{
const halfDistance = (target.y - source.y) / 2;
const halfY = source.y + halfDistance;
return `M${source.x},${source.y} L${source.x},${halfY} ${target.x},${halfY} ${target.x},${target.y}`;
}
// 8、绘制边
g.append("g")
.selectAll("path")
.data(links)
.enter()
.append("path")
.attr("d",function(d){
var start = {x:d.source.x,y:d.source.y};
var end = {x:d.target.x,y:d.target.y};
return Bézier_curve_generator({source:start,target:end});
})
.attr("fill","none")
.attr("stroke","yellow")
.attr("stroke-width",1);
// 9、老规矩,先建立用来放在每个节点和对应文字的分组
var gs = g.append("g")
.selectAll("g")
.data(nodes)
.enter()
.append("g")
.attr("transform",function(d){
var cx = d.x;
var cy= d.y;
return "translate("+cy+","+cx+")";
});
// 10、绘制节点和文字
//绘制节点
gs.append("circle")
.attr("r",6)
.attr("fill","white")
.attr("stroke","blue")
.attr("stroke-width",1);
//文字
gs.append("text")
.attr("x",function(d){
return d.children?-40:8;
}) // 如果某节点有子节点,则对应的文字前移
.attr("y",-5)
.attr("dy",10)
.text(function(d){
return d.data.name;
})