D3.js学习笔记

目录

前言

1、导入D3.js

 2、D3基础语法

 引入SVG

使用D3查询SVG

使用D3设置SVG中的属性

使用D3 添加&删除 SVG元素


前言

本笔记是基于清华大学张少魁讲授的计算机系2022年春季的《数据可视化》课程视频和pdf

课程链接:哔哩哔哩

课程github:  github 

1、导入D3.js

通过script标签导入:

  1. 通过互联网链接:https://d3js.org/d3.v5.min.js
  2. 通过本地文件:./d3.min.js

 2、D3基础语法

 引入SVG

    <svg style='display: block; margin: 0 auto;'>
        <g transform='translate(0,60)'>
            <rect width=100 height=100 fill='#EEEEEE' />
            <circle r=15 fill='#72bf67' cx=25 cy=30 />
            <circle r=15 fill='rgb(100, 149, 237)' cx=75 cy=30 />
            <g transform='translate(15,60) rotate(10)'>
                <path d="M0,0 A40,40 10 0,0 65,0" fill='none' stroke='gray' stroke-width=5 />
            </g>
        </g>
    </svg>

使用D3查询SVG

 d3.select(…)
 • d3.select(‘#rect1’)
 • 查询ID为’rect1’的元素
 • #表示后面的字符串是一个ID
 • 只找一个,若有重名也只返回第一个
d3.selectAll(…)
d3.selectAll(‘.class1’)
查询所有class是’class1’的元素
d3.selectAll(‘rect’)
查询所有标签是’rect’的元素(rect为SVG中的矩形标签)
有多少返回多少
可配合Data-Join选取‘不存在’的图元
ID前加‘#’,Class前加‘.’ ,标签名前不加符号。
基于层级的查询
d3.select(‘#maingroup rect’)
d3.selectAll('.tick text’)
d3.selectAll(‘#secondgroup rect’)
如:’#secondgroup rect’
首先会找到id为secondgroup的标签
进一步找到secondgroup的子标签中是rect的
仍然是对rect做查询,只是结果通过父标签做了筛选
d3.select(…)也可用于查询类别,如
d3.select(‘.class1’)
但只会返回找到的第一个元素
• 因此对于class、标签名称的查询建议使用d3.selectAll
• 对于特定某一个元素的查询建议使用d3.select

使用D3设置SVG中的属性

常见的属性
id, class(特殊的属性,可以使用.attr设置)
x, y, cx, cy (注意屏幕的坐标系!见下页)
fill, stroke
height, width, r (圆的半径)
transform -> translate, rotate, scale
屏幕空间的坐标系与常见坐标系不同
左上方为原点
Y、X分别垂直向下、水平向右

设置元素的属性: element.attr(‘attr_name’, ‘attr_value’)
两个参数:属性名、设置的值
rect1.attr(‘y’, ‘100’)
d3.select(‘#rect1’).attr(‘y’, ‘100’)
获取元素的属性: element.attr(‘attr_name’)
一个参数:属性名
链式调用
selection.attr(…).attr(…).attr(…)
.attr(…)返回的是选择的图元本身
修改整组属性
DOM
父节点的属性会影响子节点
子节点的属性会相对于父节点
下方代码可以直接移动组内所有元素
d3.select('#maingroup’)
.attr('transform', 'translate(200, 100)')

使用D3 添加&删除 SVG元素

element.append(…)
const myRect = svg.append(‘rect’);
const myRect = d3.select(‘#mainsvg’).append(‘rect’)
const myRect = d3.select(‘#mainsvg’).append(‘rect’).attr(‘x’, ‘100’)
D3的链式添加(调用)
const myRect = d3.select(‘#mainsvg’).append(‘g’).attr(‘id’, ‘maingroup’)
.append(‘rect’).attr(‘fill’, ‘yellow’)
element.remove()
请小心使用
会移除整个标签
Tip:在debug的过程中可以考虑使用’opacity’属性hack出移除的效果
element.attr(‘opacity’, ‘0’)

数据的读取 – CSV数据

d3.csv(…):
读取目标路径下的某一个CSV文件。
e.g., d3.csv(‘static/data/hello.csv’);
 
d3.csv是一个JavaScript异步函数
不可以直接获得它的返回值 ,如:
let myData = d3.csv(‘static/data/hello.csv’);
d3.csv(‘path/to/data.csv’).then( data => { // ‘数据读取后的代码逻辑’ } )
要通过.then( data => {…} )的方式来获得读取后的数据。
then(…)中的 ‘ data => {…}’ 是一个 函数
函数 接受的 输入 ( 参数 ),即 data ,为读取后的数据。
d3.csv('test.cvs').then(data => {
    console.log(data);
});

JavaScript异步机制(下述不做要求):

d3.csv作为异步函数,即便没有读取好数据,后面的代码也会继续执行。
d3.csv被调用后,其返回值是一个JavaScript的‘Promise’对象(object)。
Promise‘询问’:数据读取好了之后要 做什么 ?‘ 做什么 ’即对应.then()中 函数 的内容。

D3.js的数值计算

d3.max(array)
返回数组中的最大值。
e.g., d3.max([5,4,6,1,8,16,9]) // 16
d3.min(array)
返回数组中的最小值。
d3.min([5,4,6,1,8,16,9]) // 1
d3.extent(array)
同时返回最小值与最大值,以数组的形式,即[最小值,最大值]。
d3.extent([5,4,6,1,8,16,9]) // [1, 16]
数组中的内容可以是任意对象:
每个对象可能包含多个属性。
具体取哪个属性的最大值通过回调函数来提示d3.max、d3.min与d3.extent。
e.g.,
let a = [
{name: 'Shao-Kui', age:25, height: 176}, {name:'Wen-Yang', age:24, height: 180},
{name:'Liang Yuan', age: 29, height: 172}, {name:'Wei-Yu', age:23, height: 173}]
d3.max(a, d => d.age) // 29
d3.max(a, d => d.height) // 180
d3.extent(a, d => d.height) // [172, 180]
d3.min(a, d => d.age) // 23

比例尺

比例尺用于把实际 数据空间 映射到 屏幕(画布)空间 ,即两个空间的转化。
常用于映射数据and创建坐标轴。
区别主要在于数据的尺度不同。

Scale - Linear

d3.scaleLinear():
定义一个线性比例尺,返回的是一个 函数
e.g., let scale = d3.scaleLinear(); // scale为函数
scale.domain([min_d, max_d]).range([min, max]):
设置比例尺的 定义域 值域
线性比例尺的定义域和值域都是连续的(Continuous),需分别给出最大值与最小值。
e.g., const scale = d3.scaleLinear().domain([20, 80]).range([0, 120]);

比例尺本质上是一个函数
scale(20) // 0
scale(50) // 60
常结合读取的数据与d3.max等接口连用:
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, innerWidth]);

 Scale - Band

d3.scaleBand():
定义一个‘条带’比例尺,返回的是一个 函数
e.g., let scale = d3.scaleBand();
scale.domain(array).range([min, max]):
设置比例尺的定义域与值域。
Band比例尺的定义域是离散的(Discrete),值域是连续的。
e.g., const scale = d3.scaleBand().domain([‘a’, ‘b’, ‘c’]).range([0, 120]);

比例尺本质上是一个函数

scale('b') // 40
scale(‘c') // 80
scale.padding(0.1):
设置条带的间距占各自区域的比重。
scale.bandwidth():
返回条带的长度。
      d3.csv('platform_globalsale.csv').then(data => {
          // calculationg scales: 
          yScale.domain(data.map(yValue)).range([0, innerHeight]).padding(0.1);
          xScale.domain([0, d3.max(data, xValue)]).range([0, innerWidth]);   
          // data-join for rectangles: 
          mainGroup.selectAll('rect').data(data).join('rect')
          .attr('height', yScale.bandwidth()).attr('width', d => xScale(xValue(d)))
          .attr('x', 0).attr('y', d => yScale(yValue(d)));
          // adding axes: 
          const xAxisMethod = d3.axisBottom(xScale);
          const yAxisMethod = d3.axisLeft(yScale);
          const xAxisGroup = mainGroup.append('g').call(xAxisMethod);
          const yAxisGroup = mainGroup.append('g').call(yAxisMethod);
          xAxisGroup.attr('transform', `translate(${0}, ${innerHeight})`);
      })

待续。。。

猜你喜欢

转载自blog.csdn.net/Achernar0208/article/details/125467937