23、ES6、TypeScript 与外部 D3.js 库使用指南

ES6、TypeScript 与外部 D3.js 库使用指南

1. ES6 与 TypeScript 概述

ES6 在现有 JavaScript 版本 ES5 的基础上进行了扩展,它并没有从根本上改变这门语言,只是添加了一些特性和语法糖,让 JavaScript 的开发更加便捷。不过,目前现代浏览器对 ES6 的支持尚未达到 100%,因此需要进行预处理,将代码转换为可在任何浏览器中运行的 JavaScript 代码。

TypeScript 是 JavaScript 的一个替代方案,它为 JavaScript 添加了对象类型,使得开发者在编译时能够捕获更多的错误,并且在编辑器中提供更好的代码补全功能。

2. D3 与 TypeScript

和使用 ES6 一样,使用 TypeScript 时也需要将代码转换为可在任何浏览器中运行的 JavaScript 代码。虽然现代浏览器最终会支持 ES6,无需进行代码转换,但 TypeScript 始终需要进行转换才能在浏览器中运行。TypeScript 紧密遵循 JavaScript 规范,使得从 JavaScript 过渡到 TypeScript 变得非常容易,之前 ES6 的示例同样适用于 TypeScript。此外,TypeScript 还具有类型和类型检查的优势。

2.1 启动 TypeScript 项目

要启动 TypeScript 项目,可以在 <DVD3>/chapter - 09/ts/ 目录下运行以下命令:

$ npm install
$ npm run start

运行上述命令后,项目将在 http://localhost:8080/ 上运行。

2.2 在编辑器中使用类型

在支持 TypeScript 的编辑器中打开项目时,能够充分体现 TypeScript 的强大之处。例如,打开项目中的 index.ts 文件时,每当使用 d3.js 的功能,都能获得代码补全,包括方法签名。这对于探索 D3 庞大且有时复杂的 API 非常有帮助。

2.3 通过编译时类型检查防止错误

D3 的 API 非常庞大,有许多不同的对象,每个对象都有自己的函数签名。在使用 D3 时,很容易出现错误。例如,定义 d3.scaleLinear 时,可能会错误地这样定义:

var yBand = d3.scaleLinear()
      .domain(0, 10)
      .range(0, height);

这段代码在 ES6 或标准 JavaScript 中不会报错,但在使用时会出现意外的 NaN 错误或奇怪的值。原因是 domain range 函数需要的是数组参数,而不是两个单独的参数。使用 TypeScript 时,编译器能够检测到这个问题:

ERROR in ./app/js/index.ts
(37,17): error TS2346: Supplied parameters do not match any signature of call target.
webpack: Failed to compile.
2.4 处理复杂的类型签名

有时,TypeScript 编译器会对一些看似正常的代码报错。例如:

let color = d3.scaleOrdinal()
    .domain(symbols.map(s => s.name))
    .range(d3.schemeCategory10);
let symbolGroups = svg.selectAll(".symbol").data(symbols)
    .enter()
    .append("g")
        .attr("class", d => "symbol")
        .attr("transform", d => `translate(${xBand(d.name)} 40)`);
symbolGroups
    .append("path")
        .attr("fill", d => color(d.name))
        .attr("d", d => d3.symbol().size(2400).type(d.symbol)());

编译这段代码时,会出现如下错误:

ERROR in ./app/js/index.ts
(45,27): error TS2345: Argument of type '(this: BaseType, d: { name: string; symbol: SymbolType; }) => {}' is not assignable to parameter of type 'ValueFn<BaseType, { name: string; symbol: SymbolType; }, string | number | boolean>'.
  Type '{}' is not assignable to type 'string | number | boolean'.
    Type '{}' is not assignable to type 'false'.

这个错误信息比较晦涩,实际上是编译器无法正确确定 color(d.name) 函数的返回类型。可以通过添加额外的类型信息来解决这个问题:

.attr("fill", d => <string>color(d.name))
2.5 轻松创建类

在 JavaScript 中,通常会创建简单的对象来存储数据,例如:

let symbols = [
    {name: 'Cross', symbol: d3.symbolCross},
    {name: 'Circle', symbol: d3.symbolCircle},
    {name: 'Diamond', symbol: d3.symbolDiamond},
    {name: 'Square', symbol: d3.symbolSquare},
    {name: 'Star', symbol: d3.symbolStar},
    {name: 'Triangle', symbol: d3.symbolTriangle},
    {name: 'Wye', symbol: d3.symbolWye}
];

使用 TypeScript 可以创建类,提供额外的类型检查。例如:

class SymbolWrapper {
  constructor(public name: string, public symbol: SymbolType) {}
}

然后可以这样使用这个类:

