如何使用d3.js制作可视化图表

JavaScript011

如何使用d3.js制作可视化图表,第1张

D3是目前最流行的JavaScript可视化图表库之一,D3的图表类型非常丰富,并且支持SVG格式,因此应用十分广泛,也有很多图表插件基于D3开发,比如MetricsGraphics.js,在D3上构建的数据图表非常强大。

D3的特点

允许绑定任意数据到DOM,将数据驱动转换应用到Document中。

不仅可以创建精美的HTML表格,而且可以绘制折线图、柱形图和饼图等数据图表。

支持SVG,在Web页面上渲染毫无压力。

回到顶部

D3的使用方法

关于D3的具体用法,可以看D3图形库API参考这篇文章。本文主要对介绍一些经典图表的实现效果及代码。

index.html代码:

<!DOCTYPE html>

<meta charset="utf-8">

<style>

 

svg {

  font: 10px sans-serif

}

 

.y.axis path {

  display: none

}

 

.y.axis line {

  stroke: #fff

  stroke-opacity: .2

  shape-rendering: crispEdges

}

 

.y.axis .zero line {

  stroke: #000

  stroke-opacity: 1

}

 

.title {

  font: 300 78px Helvetica Neue

  fill: #666

}

 

.birthyear,

.age {

  text-anchor: middle

}

 

.birthyear {

  fill: #fff

}

 

rect {

  fill-opacity: .6

  fill: #e377c2

}

 

rect:first-child {

  fill: #1f77b4

}

 

</style>

<body>

<script src="http://d3js.org/d3.v3.min.js"></script>

<script>

 

var margin = {top: 20, right: 40, bottom: 30, left: 20},

    width = 960 - margin.left - margin.right,

    height = 500 - margin.top - margin.bottom,

    barWidth = Math.floor(width / 19) - 1

 

var x = d3.scale.linear()

    .range([barWidth / 2, width - barWidth / 2])

 

var y = d3.scale.linear()

    .range([height, 0])

 

var yAxis = d3.svg.axis()

    .scale(y)

    .orient("right")

    .tickSize(-width)

    .tickFormat(function(d) { return Math.round(d / 1e6) + "M" })

 

// An SVG element with a bottom-right origin.

var svg = d3.select("body").append("svg")

    .attr("width", width + margin.left + margin.right)

    .attr("height", height + margin.top + margin.bottom)

  .append("g")

    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

 

// A sliding container to hold the bars by birthyear.

var birthyears = svg.append("g")

    .attr("class", "birthyears")

 

// A label for the current year.

var title = svg.append("text")

    .attr("class", "title")

    .attr("dy", ".71em")

    .text(2000)

 

d3.csv("population.csv", function(error, data) {

 

  // Convert strings to numbers.

  data.forEach(function(d) {

    d.people = +d.people

    d.year = +d.year

    d.age = +d.age

  })

 

  // Compute the extent of the data set in age and years.

  var age1 = d3.max(data, function(d) { return d.age }),

      year0 = d3.min(data, function(d) { return d.year }),

      year1 = d3.max(data, function(d) { return d.year }),

      year = year1

 

  // Update the scale domains.

  x.domain([year1 - age1, year1])

  y.domain([0, d3.max(data, function(d) { return d.people })])

 

  // Produce a map from year and birthyear to [male, female].

  data = d3.nest()

      .key(function(d) { return d.year })

      .key(function(d) { return d.year - d.age })

      .rollup(function(v) { return v.map(function(d) { return d.people }) })

      .map(data)

 

  // Add an axis to show the population values.

  svg.append("g")

      .attr("class", "y axis")

      .attr("transform", "translate(" + width + ",0)")

      .call(yAxis)

    .selectAll("g")

    .filter(function(value) { return !value })

      .classed("zero", true)

 

  // Add labeled rects for each birthyear (so that no enter or exit is required).

  var birthyear = birthyears.selectAll(".birthyear")

      .data(d3.range(year0 - age1, year1 + 1, 5))

    .enter().append("g")

      .attr("class", "birthyear")

      .attr("transform", function(birthyear) { return "translate(" + x(birthyear) + ",0)" })

 

  birthyear.selectAll("rect")

      .data(function(birthyear) { return data[year][birthyear] || [0, 0] })

    .enter().append("rect")

      .attr("x", -barWidth / 2)

      .attr("width", barWidth)

      .attr("y", y)

      .attr("height", function(value) { return height - y(value) })

 

  // Add labels to show birthyear.

  birthyear.append("text")

      .attr("y", height - 4)

      .text(function(birthyear) { return birthyear })

 

  // Add labels to show age (separate not animated).

  svg.selectAll(".age")

      .data(d3.range(0, age1 + 1, 5))

    .enter().append("text")

      .attr("class", "age")

      .attr("x", function(age) { return x(year - age) })

      .attr("y", height + 4)

      .attr("dy", ".71em")

      .text(function(age) { return age })

 

  // Allow the arrow keys to change the displayed year.

  window.focus()

  d3.select(window).on("keydown", function() {

    switch (d3.event.keyCode) {

      case 37: year = Math.max(year0, year - 10) break

      case 39: year = Math.min(year1, year + 10) break

    }

    update()

  })

 

  function update() {

    if (!(year in data)) return

    title.text(year)

 

    birthyears.transition()

        .duration(750)

        .attr("transform", "translate(" + (x(year1) - x(year)) + ",0)")

 

    birthyear.selectAll("rect")

        .data(function(birthyear) { return data[year][birthyear] || [0, 0] })

      .transition()

        .duration(750)

        .attr("y", y)

        .attr("height", function(value) { return height - y(value) })

  }

})

