d3.js实现立体柱图的方法详解

这篇文章主要给大家介绍了利用d3.js实现立体柱图的方法,文中给出了详细的介绍和示例代码供大家参考学习,需要的朋友们下面来一起看看吧。

前言

众所周知随着大数据时代的来临,数据可视化的重要性也越来越凸显,那么今天就基于d3.js今天给大家带来可视化基础图表柱图进阶:立体柱图,之前介绍过了d3.js实现柱状图的文章,感兴趣的朋友们可以看一看。

关于d3.js

d3.js是一个操作svg的图表库,d3封装了图表的各种算法.对d3不熟悉的朋友可以到d3.js官网学习d3.js.

另外感谢司机大傻(声音像张学友一样性感的一流装逼手)和司机呆(呆萌女神)等人对d3.js进行翻译!

HTML+CSS

   Title 

JS

当前使用d3.v4+版本

 

图表所需数据

 var data = [{ "letter": "白皮鸡蛋", "child": { "category": "0", "value": "459.00" } }, { "letter": "红皮鸡蛋", "child": { "category": "0", "value": "389.00" } }, { "letter": "鸡蛋", "child": { "category": "0", "value": "336.00" } }, { "letter": "牛肉", "child": { "category": "0", "value": "282.00" } }, { "letter": "羊肉", "child": { "category": "0", "value": "249.00" } }, { "letter": "鸭蛋", "child": { "category": "0", "value": "242.00" } }, { "letter": "红薯", "child": { "category": "0", "value": "222.00" } }, { "letter": "白菜", "child": { "category": "0", "value": "182.00" } }, { "letter": "鸡肉", "child": { "category": "0", "value": "102.00" } }];

图表的一些基础配置数据

 var margin = { top: 20, right: 50, bottom: 50, left: 90 }; var svgWidth = 1000; var svgHeight = 500; //创建各个面的颜色数组 var mainColorList = ['#f6e242', '#ebec5b', '#d2ef5f', '#b1d894','#97d5ad', '#82d1c0', '#70cfd2', '#63c8ce', '#50bab8', '#38a99d']; var topColorList = ['#e9d748', '#d1d252', '#c0d75f', '#a2d37d','#83d09e', '#68ccb6', '#5bc8cb', '#59c0c6', '#3aadab', '#2da094']; var rightColorList = ['#dfce51', '#d9db59', '#b9d54a', '#9ece7c','#8ac69f', '#70c3b1', '#65c5c8', '#57bac0', '#42aba9', '#2c9b8f']; var svg = d3.select('#chart') .append('svg') .attr('width', svgWidth) .attr('height', svgHeight) .attr('id', 'svg-column');