let symbols = [
  new SymbolWrapper('Cross', d3.symbolCross),
  new SymbolWrapper('Circle', d3.symbolCircle),
  new SymbolWrapper('Diamond', d3.symbolDiamond),
  new SymbolWrapper('Square', d3.symbolSquare),
  new SymbolWrapper('Star', d3.symbolStar),
  new SymbolWrapper('Triangle', d3.symbolTriangle),
  new SymbolWrapper('Wye', d3.symbolWye)
];

如果重命名类的属性,TypeScript 编译器会立即指出代码中需要修改的地方。

3. 外部图表库

除了直接使用 D3 创建可视化图表,还有许多基于 D3 构建的扩展和额外的图表库。以下介绍几个知名且流行的库:
| 库名称 | 适用 D3 版本 | 特点 |
| ---- | ---- | ---- |
| Dimple.js | D3 v4 | 旨在简化 D3 的基本使用,为不太精通 D3 的人提供简单易用的 API,用于创建基于轴的可视化图表。 |
| MetricsGraphics.js | D3 v4 | 专注于提供基于时间序列的图表,如折线图、直方图和散点图等,也有一些实验性的其他类型图表。 |
| C3 | D3 v3 | 提供大量不同类型的 D3 可视化图表,只需提供数据,C3 就能渲染图表,但尚未更新到 D3 v4。 |
| nvd3 | D3 旧版本 | 提供大量现成的图表类型,无需深入了解 D3 即可直接使用,但使用的是 D3 的旧版本。 |

下面详细介绍 Dimple.js 和 MetricsGraphics.js。

3.1 Dimple.js

Dimple.js 提供了大量现成的图表,其目标是让图表的使用变得简单。以下是创建简单柱状图的示例代码:

var data = [
    { "country":"The Netherlands", "value":5000 },
    { "country":"Germany", "value":3000 },
    { "country":"Spain", "value":4000 },
    { "country":"Belgium", "value":2000 },
    { "country":"France", "value":4500 }
];
var chart = new dimple.chart(svg1, data);
chart.setBounds(0, 0, width, height);
chart.addCategoryAxis("x", "country");
chart.addMeasureAxis("y", "value");
chart.addSeries(null, dimple.plot.bar);
chart.draw();

这个示例展示了 Dimple.js 的基本使用方法:定义数据,创建图表对象,设置图表大小,定义坐标轴,指定图表类型并绘制图表。通过简单修改代码,还可以将柱状图转换为水平图表或堆叠图表等。

graph LR
    A[定义数据] --> B[创建图表对象]
    B --> C[设置图表大小]
    C --> D[定义坐标轴]
    D --> E[指定图表类型]
    E --> F[绘制图表]
3.2 MetricsGraphics.js

MetricsGraphics.js 专注于提供基于时间的图表,使用方法也很简单。以下是创建折线图的示例代码:

d3.json("./data/annual.json", function (data) {
    MG.data_graphic({
        title: "Annual temperature mean",
        description: "Annual temperature mean",
        data: data.filter(function(el) {return el.Source === "GCAG"}),
        width: width,
        height: height,
        target: "#id-1",
        x_accessor: "Year",
        y_accessor: "Mean",
        area: false,
        min_y: -1,
        chart_type: 'line',
        baselines: [{value:0, label: 'mean'}],
        interpolate: d3.curveLinear,
    })
});

通过调用 MG.data_graphic 函数并配置相应的属性,就可以创建折线图。

综上所述,ES6 和 TypeScript 为 D3 开发提供了更强大的功能和更好的开发体验,而外部图表库则可以帮助开发者更快速地创建各种类型的可视化图表。无论是使用哪种技术,D3 创建可视化图表的基本模式都是相似的。

4. 外部图表库使用案例分析

为了更好地理解 Dimple.js 和 MetricsGraphics.js 的实际应用,下面通过一些具体的案例来进一步说明。

4.1 Dimple.js 案例:堆叠图表与气泡图表转换

在前面的基础上,我们来看如何将堆叠柱状图转换为气泡图。以下是具体的代码实现:

// 堆叠数据
var stackedData = [
    { "i" : 1, "country":"Netherlands", "value":2000, "Depth": 100 },
    { "i" : 1, "country":"Belgium", "value":3000, "Depth": 200 },
    { "i" : 1, "country":"Germany", "value":3000, "Depth": 200 },
    { "i" : 2, "country":"Netherlands", "value":3000, "Depth": 300 },
    { "i" : 2, "country":"Belgium", "value":3000, "Depth": 200 },
    { "i" : 2, "country":"Germany", "value":3000, "Depth": 200 },
    { "i" : 3, "country":"Netherlands", "value":1000, "Depth": 100 },
    { "i" : 3, "country":"Belgium", "value":4000, "Depth": 400 },
    { "i" : 3, "country":"Germany", "value":4000, "Depth": 400 },
    { "i" : 4, "country":"Netherlands", "value":2000, "Depth": 200 },
    { "i" : 4, "country":"Belgium", "value":100, "Depth": 700 },
    { "i" : 4, "country":"Germany", "value":100, "Depth": 700 },
];