随着现在自定义可视化的需求日益增长,Highcharts、echarts等高度封装的可视化框架已经无法满足用户各种强定制性的可视化需求了,这个时候D3的无限定制的能力就脱颖而出。

如果想要通过D3完成可视化,除了对于D3本身API的学习, 关于web标准的HTML, SVG, CSS, Javascript 和 数据可视化的概念以及标准都是需要学习的。这无疑带来了较高的学习门槛,但这也是值得的,因为掌握 D3 后,我们几乎可以实现任何 2d 的可视化需求。

本文通过对D3核心模块分析以及进行具体案例实践的方式,来帮助初学者学习了解D3的绘图思路。

D3的全称是 Data-Driven Documents(数据驱动文档),是基于数据来操作文档的 JavaScript 库,其核心在于使用绘图指令对数据进行转换,在源数据的基础上创建新的可绘制数据, 生成SVG路径以及通过数据和方法在DOM中创建数据可视化元素(如轴)。

相对于Echats等开箱即用的可视化框架来说,D3更接近底层,它可以直接控制原生的SVG元素,并且不直接提供任何一种现成的可视化图表,所有的图表都需我们在它的库里挑选合适的方法构建而成,这也大大提高了它的可视化定制能力。而且D3 没有引入新的图形元素,它遵循了web标准(HTML, CSS, SVG 以及 Canvas )来展示数据 ,所以它可以不需要依赖其他框架独立运行在现代浏览器中。

在V4版本后,D3的 API 现在已经被拆分成一个个模块,我们可以根据自己的可视化需求进行按需加载。根据泛义可以将D3 API模块分为以下的几大类: DOM操作、数据处理,数据分析转换、地理路径,行为等

这里我们主要对 D3-selection 和 D3-scale 模块进行解析:

D3-selection (选择集) 是 D3js的核心模块,主要是用来进行选择元素,设置属性、数据绑定,事件绑定等操作。

选择元素: D3-selection 提供了两种方法来获取目标元素,d3.select():返回目标元素的第一个节点,d3.selectAll():返回目标元素的集合,乍一看有点类似原生API 的 querySelector 和 querySelectorAll,但是 d3.select 返回的是一个 selection 对象,querySelector 返回的是一个 NodeList 数组。通过控制台打印的信息,可以看到 selection 下的 groups 存放了所有选择的元素集合,parents 存放了所有选中元素的父节点。

设置属性或者绑定事件: 我们不需要关心 groups 的结构是怎么样的。当调用 selection.attr 或者 selection.style 的时候, selection 中的所有 group 的所有子元素都会被调用,group 存在的唯一影响是: 当我们传参是一个function 的时候,例如 selection.attr('attrName', function(data, i)) 或 selection.on('click', function(data, i)) 时, 传递的 function(data, i) 中, 第二个参数 i 是元素在 group 中的索引而不是在整个 selection 中的索引。

