D3.js 学习 - 1 - D3基本使用(d3-selection 选择集)

版权声明:转载请注明原文地址 https://blog.csdn.net/mjzhang1993/article/details/81240953

D3.js 学习 - 1 - D3基本使用 (d3-selection 选择集)

D3 简介

D3 是一个 Javascript 库,用于创建数据可视化图形,全称 Data-Driven Documents 也就是数据驱动文档,文档指的就是基于 web 的文档,代表可以在浏览器中展示的一切,包括 SVG 以及 一般的 HTML , D3扮演的是一个驱动程序的角色,联系着数据和文档

与 ECharts 图表相比,D3.js 不会生成事先定义好的图表,而是给你提供一些方法用来生成带数据的标签,绑定可视化的属性,如何制作图表由你自己来定义,D3.js 还提供很多数据处理的方法,用来生成可以更好的显示的数据模型。总的来说 ECharts 等可以提供更方便的图表组件,满足大部分的需求,D3.js 可以提供更丰富的自定义功能,适合定制化。

学习D3.js 更多的时候是学习它如何加载、绑定数据、数据处理、变换和过渡等这些方法的过程,官方文档的 API 非常的多,但很多都不是常用的,而且当前 D3.js 版本为 v5.5.0, 网上很多教程却都是针对 v3.x.x 版本的, D3.js 在 v4.x.x 开始 API 有了很大的不同 v5.5.0 API 与 v4.x.x 基本一致(更改的是将一些将回调转换为 promise ),于是,接下来的学习会将主要的经常被用到的 API 做下总结,然后结合实际的图表案例制作,学习其他 API ,使用的版本是最新的 v5.5.0 因此,学习过程中需要借助 这篇文章 来查询 v3.x.x 与 v4.x.x 的差异。

一些概念

1. 数据

数据就是结构化的信息,反映某些事实,可以是文本格式、二进制格式、图像、音视频、流等,但是对于 web 和 D3,js 来讲,更多的是使用文本数据

2. 链式语法

D3.js 用了一种与 jQuery 一样的 链式语法,这样通过 . 就把多个操作链接起来,执行逻辑上更加清晰,链式语法的关键就是每个操作函数都有返回值,这个返回值可以执行当前操作的对象,也可以是其他对象,在 D3.js 中要注意 .append('path') 方法的返回值是新创建的元素对象,而不是调用这个方法的元素对象

常用 d3-selection 选择集 api

d3-selection 是 D3.js 的一系列选择集 API,这些方法都有返回值,有的返回的是当前的选择集,有的则返回新的选择集。

1..select() 选取符合条件的第一个元素,返回选取的元素

select() 方法的参数是一个 css 选择器,返回的是符合选择器的第一个元素,如果没有元素被选中则返回空集

select() 方法的参数除了是字符串外,也可以是一个指定节点的引用,例如事件监听函数中的this d

    d3.select('#container')

    d3.selectAll('p').on('click', function () {
      d3.select(this).style('color', 'red')
   })

除了 d3 对象外,选择集也可以调用 select() 方法,也就是在当前选择集下边继续选择元素,这个时候除了传入选择器字符串外,还可以传入一个函数, 这个函数中的this 表示当前的元素,函数会被传入三个参数 d: 元素数据 i: 元素索引 nodes 元素集

    // 不能用 () => {} 否则 this 指向 window
   d3.selectAll('p').select(function (d, i, nodes) {
      console.log(this, d, i, nodes);
      console.log(this === nodes[i]); // true
      return this;
   })

2. .selectAll() 选取符合条件的所有元素

用来选择符合条件的所有元素,元素顺序按照其在文档中的顺序排列,如果没有元素则返回空集,与 select() 一样参数也可以是节点数组

除了 d3 对象外,选择集也可以调用 selectAll() 方法,意为进一步选择其所有符合条件的后代元素

  d3.selectAll('p').selectAll('span').selectAll(function (d, i, nodes) {
      console.log(d, i, nodes);
   })

3. .filter() 过滤选择集

过滤并返回新的选择集,参数可以是字符串或者函数

   d3.selectAll('p').filter('.none').style('color', 'red')

4. .append() 添加元素 返回值是添加的元素对象

append() 方法用来添加新元素,参数就是新元素的标签名,他的返回值也是新添加的这个元素,参数还可以是一个函数 提供 d, i, nodes 三个参数,需要返回DOM元素

    d3.select('#container')
        .append('div')

指定的标签名可能需要包含命名空间前缀 比如 svg:text 指定命名空间,如果指定标签名的命名空间是已知的则可以省略

5. .insert() 插入元素到选择集中每个选中的元素前部

