「数据可视化库王者」D3.js 极速上手到Vue应用

前言
D3近年来一直是JavaScript最重要的数据可视化库之一,在创建者Mike Bostock的维护下,前景依然无量,至少现在没有能打的:

D3与众多其他库的区别在于无限定制的能力(直接操作SVG)。
它的底层API提供对原生SVG元素的直接控制,但它也带来了高学习曲线的成本。
我们将把D3和Vue结合在一起 - 使用Vue的动态数据绑定,清晰的语法和模块化结构,可以充分发挥D3的最佳性能。

根据广泛定义,D3可拆分为以下几种分库:

绝大部分的D3课程或书籍,都会着重讲解在其DOM操作功能上,但这明显与近几年来的web框架理念相违背。
用于数据可视化的D3,其核心在于使用绘图指令装饰数据,从源数据创建新的可绘制数据,生成SVG路径以及从数据和方法在DOM中创建数据可视化元素(如轴)的功能。

有许多用于管理DOM的工具,所有这些工具都可以在D3中集成数据可视化功能。这也是D3能与Vue无缝结合的原因之一。
于此,我们不需要从D3 DOM操作功能开始学起,直接通过实例来入门D3。

  1. D3.js 渐进入门
    以下实例的模版均为以下形式:
Learn D3.js

First heading

    
    

复制代码 1. 选择和操作

你需要学习的第一件事是如何使用D3.js选择和操作DOM元素。该库在操作DOM方面实际上非常强大,因此理论上可以将其用作jQuery的替代品。以下代码请逐行添加运行。

// index.js
d3.select();
d3.selectAll();

d3.select(‘h1’).style(‘color’, ‘red’)
.attr(‘class’, ‘heading’)
.text(‘Updated h1 tag’);

d3.select(‘body’).append(‘p’).text(‘First Paragraph’);
d3.select(‘body’).append(‘p’).text(‘Second Paragraph’);
d3.select(‘body’).append(‘p’).text(‘Third Paragraph’);

d3.selectAll(‘p’).style(’’)
复制代码
2.数据加载和绑定

当你要创建可视化时,了解如何加载数据以及将其绑定到DOM非常重要。所以在这个实例中,你将学到这两点。
let dataset = [1, 2, 3, 4, 5];

d3.select(‘body’)
.selectAll(‘p’)
.data(dataset)
.enter()
.append(‘p’) // appends paragraph for each data element
.text(‘D3 is awesome!!’);
//.text(function(d) { return d; });
复制代码
3.创建一个简单的柱状图

首先需要添加一个svg标签

Bar Chart using D3.js


复制代码
然后在index.js中添加(已添加关键注释):

// 数据集
let dataset = [80, 100, 56, 120, 180, 30, 40, 120, 160];
// 定义svg图形宽高,以及柱状图间距
let svgWidth = 500, svgHeight = 300, barPadding = 5;
// 通过图形计算每个柱状宽度
let barWidth = (svgWidth / dataset.length);

// 绘制图形
let svg = d3.select(‘svg’)
.attr(“width”, svgWidth)
.attr(“height”, svgHeight);

// rect,长方形
// 文档:http://www.w3school.com.cn/svg/svg_rect.asp

let barChart = svg.selectAll(“rect”)
.data(dataset) //绑定数组
.enter() // 指定选择集的enter部分
.append(“rect”) // 添加足够数量的矩形
.attr(“y”, d => svgHeight - d ) // d为数据集每一项的值, 取y坐标
.attr(“height”, d => d) // 设定高度
.attr(“width”, barWidth - barPadding) // 设定宽度
.attr(“transform”, (d, i) => {
let translate = [barWidth * i, 0];
return “translate(”+ translate +")";
}); // 实际是计算每一项值的x坐标
复制代码
4. 在图形上方显示数值

这时就需要在上述代码中创建svg的 text文本
let text = svg.selectAll(“text”)
.data(dataset)
.enter()
.append(“text”)
.text(d => d)
.attr(“y”, (d, i) => svgHeight - d - 2)
.attr(“x”, (d, i) => barWidth * i)
.attr(“fill”, “#A64C38”);
复制代码
过程比较简单,就是返回文本,计算x/y坐标,并填充颜色。

  1. scales: 比例尺函数
    D3中有个重要的概念就是比例尺。比例尺就是把一组输入域映射到输出域的函数。映射就是两个数据集之间元素相互对应的关系。比如输入是1,输出是100,输入是5,输出是10000,那么这其中的映射关系就是你所定义的比例尺。

D3中有各种比例尺函数,有连续性的,有非连续性的,在本例子中,你将学到d3.scaleLinear() ,线性比例尺。

5.1 d3.scaleLinear(),线性比例尺
使用d3.scaleLinear()创造一个线性比例尺,其中:

domain()是输入域
range()是输出域
相当于将domain中的数据集映射到range的数据集中。
let scale = d3.scaleLinear().domain([1,5]).range([0,100])
复制代码
映射关系:

值得注意的是,上述代码只是定义了一个映射规则,映射的输入值并不局限于domain()中的输入域。

scale(1) // 输出:0
scale(4) // 输出:75
scale(5) // 输出:100
scale(-1) // 输出:-50
scale(10) // 输出:225
复制代码
于是我们来改造3~4的例子:

let dataset = [1,2,3,4,5];

let svgWidth = 500, svgHeight = 300, barPadding = 5;
let barWidth = (svgWidth / dataset.length);

let svg = d3.select(‘svg’)
.attr(“width”, svgWidth)
.attr(“height”, svgHeight);

let yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, svgHeight]);