数据绑定: 实际上是给选择的DOM元素的 __data__ 属性赋值,这里提供了3种方式进行数据绑定:

(1)给每一个单独的 DOM 元素调用 selection.datum:d3.select('body').datum(20) 等价于 document.body.__data__ = 20

(2)从父节点中继承来数据, 比如: append , insert , select,子节点会主动继承父节点的数据:

(3) 调用 selection.data() 方法,支持传入装有基础数据类型的数据,也支持传入一个function(parentNode, groupIndex)根据节点索引与数据做映射,data()方法引入了 d3 中非常重要的 join 思想:

绑定 data 到 DOM 元素, 在D3中是通过比较 data 和 DOM 的 key 值来找到对应关系的。 如果我们没有单独设置 key 值,那么默认根据 data 的下标索引来设定,但是当数据顺序发生改变,这个默认下标 key 值 就变得不可靠了,这时我们可以使用 selection.data(data, keyFunction) 中的第二个参数 keyFunction,根据当前的数据返回一个对应的 key 值。通过下面的图例可以看出,不管是有一个还是多个 group(每个group 都是独立的),只要我们保证在任意一个 group 中的 key 值是唯一的,数据一旦发生变化都会反映给对应的 DOM 元素( update 的过程):

上面提到的都是data数据和DOM元素数量相同的情况下的数据绑定,那如果data数据和DOM元素数量不相同时,我们来看看 D3 又是如何进行数据绑定的:现在终于可以来介绍 D3-selecion 模块的核心 Join 思想了,这个思想简单来说就是 “不应该告诉D3去怎么创建元素, 而是告诉D3,.selectAll() 得到的 selecion 集合应该和 .data(data) 绑定的数据要怎么一一对应”。

从上图可以看出,在进行 d3.data(data) 数据绑定的时候,会产生三种状态的选择集:

用 Join 的方式来理解意味着,我们要做的事情仅仅是声明 DOM集合和数据集合之间的关系, 并且通过处理三个不同状态的集合 enter、update 、 exit 来描述这种关系。这种方式可以大大简化我们对DOM元素的操作,我们不需要再用 if 和 for 循环的方式来进行复杂的逻辑判断,来得到我们需要得到的元素集合。并且在处理动态数据的时候,可以通过处理这三种状态,轻松的展示实时数据和添加平滑的动态交互效果。

D3-scale (比列尺) 提供多种不同类型的比例尺。经常和 D3-axis 坐标轴模块一起使用。

D3-scale 提供了多种连续性和非连续性的比例尺,总体可以将他们分为三大类:

常用的一些比例尺:

(1)d3-scaleLinear 线性比例尺(连续性输入和连续性输出)

可以看出,调用d3.scaleLinear()可以生成线性比例尺,domain()是输入域,range()是输出域,相当于将domain中的数据集映射到range的数据集中。

使用示例:

映射关系:

(2)d3-scaleTime 时间比例尺(连续性输入和连续性输出)

时间比例尺与线性比例尺类似,只不过输入域变成了一个时间轴。正常我们使用比例尺都是个正序的过程,但是D3也提供了invert()以及invertExtent()方法,我们可以通过输出域中的具体值得出对应输入域的值。

使用示例:

(3)d3.scaleQuantize 量化比例尺(连续性输入和离散性输出)

量化比例尺是将连续的输入域根据输出域被分割为均匀的片段,所以它的输出域是离散的。

使用示例:

映射关系:

(4)d3. scaleThreshold 阈值比例尺(连续性输入和离散性输出)

阈值比例尺可以为一组连续数据指定分割阈值,阈值比例尺默认的 domain:[0.5] 以及默认的 range:[0, 1] ,因此默认的 d3.scaleThreshold() 等价于 Math.round 函数。 阈值比例尺输入域为 N 的话,输出域必须为 N + 1,否则比例尺对某些值可能会返回 undefined,或者输出域多余的值会被忽略。

使用示例:

存在三种映射关系:

a. 当domain和range的数据是 N : N+1

b. 当domain和range的数据是 N : N + 大于1

