|
一、D3是什么?全称:Data-DrivenDocuments-数据驱动的文档。github仓库D3是一个被数据驱动的可视化工具库,可以帮助你使用HTML、CSS、SVG和Canvas来展示数据。D3可以将数据绑定到DOM上,然后根据所绑定的数据来计算对应DOM的属性值。(你可以根据一组数据生成一个表格,或生成一个可以过渡和交互的SVG图形等。)二、D3的优势数据能够和Dom绑定在一起,使得数据和图形成为一个整体。在更改图形时,只需对数据操作,图形便会相应的更新。D3自由度度很大,基本可以自己绘制任何想要的图形,这类情况的需求可以使用D3进行二次开发,定制适合的图表,但是开发成本会稍高。因此,开发中要根据实际情况来判断。无论采用哪种方式开发都要做好二次封装,把实现的图表成可复用的组件。数据转换和绘制是独立的:例如将数据转换为图表,需要不少的数学算法。在多种方案之中,D3独立地提供了数据转换和图表绘制的工具函数,即将开发者的数据转换为图表绘制工具函数所需的数据格式,然后按照开发者的需求进行绘制。代码简洁:采用了链式调用的方式简化图形的绘制。大量布局工具:饼状图、树形图、打包图、矩阵图等。D3将大量复杂的算法封装成了一个一个的布局,适用于各种的图表制作。主要基于Svg,缩放不会损失精度。三、D3与ECharts的对比D3可定制自由度高,而ECharts不可定制,自由度低。D3英文文档完善,使用事例只能参考,而ECharts中文文档完善、使用例子功能完善。D3需要开发,效率低,ECharts可以快速配置生成图表。D3较难,必须熟悉Svg、Canvas、D3Api,ECharts只需配置。四、D3的使用场景ECharts等配置式图表库无支持图表时使用。数据量不是特别大或者事件交互比较精细的场景采用D3,可以先在官方示例和demo搜索有没有类似的图表实现。对于频繁的DOM操作十分消耗性能。对于用户体验的影响便是可能出现闪烁、卡顿等现象。可以参考前端界对于页面DOM卡顿的解决方案:VirtualDOM技术。通过支持VirtualDom技术的框架如Vue与D3.js结合。使用D3来计算,Vue等VirtualDOM框架管理SVG节点和属性。对于数据量比较大的场景,可以采用D3Canvas来实现,或者ZRender(ECharts使用的矢量图形库)来定制,这个需要比较熟悉Canvas绘图,而且需要注意性能的优化。五、D3的开发流程根据需求确定图表类型。把输入的原始数据转化为标准的D3可接受的数据格式。根据原始数据定义好x轴函数、y轴函数和定义好作图方式在Svg上,画出x轴y轴、根据原始数据结合x轴和y轴函数做图。画出等细节东西。给已经完成的图形添加动画效果和时间交互。六、D3的版本更替D3现存三种版本:V3、V4、V5。在V3更新到V4的时候大刀阔斧地规范了一些方法,各个Api的调用形式有不同的变化。相比V3到V4的变化,V4到V5只有少量向前不兼容的改动。/* v3 比例尺 */d3.scale.linear()/* v4 比例尺 */d3.scaleLinear()七、D3的使用实例earth-master该项目使用D3Geo模块进行地球的渲染。D3Geo模块支持加载GeoJson、TopoJson等标准的地理信息数据格式。D3Geo模块根据地理信息渲染出不同的投影(如墨卡托投影,高斯投影等)。可方便对地理要素进行着色。墨卡托投影绘制的地球及风场动画的制作:帕特森投影绘制的地球及风场动画:八、D3的常用概念介绍8.1基本概念:8.1.1选择集选择集和数据是D3最重要的概念。选择集是被选择元素的集合。// 返回匹配选择器的第一个元素d3.select('.class')// 返回匹配选择器的所有元素d3.selectAll('.class')8.1.2数据绑定将数据和dom绑定是D3的特色,数据绑定就是使被选择元素含有数据。var data = [{value: 11}, {value: 12}] // 选择集的每一个元素都绑定相同的数据selection.datum(data)// 选择集的每一个元素都绑定values的每一项selection.data(data)8.1.3数据驱动当数组长度与元素数据不一样时,有enter部分和exit部分。如果没有足够的元素,就使用enter处理方法添加元素。如果元素的数据需要更新,则使用update处理方法更新元素的数据。// 颜色插值var compute = d3.interpolate(d3.rgb(255, 255, 153), d3.rgb(255, 204, 102));// 颜色比例尺var linear = d3.scaleLinear() .domain([0, 10]) .range([0, 1]);// 根据数据计算圆心x坐标function calCX(d, i) { return i * 30 + 5}// 根据数据计算圆心y坐标function calCY(d) { return (d - 1) * 30 + 5}// 根据数据计算圆形半径function calCR(d){ return d * 2.5}// 根据数据计算填充颜色function calFill(d){ return compute(linear(d))}// 根据data绘制图形function render(data){ var circles = d3.select('#target').selectAll('circle').data(data) // 处理待更新的数据集 circles.style('fill', 'rgb(255,102,102)') .attr('r', calCR) .attr('cx', calCX) .attr('cy', calCY) // 处理新添加的数据集 circles.enter() .append('circle') .style('fill', calFill) .attr('r', calCR) .attr('cx', calCX) .attr('cy', calCY) // 处理已退出的数据集 circles.exit() .remove()}// 添加Svg画布var svg = d3.select('body') .append("svg") .attr("id", "target") .attr("width", "300px") .attr("height", "300px") .attr("style", "border:1px solid black") // 绘制数据render([1, 5, 10])render([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])运行结果如下:8.1.4数据的转换和绘制// 数据处理var hierarchy = d3.hierarchy(dataset)console.log('hierarchy', hierarchy)打印结果如下:// 将数据转换为绘制所需要的数据格式var createTree = d3.tree() .size([200, 200]) .separation(function(a, b){ return a.parent == b.parent? 1: 2})var tree = createTree(hierarchy)console.log('tree', tree)// 生成节点所需数据 var descendants = tree.descendants()console.log('descendants', descendants)// 生成连线所需数据var links = tree.links()console.log('links', links)打印结果如下:经过绘制:8.2绘制8.2.1颜色// 创建各种格式的颜色var color = d3.color('steelblue')如果要计算介于两个颜色之间的颜色,需要用到插值。D3提供和很多预设的颜色主题,可以与比例尺配合进行方便地颜色绘制。 // D3提供的颜色主题 配合比例尺进行绘制 var scheme = d3.schemeCategory10 var scale = d3.scaleOrdinal(scheme)]经过绘制:8.2.2路经生成器D3中引入了路经生成器的概念,能够自动根据数据生成路经。线段生成器:用于生成线段的路经生成器。区域生成器:用于生成区域的路经生成器。弧生成器:用于生成弧的路经生成器。还有许多其他路经生成器官方文档有介绍。8.3布局8.3.1概念布局是D3中很重要的内容,使用布局能够轻松创建很多图表。我们可以简单地将布局理解为“数据转换”。布局的意义在于计算出方便绘图的数据。D3提供的布局有:饼状图(Pie)、力导向图(Force)、弦图(Chord)、树状图(Tree)、集群图(Cluster)、捆图(Bundle)、打包图(Pack)、直方图(Histogram)、分区图(Partition)、堆栈图(Stack)、矩阵数图(Treemap)、层级图(Hierarchy)。8.3.2使用布局的基本步骤:确定初始数据:转换前的数据要符合一定的格式要求,才能被布局函数转换。转换数据:指布局根据初始数据计算绘图所需数据。绘制:使用转换后的数据在SVG画板上绘图(很可能会用到生成器)// 饼状图布局(数据转换)生成arc所需要的数据格式var createPie = d3.pie() .value(function(d){ return d.value })var pie = createPie(dataset)// 路经生成器var arc = d3.arc() .innerRadius(0) .outerRadius(142)经过绘制:8.4图表8.4.1比例尺定义域和值域的对应法则定量比例尺:线性比例尺、指数和对数比例尺、两字和分位比例尺、阈值比例尺。序数比例尺。// 线性比例尺var linear = d3.scaleLinear() .domain([10, 130]) .range([0, 960])linear(20) // 80linear(50) // 3208.4.2坐标轴d3.axisLeft()创建坐标轴(可以创建不同方向的)。d3.scale([scale])设定或获取坐标轴比例尺。坐标轴比例尺:定义域为坐标轴刻度值的范围,值域是坐标轴实际的像素长度。有了比例尺后,图表元素的位置、长度等属性都由比例尺计算得到。// 坐标轴比例尺var xScale = d3.scaleLinear() .domain([-50, 50]) .range([0, 300])var yScale = d3.scaleLinear() .domain([-50, 50]) .range([300, 0])// x、y坐标轴var xAxis = d3.axisBottom(xScale) .ticks(11)var yAxis = d3.axisLeft(yScale) .ticks(11)经过绘制:8.5与Canvas的混用D3是一款基于Svg的可视化工具库,Svg比起Canvas来说,可以更方便地完成复杂的交互方式,以及由于Svg属于矢量图形绘制技术,在放大缩小时不会产生失真。不过Canvas也有很多它的优点,所以在一些情况下,我们会联合使用D3和Canvas,我们用D3将数据转换为便于绘制的格式,或者使用一些D3现成的算法,在Canvas画布上进行绘制。示例代码如下,省略了部分工具函数代码。// D3-geo模块投影相关功能(将笛卡尔坐标系的坐标投影至其他坐标系)const projection = d3.geoOrthographic() .rotate([0, 40]) .translate([250, 250]) .fitExtent([[20,20], [500-20, 500-20]], {type:"Sphere"})// 利用Canvas的context2D绘制三角形function drawTriangle([p0, p1, p2]){ context.moveTo(...p0) context.lineTo(...p1) context.lineTo(...p2) context.closePath()}// 获得可绘制的三角形面坐标const faces = geodesic(10)// 渲染函数,可增加设置投影代码,进而增加动效。function render(){ const triangles = faces.map((d,i)=>(d=d.map(projection),d.index = i, d)) .filter(d=>d3.polygonArea(d) d.fill) .attr("d", gear) .attr("transform", d=>`translate(${d.origin}) rotate(${(angle/d.radius) % 360})`)// 动画function update(){ path.attr("transform", d=>`translate(${d.origin}) rotate(${(angle/d.radius) % 360})`) angle += speed frameAngle += speed window.requestAnimationFrame(update)}update()经过绘制:九、结束:D3的基本使用今天就介绍到这里,如果大家感兴趣的话,github上有详细完整的文档可供参考。戳我进入github仓库十、感谢用友APM友云音前端技术团队,博客中提到的ECharts和D3的区别。《精通D3.js:交互式数据可视化高级编程》的作者吕之华。github项目earth-master的所有贡献者。百度上搜到一些零碎的点的作者。
|
|