如果你关注前端数据可视化,那你肯定据说过D3.js。

什么是D3.js

D3的全称是 Data-Driven Documents(数据驱动文档),Documents指的是能被浏览器渲染的dom元素,比方divspansvg等。

对元素进行增删改,是JS的惯例操作。而D3.js则是提供一系列的办法,让咱们能够疾速不便地解决数据,并依据这些数据操作dom元素

D3.js简介

D3.js诞生于2011年,它的次要作者是Mike Bostock,目前在GitHub上的star数量有102k

尽管咱们罕用D3.js绘制各种图表,但它不是图表库,更像是图形工具库,它比图表库更加底层。

如果比照echarts:echarts的绘制单位是图表;而D3的绘制单位是图形,它须要咱们本人将图形组合起来,失去想要的图表。

以绘制一个柱状图为例:

  • echarts。在配置里,传入图表类型和数据 --> 失去柱状图。
  • D3。增加画布 --> 依据数据绘制矩形(柱子) --> 增加坐标轴 --> 失去柱状图。

能够看出,应用echarts绘图更加简略。应用D3绘图更加灵便,容易定制,同时它要求你了解更多货色,所以学习曲线会比拟平缓。

要不要学习D3.js

为了疾速开发,咱们通常会抉择简略易用的图表库。那么,D3.js还有必要学习吗?

如果你有以下几点需要,强烈推荐你学习:

  • 当现有的图表库无奈满足你的展现需要时,可是试试D3
  • 当你要解决的数据文件格式是CSV, TSV, JSON, XML时,D3能够间接解决这些格局的数据。
  • 如果你是图表发烧友,想要得心应手地设计、实现各种各样的酷炫图表,那你肯定要学习D3
  • 如果你不满足绘制图表时只能调用API,而是想要更深刻地了解图表的组成、含意、绘制过程等,D3也会满足你的要求。并且它提供了一系列工具函数,让你在绘制过程中既晓得本人在做哪一步,同时又很不便。

一些入门敌对的学习材料

1、D3的官网 ,官网下面有很多图表案例,有残缺代码,和文档配合应用能够更快把握。

2、我目前看过感觉还不错的两本入门书:

《数据可视化实战:应用D3设计交互式图表》(斯科特.默里)

《快学熟用D3》(菲利普.K.贾纳特)

3、视频教程:

[数据可视化教程@基于D3.js] (https://www.bilibili.com/vide...) :口音规范,比拟好懂,手把手写代码,很适宜入门。

[D3.js和数据可视化 Fullstack D3 and Data Visualization] (https://www.bilibili.com/vide...) :口音听起来有点吃力,然而对D3有很多简洁精准的了解,作者的博客:https://wattenberger.com/。

入门实战:用D3.js绘制柱状图


(D3的所有模块,引自 https://wattenberger.com/blog/d3)

D3尽管API有很多,然而被拆分成了很多模块。一些是绘制图表时基本上都会用到的根底模块,比方操作数据的模块Array,操作dom元素Selections、比例尺Scale、绘制SVG形态Line\Polygon\Path等。另一些是特定的功能模块,地图、色调、动画、定制图表等。

在github( https://github.com/d3 ) 上列出了有所有模块,及其API文档:

D3.js根本应用

这篇文章中咱们次要探讨根底模块的应用,最终目标是绘制一个这样的柱状图。

操作元素

在操作元素方面,D3Jquery十分类似,也反对链式语法(d3.selectAll('div').style(...).text(...)),可能间断地调用函数。如果你有Jquery的应用教训,那么了解D3肯定毫不费力。

<div></div><div></div><script src="./d3/d3.js"></script><script>   // 抉择所有div,将它们的宽度设为300px,背景设为深海绿,文字设为hello world   d3.selectAll('div')   .style('width', '300px').style('background', 'darkseagreen')   .text('hello world')</script>


D3抉择元素的根底办法有两个:

  • d3.select:返回匹配指定 CSS 选择器的第一个元素,类比querySelector
  • d3.selectAll:返回匹配指定 CSS 选择器的所有元素,类比querySelectorAll

在抉择元素后,咱们能够对抉择集中的元素进行一系列操作,常见的办法如:

  • style:设置元素的css款式
  • attr:设置元素的属性
  • text/html:设置元素的内容
  • append: 为元素增加子元素,新子元素排在已有子元素的前面
  • insert: 为元素增加子元素,新子元素排在指定子元素的后面,如果没有指定子元素,则排在已有子元素的前面
  • remove: 删除元素

appendinsert为例。

  • append:为元素增加子元素,新子元素排在已有子元素的前面

    <body>  <div id="test">existing div</div>  <script src="./d3/d3.js"></script>  <script>      d3.select('body')      .append('div')      .text('appended div')  </script></body>

  • insert:为元素增加子元素,新子元素排在指定子元素的后面,如果没有指定子元素,则排在已有子元素的前面
<body>    <div id="test">existing div</div>    <script src="./d3/d3.js"></script>    <script>        d3.select('body')        .insert('div', '#test')    //新子元素排在指定子元素(id=test)的后面        //如果是insert('div'),则新插入元素会排在已有子元素的前面,和下面append成果一样        .text('inserted div')    </script></body>

基于数据操作元素

D3的外围是基于数据操作Dom元素,咱们曾经理解了如何如何操作元素,那么怎么将数据和 DOM 连接起来呢?

要实现的小指标:将一个数组的数据设为一组div的内容。

<div></div><div></div><div></div><script>    const sports = ['football', 'swimming', 'tennis']    d3.selectAll('div')        .data(sports)        .text( (d, i) => d )</script>


当咱们通过d3.selectAll('div')失去蕴含所有div的抉择集,调用data(sports)就将sports数组的数据绑定到了该抉择集上。这是D3帮咱们实现的工作,通过console.dir(document.querySelector('div'))咱们能够看到数据被绑定到了元素的__data__ 属性上(该属性是由D3增加的)。

当咱们要应用数据赋值时,.text( (d, i) => d ) ,用到了匿名函数(d, i) => d。事实上,如果抉择集调用的办法要拜访被绑定的数据,个别应用这种匿名函数的形式。

匿名函数的参数d代表数据(如football),i代表数据的索引(football对应的是0)。匿名函数的返回值就是咱们将要应用的值,能够依据须要批改。比方.text( (d, i) => d + ',我喜爱' )

Enter、Update、Exit

数据往往是多变的,咱们不得不思考的状况就是,将dom抉择集和数据进行绑定(调用data办法)的时候,当数据和dom抉择集数量不对等时,咱们要怎么更新dom?

D3中的Join思维就用于解决这个问题,它的外围概念是Enter、Update、Exit

举个例子,如果数据是['football', 'swimming', 'tennis'],但dom抉择集只有一个div,此时就会有两个数据没有dom元素与之对应,D3会创立两个空的占位元素与数据对应,这部分占位元素汇合就称为 Enter

相似上面的这段代码在D3绘图会重复呈现:

 d3.selectAll('div')    .data(sports)    .enter()    //当数据集调用data办法后,再调用enter()办法时,会返回对占位元素汇合的援用。    .append('div')    //为占位元素增加实在的dom元素,这里是`div`

接着上例,已有的一个div有数据与之对应,这部分元素汇合被称为 Update

<div></div><script>    const sports = ['football', 'swimming', 'tennis']    const update = d3.selectAll('div').data(sports)  //当数据集调用data办法,间接返回的就是Update这部分元素汇合                update.enter()    //获取占位元素汇合的援用        .append('div')   //为占位元素增加实在的dom元素        .text( (d, i) => d + ' enter')  //设置文本内容            update.text((d,i) => d + ' update')    //设置Update这部分元素的文本内容</script>

如果数据是['football', 'swimming', 'tennis'],而dom抉择集有4个div,则会有一个元素没有数据绑定,这部分元素汇合被称为 Exit

<div></div><div></div><div></div><div></div><script>    const sports = ['football', 'swimming', 'tennis']    const update = d3.selectAll('div').data(sports)                update.exit()   //获取Exit元素汇合的援用        .text('exit')   //设置文本内容,但通常是调用remove办法删除元素            update.text((d,i) => d + ' update')</script>


绘制柱状图

D3 并没有规定肯定要应用 svg 中绘图,咱们也能够用 div 来绘制柱状图。但从网页性能和图表灵活性方面思考,还是倡议应用 svg 。D3提供了很多 svg 图形的生成器,Line\Polygon\Path等,简化了绘制过程。对于SVG的根底应用,参考svg从入门到图标绘制和组件封装 。

柱状图次要由矩形、坐标轴、文字标签等组成,咱们首先来绘制矩形(柱子)。

const sales = [20,10,20,50,30,60,35];    //数据//定义SVG元素的高度、宽度,柱子的宽度,柱子之间的距离(柱子的高度由数据决定)const svgHeight = 300, svgWidth = 500, barWidth = 30,  gap = 10//增加svg元素,并设置其宽度和高度const svg = d3.select("body")    .append('svg')    .attr('width', svgWidth)    .attr('height', svgHeight);//依据数据增加矩形        svg.selectAll('rect')    .data(sales)    .enter()    .append('rect')    .attr('width', barWidth)    .attr('height', (d,i) => {  //将数据的值设置为矩形的高度        return d    })    .attr('x', (d,i) => {   //依据柱子的宽度和对应数据的索引,计算出矩形的x坐标        return i * ( barWidth +  gap )    })    .attr('y', (d,i) => {    //用SVG元素的高度减去矩形的高度,计算出矩形的y坐标        return svgHeight - d    })    .attr('fill', 'gold')    //设置填充色


(为了不便察看,咱们给svg设置一个蓝色边框)

柱子的高度由数据的大小决定,但咱们显然不能间接应用数据的绝对值设置height,不然数值上千上万的数据展现起来就麻烦了。

要解决这个问题,就须要用到比例尺了。

比例尺与数据映射

比例尺是把一组输出域(domain)映射到输入域(range)的函数,它有点像数学中的函数y=f(x),当输出 x 的值,就能够求得 y 的值。

D3中有各种比例尺函数,【D3中罕用的比例尺】(https://segmentfault.com/a/11...) 这篇文章有比拟具体的介绍。

咱们这里要应用的是线性比例尺,用法如下:

let scale = d3.scaleLinear().domain([1,5]).range([0,100])

映射关系:

咱们这里要映射的是domain([0, max of sales]) ---> range([0, height of svg] ,批改下面的代码。

const yScale = d3.scaleLinear()    .domain([0, d3.max(sales)])    .range([0, svgHeight]);   //返回一个线性比例尺,是一个函数    svg.selectAll('rect')    ...    .attr('height', (d,i) => {        return yScale(d)   //将数据传入比例尺函数,计算矩形高度    })    .attr('y', (d,i) => {        return svgHeight - yScale(d)      })

结语

咱们曾经绘制了柱状图的雏形,因为篇幅问题,剩下的局部在下一篇文章中持续。感激你的浏览,如果感觉还不错,欢送点赞、评论、珍藏哦❤️❤️!

更多技术交换欢送关注我的公众号:Alasolala