c. 当domain和range的数据是 N + 大于0 : N

(5)d3.scaleOrdinal 序数比例尺(离散性输入和离散性输出)

与scaleLinear等连续性比例尺不同,序数比例尺的输出域和输入域都是离散的。

使用示例:

存在三种映射关系:

a.当domain和range的数据是一一对应

b.当domain少于range的数据

c.当domain多于range的数据

通过以上的学习,应该对d3是如何操作DOM以及坐标轴的数据映射为相应的可视化表现有了一定的了解,下面我们来实际运用这两个模块,来实现我们常见的可视化图表:柱状图。

(1)首先添加一个SVG元素。

(2)根据我们上面说到 d3.scale 模块以及 d3.axis 模块绘制坐标轴,d3.scaleBand() 叫做序数分段比例尺,类似我们说的 d3.scaleOrdinal() 序数比例尺,但是它支持连续的数值类型的输出域,离散的输入域可以将连续的范围划分为均匀的分段。这里再讲一个细节,在绘制网格的时候,我们并没有额外添加 line 元素来实现,而是通过 d3.axis 坐标轴模块的 axis.ticks() 方法对坐标轴刻度进行了设置,通过 tickSIze() 设置了刻度线长度,来模拟和图表宽度相等的网格线,并且还可以通过 tickFormat() 对Y轴刻度值进行格式化转换。

(3)坐标轴绘制好了后,我们通过数据绑定来绘制与之对应的矩形(rect)元素了。

(4)这个时候柱状图已经基本绘制好了,我们再丰富内容展示,添加标签、标题等提示信息。

(5)最后我们通过给柱子绑定监听事件,实现tooltips的信息浮层交互。

通过对 d3.selection 、d3.scale 以及 d3.axis等模块的学习,我们已经可以绘制出常用的柱状图等图表,我们也可以通过d3提供的其他模块绘制出更加复杂的可视化效果,例如通过 d3-hierarchy(层级模块) 实现层级树图可视化,d3-geo(地理投影) 实现地图数据可视化等,本文讲解的内容还只是D3库的冰山一角。所以等我们掌握了D3后,限制我们实现可视化的不再是技术而是想象力。

准确的来描述,echarts tooltip设置正常,却无法显示的问题,都是被遮挡了。

实例如下:

HTML中DOM代码:

HTML中对应的css代码:

当z-index: -9999时,默认被遮挡,调整或则删除即可。

z-index 属性指定一个元素的堆叠顺序。

拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。

注意: z-index 进行定位元素(position:absolute, position:relative, or position:fixed)。

打开CSDN APP,看更多技术内容

echarts折线图堆叠怎么设置_百度echarts使用教程之echarts多条折线图实...

我们把selectedMode的值设置为single,单选模式,效果如下: 四、完整demo.html代码

继续访问

echarts使用toolbox切换堆叠柱形图和平铺折线图的问题解决

1.问题这两天做Echarts图表遇到一个问题。需要使用toolbox工具箱进行堆叠图和折线图的切换。由于显示市默认显示堆叠图所以数据是堆叠图的格式。在切换折线图时,折线图也是堆叠状态。本意是使折线图处于平铺形式,客观显示数据趋势。问题如下...

继续访问

最新发布 echarts tooltip显示不全问题

echarts的tooltip显示不全问题

继续访问

Echarts设置tooltip层级z-index

在项目开发过程中,滚动页面后,发现图表的tooltip遮挡住上面元素,于是设置顶部元素的z-index=99999发现没有用。 在浏览器中选中tooltip后可以发现,tooltip的z-index=9999999即7个9。所以如果设置被遮挡的元素的z-index=8个9自然可以解决这个问题 但是,这样究竟不够优雅。 查看官方文档发现,tooltip有一个extraCssText属性。所以可以设置该属性来修改它的样式: ...

继续访问

Echarts 实现Toolip轮播_一休~的博客

Echarts 实现Toolip轮播 Echarts专栏收录该内容 3 篇文章0 订阅 订阅专栏 Gallery:https://gallery.echartsjs.com/editor.html?c=x0A9jQ8a1j Echarts版本是4.0,注释写的很清楚,有问题请提出...

继续访问

echart 图谱_vue + echarts 实现有层级关系图的图谱