创建X轴序数比例尺

 function addXAxis() { var transform = d3.geoTransform({ point: function (x, y) { this.stream.point(x, y) } }); //定义几何路径 var path = d3.geoPath() .projection(transform); xLinearScale = d3.scaleBand() .domain(data.map(function (d) { return d.letter; })) .range([0, svgWidth - margin.right - margin.left], 0.1); var xAxis = d3.axisBottom(xLinearScale) .ticks(data.length); //绘制X轴 var xAxisG = svg.append("g") .call(xAxis) .attr("transform", "translate(" + (margin.left) + "," + (svgHeight - margin.bottom) + ")"); //删除原X轴 xAxisG.select("path").remove(); xAxisG.selectAll('line').remove(); //绘制新的立体X轴 xAxisG.append("path") .datum({ type: "Polygon", coordinates: [ [ [20, 0], [0, 15], [svgWidth - margin.right - margin.left, 15], [svgWidth + 20 - margin.right - margin.left, 0], [20, 0] ] ] }) .attr("d", path) .attr('fill', 'rgb(187,187,187)'); xAxisG.selectAll('text') .attr('font-size', '18px') .attr('fill', '#646464') .attr('transform', 'translate(0,20)'); dataProcessing(xLinearScale)//核心算法 }

你可能注意到了,上面代码中不仅使用了序数比例尺,还有地理路径生成器,因为需要生成立体的柱图,所以需要讲原本的X轴删除,自己重新进行绘制.下图是自己重新绘制出来的path路径:


创建Y轴线性比例尺

 var yLinearScale; //创建y轴的比例尺渲染y轴 function addYScale() { yLinearScale = d3.scaleLinear() .domain([0, d3.max(data, function (d, i) { return d.child.value * 1; }) * 1.2]) .range([svgHeight - margin.top - margin.bottom, 0]); //定义Y轴比例尺以及刻度 var yAxis = d3.axisLeft(yLinearScale) .ticks(6); //绘制Y轴 var yAxisG = svg.append("g") .call(yAxis) .attr('transform', 'translate(' + (margin.left + 10) + "," + margin.top + ")"); yAxisG.selectAll('text') .attr('font-size', '18px') .attr('fill', '#636363'); //删除原Y轴路径和tick yAxisG.select("path").remove(); yAxisG.selectAll('line').remove(); }

创建Y轴时同样需要把原来的路径和tick删除,下图是效果:

到这,我们的基础搭建完毕,下面就是核心算法

核心算法

为了实现最终效果,我希望大家在理解的时候能把整个立体柱图分解一下.

我实现立体柱图的思路是通过2个path路径和一个rect进行拼凑.

正面是一个rect,上面和右面利用path路径生成.

利用三角函数,通过给定的angle角度计算上面的一个点就可以知道其他所有点的位置进而进行绘制.

通过上图可以看到,一个立体柱图我们只需要知道7个点的位置就能够绘制出来.

并且已知正面rect4个红色点的位置.已知柱子的宽度和高度,那么只要求出Top面左上角点的位置,就可以知道余下绿色点的位置.具体算法如下:

 //核心算法思路是Big boss教的,我借花献佛 function dataProcessing(xLinearScale) { var angle = Math.PI / 2.3; for (var i = 0; i 

渲染

最终我们还要鼠标进行交互,所以先添加tip生成函数

 //tip的创建方法(方法来自敬爱的鸣哥) var tipTimerConfig = { longer: 0, target: null, exist: false, winEvent: window.event, boxHeight: 398, boxWidth: 376, maxWidth: 376, maxHeight: 398, tooltip: null, showTime: 3500, hoverTime: 300, displayText: "", show: function (val, e) { "use strict"; var me = this; if (e != null) { me.winEvent = e; } me.displayText = val; me.calculateBoxAndShow(); me.createTimer(); }, calculateBoxAndShow: function () { "use strict"; var me = this; var _x = 0; var _y = 0; var _w = document.documentElement.scrollWidth; var _h = document.documentElement.scrollHeight; var wScrollX = window.scrollX || document.body.scrollLeft; var wScrollY = window.scrollY || document.body.scrollTop; var xMouse = me.winEvent.x + wScrollX; if (_w - xMouse = 0; i--) { document.body.removeChild(delDiv[i]); } }, createTimer: function (delTarget) { "use strict"; var me = this; var delTip = me.tooltip; var delTarget = tipTimerConfig.target; var removeTimer = window.setTimeout(function () { try { if (delTip != null) { document.body.removeChild(delTip); if (tipTimerConfig.target == delTarget) { me.exist = false; } } clearTimeout(removeTimer); } catch (e) { clearTimeout(removeTimer); } }, me.showTime); }, hoverTimerFn: function (showTip, showTarget) { "use strict"; var me = this; var showTarget = tipTimerConfig.target; var hoverTimer = window.setInterval(function () { try { if (tipTimerConfig.target != showTarget) { clearInterval(hoverTimer); } else if (!tipTimerConfig.exist && (new Date()).getTime() - me.longer > me.hoverTime) { //show tipTimerConfig.show(showTip); tipTimerConfig.exist = true; clearInterval(hoverTimer); } } catch (e) { clearInterval(hoverTimer); } }, tipTimerConfig.hoverTime); } }; var createTooltipTableData = function (info) { var ary = []; ary.push("
"); ary.push("

品种信息:" + info.letter + "

"); ary.push("

成交量: " + info.child.value); ary.push("

"); return ary.join(""); };

核心算法写完,就到了最终的渲染了

 function addColumn() { function clumnMouseover(d) { d3.select(this).selectAll(".transparentPath").attr("opacity", 0.8); // 添加 div tipTimerConfig.target = this; tipTimerConfig.longer = new Date().getTime(); tipTimerConfig.exist = false; //获取坐标 tipTimerConfig.winEvent = { x: event.clientX - 100, y: event.clientY }; tipTimerConfig.boxHeight = 50; tipTimerConfig.boxWidth = 140; //hide tipTimerConfig.ClearDiv(); //show tipTimerConfig.hoverTimerFn(createTooltipTableData(d)); } function clumnMouseout(d) { d3.select(this).selectAll(".transparentPath").attr("opacity", 1); tipTimerConfig.target = null; tipTimerConfig.ClearDiv(); } var g = svg.selectAll('.g') .data(data) .enter() .append('g') .on("mouseover", clumnMouseover) .on("mouseout", clumnMouseout) .attr('transform', function (d) { return "translate(" + (d.ox + margin.left + 20) + "," + (svgHeight - margin.bottom + 15) + ")" }); g.transition() .duration(2500) .attr("transform", function (d) { return "translate(" + (d.ox + margin.left + 20) + ", " + (yLinearScale(d.child.value) + margin.bottom - 15) + ")" }); g.append('rect') .attr('x', 0) .attr('y', 0) .attr("class", "transparentPath") .attr('width', function (d, i) { return d.ow; }) .attr('height', function (d) { return d.oh; }) .style('fill', function (d, i) { return mainColorList[i] }) .transition() .duration(2500) .attr("height", function (d, i) { return svgHeight - margin.bottom - margin.top - yLinearScale(d.child.value); }); g.append('path') .attr("class", "transparentPath") .attr('d', function (d) { return "M0,0 L" + d.p1.x + "," + d.p1.y + " L" + d.p2.x + "," + d.p2.y + " L" + d.ow + ",0 L0,0"; }) .style('fill', function (d, i) { return topColorList[i] }); g.append('path') .attr("class", "transparentPath") .attr('d', function (d) { return "M" + d.ow + ",0 L" + d.p2.x + "," + d.p2.y + " L" + d.p3.x + "," + d.p3.y + " L" + d.ow + "," + d.oh + " L" + d.ow + ",0" }) .style('fill', function (d, i) { return rightColorList[i] }) .transition() .duration(2500) .attr("d", function (d, i) { return "M" + d.ow + ",0 L" + d.p2.x + "," + d.p2.y + " L" + d.p3.x + "," + (d.p3.y + svgHeight - margin.top - margin.bottom - yLinearScale(d.child.value)) + " L" + d.ow + "," + (svgHeight - margin.top - margin.bottom - yLinearScale(d.child.value)) + " L" + d.ow + ",0" }); }

由于需要考虑动画,所以对渲染时的柱子位置进行了处理.对这方面不理解的话可以留言讨论.

总结


以上就是d3.js实现立体柱图的方法详解的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » JavaScript 教程