技术分享——用D3.js框架V5版本入门基础
一、前言
在最近公司项目中,因为需求原因需要使用D3.js实现力导向图。所以花了一些时间对D3.js这个绘制图表的框架进行了研究。
-
和Echarts相比D3的优势在于:
- 使用svg绘制图形,不依赖分辨率;
- 支持事件处理器,能满足交互较复杂的需求;
- 基于xml绘制图形,可以操作dom
-
缺点在于:
- 支持IE9以上的主流浏览器,不兼容IE6 ;
- 复杂度高,会减慢页面的渲染速度;
- 学习成本大
所以对于客户需求要求的图表拥有大量的用户交互场景,可以选择用D3实现。
因为项目使用的是React框架进行前端页面的开发,本编文章主要是D3在React上的实现。
二、安装和导入
-
安装:
npm install -save d3
-
前段项目中导入:
import * as d3 from 'd3';
三、Hello World
老套路,第一段代码永远是伟大的hello world;
class App extends Component {
componentDidMount() {
let div = d3.select(this.chartRef);
div.append("p").text("Hello World");
}
render() {
return (
<div className="App">
<div ref={r => this.chartRef = r}></div>
</div>
);
}
}
可以看到d3.select()
是d3的选择元素的方法,从字面意思上理解,它是选择某个dom标签然后在对这个标签进行操作。append()
是d3的添加元素的方法,如上代码,意思是添加一个<p>标签然后在<p>标签里显示"Hello World"。
运行效果如下:
image-20190327193727961现力导向图-图1.png)
四、元素选择和数据绑定
4.1、元素选择
d3所有的操作基本都是基于dom标签进行的,所以选择元素是d3里最基本也是最重要的功能;如果没有这一步,下面的操作也进行不下去。
在上一节中,我们可以看到d3.select()
方法,这个方法就是d3提供的一个选择元素的方法。在d3中一共提供了连个选择元素的方法:
-
select(selector)
:选中与指定选择器字符串匹配的第一个元素,返回单元素选择结果。如果当前文档中没有匹配的元素则返回空的选择。如果有多个元素被选中,只有第一个匹配的元素(在文档遍历次序中)被选中。 -
selectAll(selector)
:选中匹配指定选择器的所有的元素。这些元素会按照文档的遍历顺序(从上到下)选择。如果当前文档中没有匹配的元素则返回空的选择。代码演示:
let body = d3.select("body") //选中文档中body标签
let test1 = d3.select("#test1") //选中id为test1的标签
let test1 = d3.select(".test1") //选中class为test1的标签
let p = d3.select("p") //选中文档中第一个<p>标签
let ps = d3.selectAll("p") //选中文档中所有的<p>标签
4.2、数据绑定
获得选择的元素后,下一步就是要将元素绑定数据,才能绘制出相应的图表。所以数据绑定对于d3来说意识一个重要的功能。
d3提供了两个方法绑定数据:
-
data()
:将元素与数据绑定; -
datum()
:设置或获取元素绑定的数据集(不进行数据与元素个数的对比)。我们可以通过下面两个例子直观的观察他们的区别;
class App extends Component {
componentDidMount() {
let datas = ["a", "b", "c"]
let div = d3.select("#secondDiv");
let ps = div.selectAll("p").datum(datas).text(function (d, i) {
return "第" + i + "个<p>显示" + d;
})
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p>aa</p>
<p>bb</p>
<p>cc</p>
</div>
</div>
);
}
}
运行效果如下:
image-20190328170551010.png
接下来我们再看看data()
方法的效果:
class App extends Component {
componentDidMount() {
let datas = ["a", "b", "c"]
let div = d3.select("#secondDiv");
let ps = div.selectAll("p").data(datas).text(function (d, i) {
return "第" + i + "个<p>显示" + d;
})
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p>aa</p>
<p>bb</p>
<p>cc</p>
</div>
</div>
);
}
}
运行效果如下:
image-20190328171040167.png
可以看出data()
和datum()
的区别在于data()
会将数据的个数和元素的个数进行一一对应,而datum()
并不会做这种对应,它直接就将所有数据绑定在每个元素上;data()
使用较多。
五、数据和元素的关系
在上一节我们讲到元素的选择和数据绑定,data()
方法会将数据和元素一一对应上,同时也举出了一个代码示例;但是上一节的事例,我们可以看到数据的长度和元素的个数正好是相等的,所以数据和元素是可以一一对应上的,如果情况是数据比元素多或者元素比数据多呢?不说废话,我们代码伺侯:
- 第一种情况,数据比元素多;
class App extends Component {
componentDidMount() {
let datas = ["a", "b", "c", "d"];
let div = d3.select("#secondDiv");
let ps = div.selectAll("p").data(datas).text(function (d, i) {
return "第" + i + "个<p>显示" + d;
})
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p>aa</p>
<p>bb</p>
<p>cc</p>
</div>
</div>
);
}
}
运行效果:
image-20190328171040167.png 可以看到如果数据的个数比元素的个数多,那么只会展示前三个数据,而第四个数据将不展示出来。
- 第二种情况,元素比数据多:
class App extends Component {
componentDidMount() {
let datas = ["a", "b", "c"]
let div = d3.select("#secondDiv");
let ps = div.selectAll("p").data(datas).text(function (d, i) {
return "第" + i + "个<p>显示" + d;
})
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p>aa</p>
<p>bb</p>
<p>cc</p>
<p>dd</p>
</div>
</div>
);
}
}
运行效果:
image-20190328172952264.png
可以看出元素比数据多时,多出的元素会显示本身的内容,毕竟没有数据展示。
我们在项目中,肯定不会去手动的修改元素的个数使元素和数据个数相等。我们肯定希望元素的个数可以根据数据的个数自动的添加和删除。为此,d3提供了三个概念:update、enter和exit;
5.1、基本概念
update、enter和exit的概念非常简单,要理解它们下面的一张图(非原创)就够了:
image-20190328181125747.png 可以看出update就是元素和数据能够对应上的部分,而enter就是多出的数据部分,exit就是多出的元素部分。
5.2、update和enter的使用
现在有个需求,是元素比数据少时,动态添加元素,并且显示多出的数据内容。
代码如下:
class App extends Component {
componentDidMount() {
let datas = ["a", "b", "c", "d", "e"];
let div = d3.select("#secondDiv");
let update = div.selectAll("p").data(datas);
let enter = update.enter().append("p");
update.text(function (d, i) {
return "第" + i + "个update<p>显示" + d;
});
enter.text(function (d, i) {
return "第" + i + "个enter<p>显示" + d;
})
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p>aa</p>
<p>bb</p>
<p>cc</p>
</div>
</div>
);
}
}
运行结果:
image-20190328181846279.pnglet update = div.selectAll("p").data(datas);
:表示获得update部分的元素;
let enter = update.enter().append("p");
:表示获得enter部分,并且添加<p>标签;
5.2、update和exit的使用
现在有个需求,是元素比数据多时,动态删除元素。
代码如下:
class App extends Component {
componentDidMount() {
let datas = ["a", "b", "c"];
let div = d3.select("#secondDiv");
let update = div.selectAll("p").data(datas);
let exit = update.exit();
update.text(function (d, i) {
return "第" + i + "个update<p>显示" + d;
});
exit.remove();
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p>aa</p>
<p>bb</p>
<p>cc</p>
<p>dd</p>
<p>ee</p>
</div>
</div>
);
}
}
运行效果:
image-20190328182513201.pnglet exit = update.exit();
:表示获得exit部分;
exit.remove();
:删除exit部分的元素;
六、元素的添加和删除
上一节我们简单的接触到了添加和删除元素的方法:append()
和remove()
。这一节我们
将详细的了解d3中元素的添加和删除。
6.1、元素添加
d3提供的添加元素的方法有两个:
-
append()
:在选择集的尾部添加元素; -
insert()
:在选择集的头部添加元素;
append()
class App extends Component {
componentDidMount() {
let div = d3.select("#secondDiv");
let ps = div.selectAll("p");
let newPs = ps.append("p");
newPs.text(function (d, i) {
return i + "";
})
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p>aa</p>
<p>bb</p>
</div>
</div>
);
}
}
运行效果:
image-20190328192149567.pnginsert()
class App extends Component {
componentDidMount() {
let div = d3.select("#secondDiv");
let newP = div.insert("p", "#aa");
newP.text("insert a new dom")
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p id="aa">aa</p>
<p>bb</p>
</div>
</div>
);
}
}
运行效果:
image-20190328195251151.png let newP = div.insert("p", "#aa");
表示在id为"aa"的元素前,添加一个<p>元素
6.2、删除元素
删除元素很简单,只需要remove()
方法即可。
class App extends Component {
componentDidMount() {
let div = d3.select("#secondDiv");
let aa = d3.select("#aa");
aa.remove();
}
render() {
return (
<div className="App">
<div id="secondDiv">
<p id="aa">aa</p>
<p>bb</p>
</div>
</div>
);
}
}
运行效果:
image-20190328200236393.png七、画一个矩形图
这一节通过之前学习的基础知识,画出一个矩形图。先不说废话,先贴出实现代码,之后最讲解:
class App extends Component {
componentDidMount() {
let datas = [100, 85, 30, 56, 96, 50, 66, 123, 150, 133]; //数据集,控制矩阵的高度
let bottomMarge = 200; //固定底部的位置
let leftMarge = 200; //固定最左边的位置
let rectWidth = 30; //矩形的宽度
let rectMargin = 10; //矩形之间的间距
let svg = d3.select("svg").attr("width", "100%").attr("height", 1000); //获得svg的画布
svg.selectAll("rect")
.data(datas)
.enter()
.append("rect") //插入矩形
.attr("x", function (d, i) {
return leftMarge + (i * (rectWidth + rectMargin)) //计算矩形的x坐标
})
.attr("y", function (d) {
return bottomMarge - d; //计算矩形的y坐标,保证矩形底部的位置是200
})
.attr("width", rectWidth) //矩形的宽度固定为30
.attr("height", function (d) {
return d; //数据对应每个矩形的高度
})
.attr("fill", "#f00"); //用红色填充矩形
}
render() {
return (
<div className="App">
<svg/>
</div>
);
}
}
首先准备绘制需要使用的数据let datas = [100, 85, 30, 56, 96, 50, 66, 123, 150, 133];
,之后在准备一些绘制所需要的必要参数:
let bottomMarge = 200; //固定底部的位置
let leftMarge = 200; //固定最左边的位置
let rectWidth = 30; //矩形的宽度
let rectMargin = 10; //矩形之间的间距
数据和参数准备好后,就需要获取画布元素,这里我们使用的是svg画布:
let svg = d3.select("svg").attr("width", "100%").attr("height", 1000);
attr()
方法是给元素添加一个属性,比如上面代码是给svg添加width和height属性,值分别是"100%"和1000。
获得画布之后就要开始绘画矩形图,绘画时最主要的是确定绘制的位置和矩形的宽高和颜色,如下:
svg.selectAll("rect")
.data(datas)
.enter()
.append("rect") //插入矩形
.attr("x", function (d, i) {
return leftMarge + (i * (rectWidth + rectMargin)) //计算矩形的x坐标
})
.attr("y", function (d) {
return bottomMarge - d; //计算矩形的y坐标,保证矩形底部的位置是200
})
.attr("width", rectWidth) //矩形的宽度固定为30
.attr("height", function (d) {
return d; //数据对应每个矩形的高度
})
.attr("fill", "#f00"); //用红色填充矩形
运行效果:
image-20190328203352650.png总结
以上只是d3最基本的使用方法,但是也是最重要的部分,后面的复杂的功能都是基于以上基础功能拓展的,所以一定要熟练掌握。后面还会和大家继续分享进阶部分。
网友评论