有两个参数,第一个参数定义要插入的元素标签名,第二个定义了一个选择器,指定在那个元素前添加


   d3.select('#container')
      .append('p')
      .text('blue')
      .style('color', 'blue')
      .append('mark')
      .text('mark')
      .classed('blue', true);

   d3.select('#container')
      .selectAll('p')
      .insert('span', '.blue') // 被插入到 类名为 blue 的 mark 标签前
      .text('嘻嘻嘻嘻嘻')

6. .remove() 移除元素

移除当前选择集

d3.select('.blue')
      .remove()

7. .clone() 克隆当前选择集并添加到当前选择集后边

有一个可选参数 deep 表示是否 将所选元素的子元素页克隆了

d3.select('#container')
      .append('p')
      .text('blue')
      .style('color', 'blue')
      .append('mark')
      .text('mark')
      .classed('blue', true)
      .clone(true)

8. .text() 添加文本

参数是一段文本字符串或者是动态更新的函数,提供 d, i, nodes 三个参数,返回值即为文本值, 没有参数时则返回选择集第一个元素的文本值

    d3.select('#container')
        .append('div')
        .text('this is div')

9. .attr() 给元素添加属性 返回添加属性的元素

有两个参数 分别是 属性名和属性值,表示设置属性,如果参数只有属性名则表示获取属性值

    d3.select('#container')
        .append('div')
        .attr('id', 'box')
        .attr('id'); // box

属性值也可以是一个函数,接受 d: 数据 i: 索引 nodes 组节点 三个参数,函数中 this 指向当前元素,函数返回值就是属性值,设置为 null 会将其移除

   d3.select('#container')
      .selectAll('p')
      .data(data)
      .enter()
      .append('p')
      .text((d, i) => `index: ${i}; org: ${d.org}; value: ${d.value};`)
      .append('span')
      .attr('class', 'to')
      .attr('class', function (d, i, nodes) {
         console.log(d, i, nodes, this);
         return this.className;
      })

10. .classed() 添加或者删除类名

classed() 有两个参数,第一个参数是一个或多个类名用空格链接的字符串,表示要操作的类名,第二个参数可以是一个布尔值或者返回值为布尔值的函数,同样获得 d, i, nodes 三个参数
用来判断操作的类名是添加(true)还是删除(false)

d3.select('#container')
      .selectAll('p')
      .data(data)
      .enter()
      .append('p')
      .text((d, i) => `index: ${i}; org: ${d.org}; value: ${d.value};`)
      .append('span')
      .attr('class', 'to')
      .attr('class', 'post')
      .classed('to get', false)
      .attr('class', function () {
         console.log(this); // 只剩 post class
         return this.className;
      })

11. .style() 给元素添加样式

两个参数,样式名和样式值,表示设置样式,如果参数只有 样式名,表示获取指定的样式值

注意对于SVG 元素设置 style 的属性还是要 加上单位的,因为有些浏览器不支持默认单位

d3.select('#container')
      .append('p')
      .attr('class', 'blusse')
      .text('blue')
      .style('color', 'blue')
      .style('color'); // blue

12. .data() 将数据绑定到元素上

将指定数组的数据 data 与已经选中的元素进行绑定并返回一个新的选择集,新的选择集使用 update 表示:此时数据已经成功与元素绑定,并且定义了 enter 和 exit 方法用来返回需要加入元素和移除元素的选择集,参数 data 可以是一个数组,也可以是一个返回数组的函数

后续的操作如果有需要计算的属性值,就可以将属性值设置为有返回值的函数,这个函数会被传入参数 d 代表这个元素被分配的数据

   var matrix = [
      [11975,  5871, 8916, 2868],
      [ 1951, 10048, 2060, 6171],
      [ 8010, 16145, 8090, 8045],
      [ 1013,   990,  940, 6907]
   ];
const p = d3.select('#container')
      .selectAll('p')
      .data(matrix)
      .enter()
      .append('p')
      .text((d, i) => `index: ${i}; d:`)

   p.selectAll('span')
      .data(function (d, i, nodes) {
         console.log(d, i); // 这个时候 d 是 matrix 的每一行 [11975,  5871, 8916, 2868]

         return d
      })
      .enter()
      .append('span')
      .text(d => '--' + d + '--')

data() 方法的第二个参数 key 函数,用来指定第几个数据被分配给第几个索引的元素,返回值即为 标识,代替索引

d3.select('#container')
      .selectAll('p')
      .data(data, d => d.id)

13. .enter() 返回没有 DOM 节点的占位节点选择集

在 enter() 之前调用 data() 传入数据后再调用 enter() 方法,可以生成当前没有与 data 数据对应的 DOM 的占位节点,通常用来在数据比节点多的时候创建缺失的节点

   const oldP = d3.select('#container')
      .selectAll('p')
      .data(matrix)
      .text((d, i) => `old index: ${i}; d:`)

   console.log(oldP);

   const p = oldP.enter() // enter() 后 append 的元素操作的仅仅是新添加的元素,旧的元素不会被操作
      .append('p')
      .text((d, i) => `new index: ${i}; d:`)