let barChart = svg.selectAll(“rect”)
.data(dataset)
.enter()
.append(“rect”)
.attr(“y”, d => svgHeight - yScale(d))
.attr(“height”, d => yScale(d))
.attr(“width”, barWidth - barPadding)
.attr(“transform”, (d, i) => {
let translate = [barWidth * i, 0];
return “translate(”+ translate +")";
});
复制代码
然后就会得到以下图形:

  1. Axes:轴

轴是任何图表的组成部分,本例子中将会用到上面讲到的比例尺函数。

let data= [80, 100, 56, 120, 180, 30, 40, 120, 160];

let svgWidth = 500, svgHeight = 300;

let svg = d3.select(‘svg’)
.attr(“width”, svgWidth)
.attr(“height”, svgHeight);

// 首先是拿最大值构建x轴坐标
let xScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([0, svgWidth]);

// 接下来是反转值,用作y轴坐标。
let yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([svgHeight, 0]);

// 横轴的API使用
let x_axis = d3.axisBottom()
.scale(xScale);

// 纵轴的API使用
let y_axis = d3.axisLeft()
.scale(yScale);

// 在svg中提供了如g元素这样的将多个元素组织在一起的元素。
// 由g元素编组在一起的可以设置相同的颜色,可以进行坐标变换等,类似于Vue中的

svg.append(“g”)
.attr(“transform”, “translate(50, 10)”)
.call(y_axis);

let xAxisTranslate = svgHeight - 20;

svg.append(“g”)
.attr(“transform”, “translate(50, " + xAxisTranslate +”)")
.call(x_axis);
复制代码
7. 创建简易的SVG元素

在这里面,你会创建 , 和 元素
let svgWidth = 600, svgHeight = 500;
let svg = d3.select(“svg”)
.attr(“width”, svgWidth)
.attr(“height”, svgHeight)
.attr(“class”, “svg-container”)

let line = svg.append(“line”)
.attr(“x1”, 100)
.attr(“x2”, 500)
.attr(“y1”, 50)
.attr(“y2”, 50)
.attr(“stroke”, “red”);

let rect = svg.append(“rect”)
.attr(“x”, 100)
.attr(“y”, 100)
.attr(“width”, 200)
.attr(“height”, 100)
.attr(“fill”, “#9B95FF”);

let circle = svg.append(“circle”)
.attr(“cx”, 200)
.attr(“cy”, 300)
.attr(“r”, 80)
.attr(“fill”, “#7CE8D5”);
复制代码
8. 创建饼图

let data = [
{“platform”: “Android”, “percentage”: 40.11},
{“platform”: “Windows”, “percentage”: 36.69},
{“platform”: “iOS”, “percentage”: 13.06}
];

let svgWidth = 500, svgHeight = 300, radius = Math.min(svgWidth, svgHeight) / 2;
let svg = d3.select(‘svg’)
.attr(“width”, svgWidth)
.attr(“height”, svgHeight);

//Create group element to hold pie chart
let g = svg.append(“g”)
.attr(“transform”, “translate(” + radius + “,” + radius + “)”) ;

// d3.scaleOrdinal() 序数比例尺
// schemeCategory10, 颜色比例尺
// D3提供了一些颜色比例尺,10就是10种颜色,20就是20种:
let color = d3.scaleOrdinal(d3.schemeCategory10);

let pie = d3.pie().value(d => d.percentage);

let path = d3.arc()
.outerRadius(radius)
.innerRadius(0);

let arc = g.selectAll(“arc”)
.data(pie(data))
.enter()
.append(“g”);

arc.append(“path”)
.attr(“d”, path)
.attr(“fill”, d => color(d.data.percentage));

let label = d3.arc()
.outerRadius(radius)
.innerRadius(0);

arc.append(“text”)
.attr(“transform”, d => translate(${label.centroid(d)}))
.attr(“text-anchor”, “middle”)
.text(d => ${d.data.platform}:${d.data.percentage}%);
复制代码
9. 创建折线图

最后,你将学习如何创建折线图以显示近四个月的比特币价格。要获取数据,你将使用外部API。这个项目还将你在整个课程中学到的很多概念结合在一起,所以这是一个很好的可视化课程结束。

// 外部API,注意日期记得补零
const api = ‘https://api.coindesk.com/v1/bpi/historical/close.json?start=2019-03-31&end=2019-07-01’;

/**

  • dom内容加载完毕时,从API中加载数据
    */
    document.addEventListener(“DOMContentLoaded”, function(event) {
    fetch(api)
    .then(response => response.json())
    .then(data => {
    let parsedData = parseData(data);
    drawChart(parsedData);
    })
    .catch(err => console.log(err))
    });

/**

  • 将数据解析为键值对
    */
    parseData = data =>{
    let arr = [];
    for (let i in data.bpi) {
    arr.push({
    date: new Date(i), //date
    value: +data.bpi[i] //convert string to number
    });
    }
    return arr;
    }

/**

  • 创建图表
    */
    drawChart = data => {
    let svgWidth = 600, svgHeight = 400;
    let margin = { top: 20, right: 20, bottom: 30, left: 50 };
    let width = svgWidth - margin.left - margin.right;
    let height = svgHeight - margin.top - margin.bottom;

let svg = d3.select(‘svg’)
.attr(“width”, svgWidth)
.attr(“height”, svgHeight);

let g = svg.append(“g”)
.attr(“transform”, “translate(” + margin.left + “,” + margin.top + “)”);

let x = d3.scaleTime()
.rangeRound([0, width]);

let y = d3.scaleLinear()
.rangeRound([height, 0]);

let line = d3.line()
.x(d=> x(d.date))
.y(d=> y(d.value))
x.domain(d3.extent(data, function(d) { return d.date }));
y.domain(d3.extent(data, function(d) { return d.value }));

g.append(“g”)
.attr(“transform”, “translate(0,” + height + “)”)
.call(d3.axisBottom(x))
.select(".domain")
.remove();

g.append(“g”)
.call(d3.axisLeft(y))
.append(“text”)
.attr(“fill”, “#000”)
.attr(“transform”, “rotate(-90)”)
.attr(“y”, 6)
.attr(“dy”, “0.71em”)
.attr(“text-anchor”, “end”)
.text(“Price ($)”);

g.append(“path”)
.datum(data)
.attr(“fill”, “none”)
.attr(“stroke”, “steelblue”)
.attr(“stroke-linejoin”, “round”)
.attr(“stroke-linecap”, “round”)
.attr(“stroke-width”, 1.5)
.attr(“d”, line);
}

复制代码
以上原实例均来自:Learn D3 for free。

scrimba是一个非常神奇的网站。它是使用交互式编码截屏工具构建的。

所有的操作都是:
暂停截屏视频 → 编辑代码 → 运行它! → 查看更改

非常值得安利一波。接下来进入第二部分:Vue中使用D3.js的正确姿势

  1. Vue中使用D3.js的正确姿势
    我们将使用D3和Vue构建一个基本的柱状图组件。网上有一堆例子,但我们将专注于写Vue,而不是滥用D3。

  2. 安装依赖
    首先,我们需要为项目安装依赖项。我们可以简单地安装和使用D3整库:

npm i d3
复制代码
但我在前面讲到,实际上D3是几个分库的集合,考虑到项目的优化,我们只安装所需的模块。

使用Vue Cli 初始化项目即可。

  1. 创建柱状图

  2. 柱状图模块导入

  3. 创建svg元素

因Vue数据响应的特性,我们不需要用到D3操作DOM的那套链式创建。

  1. 数据与窗口大小响应

在mounted钩子中,我们将为窗口调整大小事件添加一个监听器,它将触发绘制动画,并将 大小设置为新窗口的比例。我们不会立即渲染,而是等待300毫秒,以确保完全调整窗口大小。
以下是完整的BarChart.vue,请配合注释食用:

{{ title }}

复制代码
我们将从父组件App.vue获取数据:

复制代码
这时候yarn run serve后将会看到:

好像还缺点显示数值,考虑到该图高度是根据比例尺生成,我们调整下y坐标:

yScale() {
return scaleLinear()
.rangeRound([this.svgHeight, 0])
.domain([this.dataMin > 0 ? 0 : this.dataMin + 2, this.dataMax + 2]);
},
复制代码
在AnimateLoad()末尾添加:

selectAll(“text”)
.data(this.data)
.enter()
复制代码
最后在 元素中添加:

v-for=“item in data”
:key=“item[xKey].amount”
:x=“xScale(item[xKey]) + 30”
:y=“yScale(item[yKey]) - 2”
fill=“red”

{{ item[xKey]}} {{ item[yKey]}}

复制代码

  1. 参考文章

The Hitchhiker’s Guide to d3.js

D3 is not a Data Visualization Library

D3中常用的比例尺

D3 vs G2 vs Echarts

Dynamic Data Visualizations With Vue.js and D3

  1. 总结
    该库几乎凭 Mike Bostock 一人之力完成,且在学术界、专业团队中享有极大声誉。

D3更接近底层,与 g2、echarts 不同,d3 能直接操作 svg,所以拥有极大的自由度,几乎可以实现任何 2d 的设计需求。
正如其名 Data Driven Documents,其本质是将数据与 DOM 绑定,并将数据映射至 DOM 属性上。
D3 长于可视化,而不止于可视化,还提供了数据处理、数据分析、DOM 操作等诸多功能。
如果有想深耕数据可视化方面的前端,D3不得不学。

掌握 D3 后,限制作品水平的只会是想象力而不再是技术。

你可能感兴趣的:(前端程序员,前端工程师,前端)