文章目录:引言1.D3js是什么2.D3js相对其他数据可视化方案的优势2.1SVG对比Canvas2.2D3.js对比Echarts3.怎么用D3.js开发一个树图
文章目录:
- 引言
- 1. D3js 是什么
- 2. D3js 相对其他数据可视化方案的优势
- 2.1 SVG 对比 Canvas
- 2.2 D3.js 对比 Echarts
- 3. 怎么用 D3.js 开发一个树图
- 3.1 前置基础
- 3.2 d3开发树图流程
- 3.3 动手实现一个树图
- 3.3.1 普通tide tree
- 3.3.2 你的树图不简单—— radio tide tree
- 3.3.3 更多可能——更多类型的树图
引言
上周我们组新开项目,技术调研之后决定使用 d3.js 做数据可视化开发,mentor让我来做初期技术调研,之后的技术交流会与大家分享。可是初期的调研着实碰壁了 网上一些demo用的版本过低,包括d3js官网的demo也是如此,一些重要的API已废弃。
可是我们项目不打算用过低版本,于是我结合网上demo和d3文档,各种谷歌百度,总算是用最新版本实现了一些demo。想到好久没有记录学习到的技术了,趁此机会写下这篇 d3.js可视化开发快速入门博客,与大家交流分享。
1. D3js 是什么
近年来,数据可视化越来越流行,许多拥有大量数据的大型网站都开始使用可视化技术,使得大量杂乱的数据规则起来,易于理解,真可谓“一图胜千言”,毫不夸张。正是在这种趋势的催动下,各种数据化工具如井喷式的发展,而D3,Echarts正是其中的佼佼者。
D3 的全称是(Data-DrivenDocuments),顾名思义是一个被数据驱动的文档。其实是对数据进行可视化的Javascript库。D3将强大的可视化和交互技术与数据驱动的DOM操作方法相结合,能最大限度地使用现代浏览器的性能同时为设计可视化界面保留了最大的自由度。D3强调Web标准,所以无需将自己与专有框架联系起来。
D3js 是一个可以基于数据来操作文档的 Javascript 库。可以帮助你使用 HTML, CSS, SVG 以及 Canvas 来展示数据。D3 遵循现有的 Web 标准,可以不需要其他任何框架独立运行在现代浏览器中,它结合强大的可视化组件来驱动 DOM 操作。
D3 可以将数据绑定到 DOM 上,然后根据数据来计算对应 DOM 的属性值。例如你可以根据一组数据生成一个表格。或者也可以生成一个可以过渡和交互的 SVG 图形。
D3 不是一个框架,因此也没有操作上的限制。没有框架的限制带来的好处就是你可以完全按照自己的意愿来表达数据,而不是受限于条条框框,非常灵活。D3 的运行速度很快,支持大数据集和动态交互以及动画。
2. D3js 相对其他数据可视化方案的优势
对比D3和ECharts之前,先对比下两种浏览器图形渲染技术Canvas和SVG的主要区别,基本上所有的浏览器可视化第三方库都是基于这两种浏览器图形渲染技术实现的:
2.1 SVG 对比 Canvas
SVG | Canvas |
---|
矢量图不依赖分辨率,放大不失真 | 位图依赖分辨率 |
基于XML支持DOM事件处理器,SVG中每个图形节点都是单独的DOM节点可以附加js事件 | 不支持事件处理器 如果要为细粒度的元素添加事件,就需要边缘检测算法,无疑增加了难度而且不一定能保证十分精确 |
适合带有大型渲染区域的应用(比如:地图(每个节点区域都比较大,节点不是很多)) | 最适合图像密集型的应用比如游戏 能够方便的保存结果图像 |
复杂度高会减慢渲染速度(任何过度使用DOM的应用都快不了) | 一旦图形绘制完成,就不会再得到浏览器的关注 如果位置或者大小变化整个区域都要重新绘制 |
可以使用CSS 文本渲染能力较强 | 文本渲染能力较弱 |
简单对比后,可以得出结论:
SVG比较适合 数据量不是很大,应用存在大量的用户交互场景。
Canvas比较适合事件交与较少,文本较少,或者数据量大画面刷新快的场景。
2.2 D3.js 对比 Echarts
ECharts | D3 |
---|
Canvas为主4.0+也支持SVG | SVG为主4.0+也支持Canvas |
提供很多图表通过简单配置可以满足大部分需求 | 不能通过简单配置实现大部分图表 |
基本不可定制 | 自由度很大,基本可以自己绘制任何图表 |
提供完善中文文档,示例功能完善 | 提供完善英文文档,3.x版本中文文档比较完善,之后翻译的不完善,部分翻译可能需求查看英文方便理解,大部分示例需要完善才能使用,主要是参考价值 |
开发效率高,通过快速配置即可完成 | 大部分图表需要开发 |
大数据量性能较好 | 需要实现时手动优化 例如virtual DOM |
提供基于WebGl的GL版实现3D图表 | 借助其他库来实现例如:three.js、glMatrix、Sylvester |
总结:
ECharts等提供的图表的确可以满足大部分的需求,遵循了数据可视化的一些经典范式,一切皆可配置。然而,每个不同的行业对于数据可视化都会有一些定制化的需求,希望能以一些带有行业特征的图表向使用者展示数据背后隐藏的秘密,但是ECharts这类图形库基本不可定制,而D3自由度度很大,基本可以自己绘制任何想要的图形,这类情况的需求可以使用D3进行二次开发,定制适合的图表,但是开发成本会稍高。因此,开发中要根据实际情况来判断。无论采用哪种方式开发都要做好二次封装,把实现的图表成可复用的组件。
ECharts,hightchart这类可视化库,经过配置即可完成图表的绘制,D3要稍微麻烦一点。
使用D3绘图需要:
- 熟悉SVG(canvas)作图、熟悉CSS
- 熟悉D3的API
- 学习D3.js的编程风格
总结:D3和ECharts,hightchart这类可视化库相比属于可视化中较为基础的工具库,需要一定的可视化方面的基础才能比较快速开发出较复杂的图表,可以先尝试实现简单的图形,官网首页上面就大部分酷炫的在线Demo,难度较高可以先从demo网址找些简单点来实现
3. 怎么用 D3.js 开发一个树图
3.1 前置基础
想要使用D3.js做数据可视化你需要了解一些前置知识,如SVG
首先你需要熟悉它的常用标签及属性:
...
width height color transform fill stroke class d ...
3.2 d3开发树图流程
-
根据需求确定图表类型(例如树图)
-
数据预处理(把输入的原始数据转化成为标准的D3可接受的数据格式一般图像是对象数组)
-
根据处理好的数据结合布局API作图(例如树图 :d3.hierarchy()和d3.tree()的结合使用)
-
再调整文本位置,间隔等细节的东西,从而让图形布局更合理
-
给已经完成的图形添加动画效果和事件交互
3.3 动手实现一个树图
3.3.1 普通tide tree
tide tree长啥样?
怎么实现?
第一步:在页面中初始化设置 svg
let margin = { top: 470, right: 90, bottom: 30, left: 90 };let width = 1960 - margin.left - margin.right;let height = 1200 - margin.top - margin.bottom;let svg = d3.select("body").append("svg").attr("width", width).attr("height", height).append("g").attr("transform", `translate(${margin.left},${margin.top})`);
第二步:数据预处理
let tree = d3.tree().nodeSize([30, 170]);let root = d3.hierarchy(this.treeData); this.tree(root);
第三步:生成节点和链接
let link = svg.selectAll(".link").data(links).join("path").attr("class", "link").attr("fill", "none").attr("stroke", "#ccc").attr("d", diagonal);let node = svg.selectAll(".node").data(nodes).enter().append("g").attr("class", "node").attr("transform", function (d) {return "translate(" + d.y + "," + d.x + ")";})node.append("circle").attr("r", 5);node.append("text").text((d) => {console.log("d", d);return d.data.name;}).attr("dx", (d) => (d.children ? -15 : 15)).attr("dy", 5).attr("text-anchor", (d) => (d.children ? "end" : "start"));
第四步:给已经完成的图形添加动画效果和事件交互,例如这里给每个节点添加点击事件和mouseover事件
let node = svg.selectAll(".node").data(nodes).enter().append("g").attr("class", "node").attr("transform", function (d) {return "translate(" + d.y + "," + d.x + ")";}).on("click", (d) => {console.log(`----------click------------`);}).on("mouseover", () => {console.log(`----------hover------------`);});
没错,正是在第三步添加节点后接着链式调用on()方法即可绑定事件
如此,一颗简单的树就生成了!
3.3.2 你的树图不简单—— radio tide tree
关于树图,你是否还有其他联想?接下来我们看看一个不太“规矩”的树图——radio tide tree
radio tide tree又是如何实现的呢?
一番摸索后,我发现这两种树图实现的差别在于:
- d3.tree()传入参数不同
- link对角线生成器不同
- 节点transform设置不同
差别 | tide tree | radio tide tree |
---|
d3.tree()传入参数不同 | [width,height] | [radian,radius] |
link对角线生成器不同 | linkHorizontal()或者 | linkRadial() |
节点transform设置不同 | x,y坐标 | 极坐标 |
具体设置方法,如下:
切记d3.tree().size()参数为弧度和半径
let tree = d3.tree().size([2 * Math.PI, 140]).separation(function (a, b) {return (a.parent == b.parent ? 1 : 2) / a.depth;}); let root = d3.hierarchy(this.treeData);tree(root);
link对角线生成器的不同,linkRadial()
let link = svg.selectAll(".link").data(links).join('path').attr("class", "link").attr('fill','none').attr('stroke','#ccc').attr('stroke-width','1.5px').attr("d",d3.linkRadial().angle(function (d) {return d.x;}).radius(function (d) {return d.y;}));
节点transform设置不同,极坐标的方式设置
function radialPoint(x, y) {return [(y = +y) * Math.cos((x -= Math.PI / 2)), y * Math.sin(x)];}let node = svg.selectAll(".node").data(nodes).enter().append("g").attr("class", "node").attr("stroke", "red").attr("transform", d => `rotate(${d.x * 180 / Math.PI - 90})translate(${d.y},0) `)
此处可上滑与tide tree代码进行对比,观察不同
3.3.3 更多可能——更多类型的树图
当然,树图的具体实现效果绝不仅此两种,可以预见的是,符合树型特点的图皆可称之为树图,它们对数据的层次结构要求皆相同
例如:
indented tree
Tangled tree visualization II
Sunburst
看了这么多炫酷的tree,不如自己去操作一番!!
文中所用demo已上传到github仓库:demo地址链接https://github.com/coder-WJie/D3-test/tree/
D3js 官网:https://d3js.org/