text: 'ECharts 关系图', show:false }, // 提示框的配置 tooltip: { enterable: true, // 鼠标可移入tooltip中 axisPointer: { type: 'cross', lineStyle: { type: 'solid',

继续访问

echarts canvas 层级太高 导致tooltip被遮盖

问题截图: 解决方法: 给echarts里面的canvas设置层级, z-index: -1 import ReactEcharts from 'echarts-for-react' import styled from 'styled-components' // 这里本人习惯使用styled-components处理样式, 普通的css方式也可以 // 降...

继续访问

热门推荐 Echarts提示框(tooltip)位置设置

Echarts提示框(tooltip)浮层位置,不设置时,默认位置会跟随鼠标的位置。 但是,当提示框位置超出图表所在区域时,就可能出现提示框显示不全的问题,如下所示: 这就需要我们去设置提示框位置 1、设置提示框位置的方法 方法一:通过数组设置提示框位置 数组第一个元素设置距离父元素左边的距离,数组第二个元素设置距离父元素上边的距离 这种方法设置的提示框位置固定不...

继续访问

...比关系(某项占比100%)_敲之的博客_echarts饼图包含关系

echart饼图设置堆叠效果 option = { legend: { orient:'vertical', left:'left', }, series: [ { type:'pie', radius:'50%', data: [ {value:1000,name:'全部'}, ], },

继续访问

echarts 树状图节点太多导致节点重叠,如何调整_Miss.PH的博客...

由于节点太多导致节点重叠在一起后用户无法正常查看,我们可以根据节点的数量并设置每个节点间的高度动态调整E charts 画布的高度和宽度。 如下代码: html部分: <template></template>js部分: data() { return{ chart...

继续访问

微信小程序使用echarts图表

1 微信小程序使用echarts图表 2 网页嵌套和其它页面的引用

d3.js第七次人口普查可视化分析(含数据和源代码)

d3.js,将获取到的数据进行处理,将不同数据可视化成各种图形,折线图,饼状图,条形等,将各个年份的数据与图形对应起来分别是受教育情况、年龄组成、性别比例和户籍比例,并且将数据与图形相结合进行交互。

解决echarts不显示tooltip(提示框)

tooltip对象中不需要加triggerOn,不知道为什么加了就不显示tooltip

继续访问

echarts 移动端画面失真模糊,tooltip不显示

echarts 移动端画面失真等使用心得 这次的项目移动端电脑端都有用到echarts这个插件。在此记录一些使用心得,方便日后查看。如果能帮到看文章的你,我很高兴。 移动端 一、柱状图,画面失真,柱状图发虚 解决方案一 换svg绘图 解决方案二 我的项目出现这个的原因是我使用echarts.init()的时候,直接将id绑定在canvas上了。将id绑在div上就会避免这个现象了......

继续访问

移动端-echarts-tooltip不显示提示框

移动端-echarts-tooltip不显示提示框,echart实例销毁

继续访问

移动端 Echart 点击toolTip

tooltip: { trigger: "axis", show: true, formatter: defaultFormatter, enterable: true // 鼠标可以进入提示框 } let defaultFormatter = function (params) { let str = params[0].name || params[0].value[0] + "<br/>"return clickToo.

继续访问

echarts-taro3-react:taro3.x-react构建的微信小程序echarts组件,及使用示列

echarts-taro3-react 基于Taro3.x-React框架构建的微信小程序版本echarts组件,及使用示例 代码体积过大解决方案: 使用分包, 自定义echart.js,自行前往(注意:版本需要选择4.9.0,不要勾选压缩,可下载下来后自行压缩) 按需构建echart.js,然后替换掉echarts-taro3-react/lib/ec-canvas/echart.js 版本日志: 1.0.11: 新增datazoom功能 1.0.10: 压缩了echarts.js, 代码大小从2.2m下降至543kb 修复了更新数据不能刷新的问题 传送门 特性 Taro3.x React Typescript 功能 柱状图 折线图 饼图 散点图 雷达图 热力图 地图 仪表盘 漏斗图 快速安装 npm i echarts-taro3-react 使用 引入EChart组

ECharts折线图堆叠设置为不堆叠的方法

今天拿到一个多条线的折线图, 但是官网的源码示例是这样的,当我把搜索引擎的周二数据设置为0是并没有在y轴为0上,而是跟上一条线重合了, ECharts折线图是堆叠的,折线图堆叠的意思就是:第二条线的数值=本身的数值+第一条线的数值,第三条的数值=第二条线图上的数值+本身的数值,以此类推...... 设置折线图不堆叠只需要将每一个stack的值设置为不一样的名称或者将stack属性删除即可。 option = { title: { text: '折线图堆叠' ..

继续访问

【echarts】tooltip显示位置设置

echarts中的柱状图或折线图,tooltip默认是根据鼠标的移动而移动,视觉焦点始终是与鼠标保持一致,但有时根据用户需要会对tooltip的位置做特殊处理,让其固定在图形顶部或者鼠标的左侧,具体实现方法如下: 1、tooltip显示框始终固定在图形顶部 //tooltip显示框始终固定在图形顶部 tooltip: { trigger: 'axis', position: function (point, params, dom, rect, size) {

继续访问

echarts 树状图节点太多导致节点重叠,如何调整

由于节点太多导致节点重叠在一起后用户无法正常查看,我们可以根据节点的数量并设置每个节点间的高度动态调整E charts 画布的高度和宽度。 如下代码: html部分: <template><div :style="{height: chartHeight}"><div ref="main" style="height: 100%"></div></div></template>js部分

继续访问

echarts图表相关配置问题整理、自定义图表滚动条样式、x轴label竖直显示、自定义toolltip内容及样式、tooltip位置自适应、图表无数据引用自定义图片、浏览器窗口变化图表自适应更新等

自定义echarts图表滚动条样式: 代码: 图表的html格式为: <div className="medicFeeStyl"><div id="medicFeePic" style={{ width: '100%', height: '100%' }}></div><div id="noDataImg6" style={{ width: '100%', height: '100%', display: 'flex', justif.

继续访问

echart tooltip显示不全,溢出屏幕问题

在tooltip下面加入position回调函数 //如果饼图位于屏幕的左侧 position: function(point, params, dom, rect, size){ // size为当前窗口大小 if((size.viewSize[0]/2)>=point[0]){ //其中point为当前鼠标的位置,在左侧 return [point[0],point[1]].

继续访问

echart项目里面兼容不同版本

情景:在项目中需要用到echarts新版本的功能,但是发现项目中一直用的是老版本的echarts,假如直接 升级echarts版本就会影响之前echarts图的一下功能; 实现:下载新版本的echarts同时在项目中引入进来,再修改新版本的echarts.js文件,把echarts.js文件中所有的‘echarts’改成‘echarts4’(如果你下载的是echarts.4.0.x 的版本就改成‘echarts4’;如果是echarts.5.0.x 或其他的,就改成 ‘echarts5’或 其他的); 用.

继续访问

解决echarts图表tooltips 被内容遮挡问题

问题描述:在自定义echarts 柱状图时导致tooltips 被柱状图压住。如图: 解决方法:在options中的tooltips设置如下属性: tooltip: { trigger: 'axis', position: function(point,params,dom,rect,size){ dom.style.transform = "translateZ(0)" },

继续访问

echart关系树状图_echarts - 树图实现四个层级

我相信很多人和我一样,制作echats图标时,都会先去demo官网找相同的或者近似的效果,然后再此基础上改进成我们想要的那个。但是近期混迹某微信群时,我看到一个群友抛出问题说,echarts画树状图,自己有四层数据,但是只有三层渲染出来了。我跟他说你去ecahrts官网找个四级的。我记得有的。他说官网的也只有三层,还截图给我看,果然还是被我找到了:数据太多,勉强观看。我甚至还能搞成五层不卖关子。其...

继续访问

echarts 图表,tooltip被容器遮挡,显示不全

echarts图表tooltip显示不全 场景,一个对话框里展示6个eacharts图表,容器高度不够,导致图表的tooltip被遮挡,如图 尝试使用echarts的position属性控制,但效果不好,最后使用 confine属性解决 官方文档解释是 代码实现: options: { tooltip: { trigger: 'axis', axisPointer: { type: 'shadow'

继续访问

怎么设置echarts