// 创建堆叠柱状图
var stackedChart = new dimple.chart(svg2, stackedData);
stackedChart.setBounds(0, 0, width, height);
stackedChart.addCategoryAxis("x", "i");
stackedChart.addMeasureAxis("y", "value");
stackedChart.addSeries("country", dimple.plot.bar);
stackedChart.addLegend(60, 10, width - 30, 20, "right");
stackedChart.draw();

// 转换为气泡图
var stackedChart = new dimple.chart(svg2, stackedData);
stackedChart.setBounds(0, 50, width, height-50);
stackedChart.addCategoryAxis("x", "i");
stackedChart.addMeasureAxis("y", "value");
stackedChart.addMeasureAxis("z", "Depth");
stackedChart.addSeries("country", dimple.plot.bubble);

从上述代码可以看出,Dimple.js 的灵活性很高,只需要对代码进行少量修改,就可以实现不同类型图表的转换。

以下是这个过程的流程图:

graph LR
    A[定义堆叠数据] --> B[创建堆叠柱状图]
    B --> C[设置图表参数]
    C --> D[绘制堆叠柱状图]
    D --> E[修改图表参数]
    E --> F[转换为气泡图]
    F --> G[绘制气泡图]
4.2 MetricsGraphics.js 案例:温度偏差折线图

在前面已经给出了创建温度偏差折线图的基本代码,下面我们来详细分析代码的各个部分:

d3.json("./data/annual.json", function (data) {
    MG.data_graphic({
        title: "Annual temperature mean",
        description: "Annual temperature mean",
        data: data.filter(function(el) {return el.Source === "GCAG"}),
        width: width,
        height: height,
        target: "#id-1",    // 我们创建了具有此 id 的容器
        x_accessor: "Year",
        y_accessor: "Mean",
        area: false,
        min_y: -1,
        chart_type: 'line',
        baselines: [{value:0, label: 'mean'}],
        interpolate: d3.curveLinear,
    })
});
参数 说明
title 图表的标题
description 图表的描述
data 用于绘制图表的数据,这里过滤出了来源为 “GCAG” 的数据
width height 图表的宽度和高度
target 图表要渲染到的容器的 ID
x_accessor x 轴的数据访问器,这里指定为 “Year”
y_accessor y 轴的数据访问器,这里指定为 “Mean”
area 是否绘制面积图,这里设置为 false 表示不绘制
min_y y 轴的最小值
chart_type 图表类型,这里设置为 ‘line’ 表示折线图
baselines 基线设置,这里添加了一条值为 0 的基线,并标记为 ‘mean’
interpolate 插值方法,使用 d3.curveLinear 来绘制通过数据点的线
5. 总结与选择建议

通过以上对 ES6、TypeScript 和外部 D3.js 库的介绍,我们可以总结出以下要点:

  • ES6 :为 JavaScript 带来了更多便捷的特性和语法糖,虽然目前浏览器支持不完全,但未来趋势良好。在需要兼容性的项目中,需要进行代码转换。
  • TypeScript :为 JavaScript 增加了类型系统,能够在编译时捕获更多错误,提高代码的可维护性和开发效率。尤其适合大型项目和团队协作开发。
  • 外部图表库
    • Dimple.js :适合初学者和想要快速创建基于轴的可视化图表的开发者,提供了简单易用的 API,代码修改灵活,可以轻松实现不同类型图表的转换。
    • MetricsGraphics.js :专注于时间序列图表的绘制,对于处理时间相关的数据非常方便,代码配置简单。
    • C3 :提供了丰富的图表类型,但由于尚未更新到 D3 v4,在与最新版本 D3 结合使用时可能会出现问题。
    • nvd3 :有大量现成的图表类型,但使用的是 D3 的旧版本,不适合需要使用最新 D3 特性的项目。

在选择使用哪种技术和库时,可以根据项目的需求、团队的技术栈和开发者的经验来决定。如果是小型项目且对兼容性要求不高,可以考虑直接使用 ES6;如果是大型项目,建议使用 TypeScript 来提高代码质量。对于图表库的选择,如果需要创建基于轴的图表,Dimple.js 是不错的选择;如果专注于时间序列图表,MetricsGraphics.js 更为合适。

总之,无论是使用原生 D3 还是外部图表库,都可以根据具体情况灵活选择,以达到最佳的开发效果。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值