14. .exit() 返回没有对应数据的,曾经存在的DOM节点选择集

exit 选择集通常用来移除多余的元素

   const data = [
      {name: 'xxx1', age: 19, id: 123},
      {name: 'xxx2', age: 21, id: 124},
      {name: 'xxx3', age: 34, id: 125},
      {name: 'xx4x', age: 12, id: 126},
      {name: 'xxx5', age: 1, id: 127}
   ];
   d3.select('#container')
      .selectAll('p')
      .data(data, d => d.id)
      .enter()
      .append('p')
      .text((d, i) => `F index: ${i}; d: ${JSON.stringify(d)}`)

   const newData = [
      {name: 'xxx2', age: 21, id: 124},
      {name: 'xx4x', age: 80, id: 126}, // 这里实际修改了数据,但是是否更新的标准是 key(id) 是否一致,所以旧的没有被更新
      {name: 'xxx7', age: 212, id: 129},
      {name: 'xx48', age: 122, id: 128},
   ];

   // 按照新数据重新渲染
   const selectP = d3.select('#container')
      .selectAll('p')
      .data(newData, d => d.id); // 指定 按照 key 索引,这样旧的数据 key 没有对应上的就会被清除到 exit 的集合

   // 将新的数据补上
   selectP.enter()
      .append('p')
      .text((d, i) => `S index: ${i}; d: ${JSON.stringify(d)}`);

   // 清除不用的数据
   selectP.exit().remove();

15. .datum() 获取或者 设置每个选中元素上绑定的数据

data() 方法不同,这个方法不会进行数据链式计算,不影响索引,不影响 enter 和 exit 选择集,参数为要设置的值,或者一个函数,返回要设置的值

   // 按照新数据重新渲染
   const selectP = d3.select('#container')
      .selectAll('p')
      .data(newData, d => d.id); // 指定 按照 key 索引,这样旧的数据 key 没有对应上的就会被清除到 exit 的集合

   // 将新的数据补上
   selectP.enter()
      .append('p')
      .datum((d, i) => `name: ${d.name}`)
      .text((d, i) => `S index: ${i}; d: ${JSON.stringify(d)}`)

   // 清除不用的数据
   selectP.exit().remove();

d3-selection 选择集的事件

为了交互,选择集允许监听和分派事件

1. .on(type, listener, capture) 添加事件监听器

  1. 为每个选中元素添加或者移除指定的事件监听器(浏览器支持的DOM事件都可用)
  2. listener 设置为 null 则监听器移除
  3. 同一个元素上注册同一个事件的监听函数会被覆盖
  4. 同一个元素注册同一个事件的监听器,可以在事件名后通过 . 指定分类,这样就不会被覆盖了
  5. capture 是个布尔值,表示是否启动事件捕获
   d3.select('#container')
      .selectAll('p')
      .on('click.foo', function (d) {
         console.log('foo click', d);
      })
      .on('click.bar', function (d) {
         console.log('bar click', d);
      })
      .on('click.remove', function (d) {
         console.log('remove click', d);
      })
      .on('click.remove', null)

2. d3.touches(container[, touches]) 返回触摸事件的坐标

返回触摸事件相对于 container 的坐标 container 是一个DOM元素

   d3.select('#container')
      .selectAll('p')
      .on('touchstart', function (d) {
         const touches = d3.touches(document.getElementById('container')); // [Array(2)]
         console.log(touches)
      })

定制化的控制流

1. .each(function)

为每个选中的元素依次调用指定的 function ,将会传入 d, i, nodes this指向当前DOM 元素

    d3.select('#container')
      .selectAll('p')
      .each(function (d, i) {
         console.log(d, i)
      })

2. .call(function, arguments)

调用一个指定函数,函数的第一个参数是当前的选择集,其余参数就是 arguments

   d3.select('#container')
      .selectAll('p')
      .call(function (selection, a, b) {
         selection.append('span')
            .text(a + b);
      }, 'aaa', 'bbb')

3. .empty() 返回选择集是否为空

4. .nodes() 返回选择集中的元素数组

5. .size() 返回选择集中的元素个数

   const empty = d3.select('#container')
      .selectAll('div')
      .empty()

   console.log(empty)

   const nodes = d3.select('#container')
      .selectAll('p')
      .nodes()

   console.log(nodes)

   const size = d3.select('#container')
      .selectAll('p')
      .size()

   console.log(size)

参考文档

猜你喜欢

转载自blog.csdn.net/mjzhang1993/article/details/81240953