d3.js 基础学习
1. 下载安装
-
npm 安装
npm install d3
-
类型支持(ts)
npm install @types/d3
-
导入
import * as d3 from "d3"
2. 如何使用d3.js画图
eg:
d3.select('body') .append('svg') .attr('width',500) .attr('height',500)
-
这行代码的意思是:选择 body 标签,添加 svg 元素,设置 svg 元素的宽度和高度为 500。
2.1 选择和操作
-
d3选择元素提供了两个函数
-
select
-
selectAll
-
-
选择元素后可以操作元素,操作元素主要分为增加 ( append )、插入 ( insert )、删除元素 ( remove ) 和设置获取元素属性 ( attr, style )。下面用一个例子展示 D3 选择和操作元素,便于理解,大家可以自己逐行添加运行。
2.2 数据绑定
-
D3中数据绑定提供了datum()和data()两个函数
-
datum():绑定一个数据到选择集上
-
这个方法不会进行数据绑定的(update、enter、exit),只能在现有的元素上绑定数据。如果 value 是数组会自动转成字符串
-
-
data():更常用, 绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定
var body = d3.select("body"); var p = body.selectAll("p"); p.data(dataset) .text(function(d, i){ return d; });
-
-
数据绑定是将数据绑定到DOM元素上,这是D3最大的特色,也是D3的设计哲学
2.3 插入,删除元素
插入元素涉及的函数有两个
-
append() :在选择集末尾插入元素
body.append("p") .text("append p element");
-
insert() : 在选择集前面插入元素
在 body 中 id 为 myid 的元素前添加一个段落元素。 body.insert("p","#pear-id") .text("insert p element");
2.4 . 比例尺
-
原因在于真实数据与创建数据可视化的视觉元素大小之间存在差异。
let scale = d3.scaleLinear() .domain([0,10000]) .range([0,100])
-
其中 scaleLinear() 对应法则,domain() 是定义域,range() 是值域。
3. 做一个简单的图表
3.1 画布
-
HTML 5 提供两种强有力的“画布”:SVG 和 Canvas。
3.1.1 添加画布
-
使用 D3 在 body 元素中添加 svg
var svg = d3.select("body") //选择文档中的body元素 .append("svg") //添加一个svg元素 .attr("width", width) //设定宽度 .attr("height", height); //设定高度
-
绘制矩形
<svg> <rect></rect> <rect></rect> </svg>
-
矩形的属性
-
x:矩形左上角的 x 坐标
-
y:矩形左上角的 y 坐标
-
width:矩形的宽度
-
height:矩形的高度
-
-
绑定属性
var dataset = [ 250 , 210 , 170 , 130 , 90 ]; . . svg.selectAll("rect") //选择svg内所有的矩形 .data(dataset) //绑定数组 .enter() //指定选择集的enter部分 .append("rect") //添加足够数量的矩形元素 .attr("fill","steelblue");//是给矩形元素设置颜色
3.2 比例尺
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
-
其中,d3.scaleLinear() 返回一个线性比例尺。domain() 和 range() 分别设定比例尺的定义域和值域。在这里还用到了两个函数,它们经常与比例尺一起出现:
-
d3.max()
-
d3.min() 这两个函数能够求数组的最大值和最小值,是 D3 提供的。按照以上代码,
-
3.3 坐标轴
-
D3 中提供了专门的坐标轴模块d3-axis,只需要几行代码就可以生成各种各样的坐标轴。一般情况下,坐标轴要与比例尺一起使用。记住这句话,离开比例尺的坐标轴就是耍流氓。
-
<g>
分组元素 ,是 SVG 画布中的元素,意思是 group。此元素是将其他元素进行组合的容器,在这里是用于将坐标轴的其他元素分组存放。 -
D3 提供了轴生成器:d3.axisTop()、d3.axisRight()、 d3.axisBottom()、d3.axisLeft()。它为我们完成了以上工作。
3.3.1 定义坐标轴
var dataset = [2.5, 2.1, 1.7, 1.3, 0.9]; //数据 //定义线性比例尺 var xScale = d3 .scaleLinear() .domain([0, d3.max(dataset)]) .range([0, 250]); //定义坐标轴 var axis = d3 .axisBottom(xScale) //定义一个axis并指定刻度的方向为bottom(朝下)且指定比例尺为xScale .ticks(7); //指定刻度的数量
4. 动态图表
-
transition() 启动过渡效果
4. 树状图
树状图,可表示节点之间的包含与被包含关系。
4.1 树状图知识点:
d3.hierarchy(),层级布局,需要和tree生成器一起使用,来得到绘制树所需要的节点数据和边数据; d3.hierarchy().sum() ,后序遍历; d3.tree(),创建一个树状图生成器; d3.tree().size(),定义树的大小; d3.tree().separation(),定义邻居节点的距离; node.descendants()得到所有的节点,已经经过转换的数据; node.links(),得到所有的边,已经经过转换的数据; d3.linkHorizontal(),创建一个贝塞尔生成曲线生成器,当然不止只有水平的,还有垂直的(d3.linkVertical())
4.2 案例
<script lang="ts" setup> import * as d3 from 'd3' import {onMounted} from "vue"; // var width:any = svg.attr("width"); // var height:any = svg.attr("height"); onMounted(() => { interface HierarchyNode { name: string; value?: number; children?: HierarchyNode[]; } // 需要渲染的数据 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: 300}, {name: "大庆", value: 100} ] }, { name: "新疆", children: [ {name: "乌鲁木齐"}, {name: "克拉玛依"}, {name: "吐鲁番"}, {name: "哈密"} ] } ] }; //定义边界 var marge = {top: 50, bottom: 0, left: 10, right: 0}; var svg = d3.select("svg") var g = svg.append("g") .attr("transform", "translate(" + marge.top + "," + marge.left + ")"); var scale = svg.append("g") .attr("transform", "translate(" + marge.top + "," + marge.left + ")"); //扁平化获得树状数据 var hierarchyData = d3.hierarchy<HierarchyNode>(dataset) .sum(function (d) { console.log(d.value) return d.value; }); console.log(hierarchyData) //创建一个树状图 var tree = d3.tree() .size([400, 200]) .separation(function (a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth }) //初始化树状图,也就是传入数据,并得到绘制树的基本数据 var treeData = tree(hierarchyData); //得到边和节点(已经完成转换的) var nodes = treeData.descendants(); var links = treeData.links(); // 定义节点类型接口 interface TreeNode { x: number; // 节点的 x 坐标 y: number; // 节点的 y 坐标 // 如果需要,可以添加其他属性,例如子节点 children?: TreeNode[]; } // 创建树形贝塞尔曲线生成器 const Bézier_curve_generator = d3.linkHorizontal<TreeNode, TreeNode>() .x((d) => d.y) // x 坐标使用 y 属性 .y((d) => d.x); // y 坐标使用 x 属性 //绘制边 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); //建立用来放在每个节点和对应文字的分组<g> 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 + ")"; }); //绘制节点 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; }) }) </script> <template> <div class="container"> <svg> </svg> </div> </template> <style lang="less"> </style>