国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

d3.js實現(xiàn)立體柱圖的方法詳解

2019-11-19 16:42:29
字體:
供稿:網(wǎng)友

前言

眾所周知隨著大數(shù)據(jù)時代的來臨,數(shù)據(jù)可視化的重要性也越來越凸顯,那么今天就基于d3.js今天給大家?guī)砜梢暬A(chǔ)圖表柱圖進階:立體柱圖,之前介紹過了d3.js實現(xiàn)柱狀圖的文章,感興趣的朋友們可以看一看。

關(guān)于d3.js

d3.js是一個操作svg的圖表庫,d3封裝了圖表的各種算法.對d3不熟悉的朋友可以到d3.js官網(wǎng)學(xué)習(xí)d3.js.

另外感謝司機大傻(聲音像張學(xué)友一樣性感的一流裝逼手)和司機呆(呆萌女神)等人對d3.js進行翻譯!

HTML+CSS

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <style> * { margin: 0; padding: 0; } div.tip-hill-div { background: rgba(0, 0, 0, 0.7); color: #fff; padding: 10px; border-radius: 5px; font-family: Microsoft Yahei; } div.tip-hill-div > h1 { font-size: 14px; } div.tip-hill-div > h2 { font-size: 12px; } </style></head><body><div id="chart"></div></body></html>

JS

當(dāng)前使用d3.v4+版本

<script src="d3-4.js"></script>

圖表所需數(shù)據(jù)

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" } }];

圖表的一些基礎(chǔ)配置數(shù)據(jù)

var margin = { top: 20, right: 50, bottom: 50, left: 90 };var svgWidth = 1000;var svgHeight = 500;//創(chuàng)建各個面的顏色數(shù)組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');

創(chuàng)建X軸序數(shù)比例尺

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)//核心算法 }

你可能注意到了,上面代碼中不僅使用了序數(shù)比例尺,還有地理路徑生成器,因為需要生成立體的柱圖,所以需要講原本的X軸刪除,自己重新進行繪制.下圖是自己重新繪制出來的path路徑:


創(chuàng)建Y軸線性比例尺

var yLinearScale; //創(chuàng)建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(); }

創(chuàng)建Y軸時同樣需要把原來的路徑和tick刪除,下圖是效果:

到這,我們的基礎(chǔ)搭建完畢,下面就是核心算法

核心算法

為了實現(xiàn)最終效果,我希望大家在理解的時候能把整個立體柱圖分解一下.

我實現(xiàn)立體柱圖的思路是通過2個path路徑和一個rect進行拼湊.

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

利用三角函數(shù),通過給定的angle角度計算上面的一個點就可以知道其他所有點的位置進而進行繪制.

通過上圖可以看到,一個立體柱圖我們只需要知道7個點的位置就能夠繪制出來.

并且已知正面rect4個紅色點的位置.已知柱子的寬度和高度,那么只要求出Top面左上角點的位置,就可以知道余下綠色點的位置.具體算法如下:

//核心算法思路是Big boss教的,我借花獻(xiàn)佛function dataProcessing(xLinearScale) { var angle = Math.PI / 2.3; for (var i = 0; i < data.length; i++) {  var d = data[i];  var depth = 10;   d.ow = xLinearScale.bandwidth() * 0.7;  d.ox = xLinearScale(d.letter);  d.oh = 1;  d.p1 = {  x: Math.cos(angle) * d.ow,  y: -Math.sin(angle) - depth  };  d.p2 = {  x: d.p1.x + d.ow,  y: d.p1.y  };  d.p3 = {  x: d.p2.x,  y: d.p2.y + d.oh  }; } }

渲染

最終我們還要鼠標(biāo)進行交互,所以先添加tip生成函數(shù)

//tip的創(chuàng)建方法(方法來自敬愛的鳴哥) 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 < me.boxWidth) {  _x = xMouse - me.boxWidth - 10;  } else {  _x = xMouse;  }  var _yMouse = me.winEvent.y + wScrollY;  if (_h - _yMouse < me.boxHeight + 18) {  _y = _yMouse - me.boxHeight - 25;  } else {  _y = _yMouse + 18;  }  me.addTooltip(_x, _y); }, addTooltip: function (page_x, page_y) {  "use strict";  var me = this;  me.tooltip = document.createElement("div");  me.tooltip.style.left = page_x + "px";  me.tooltip.style.top = page_y + "px";  me.tooltip.style.position = "absolute";  me.tooltip.style.width = me.boxWidth + "px";  me.tooltip.style.height = me.boxHeight + "px";  me.tooltip.className = "three-tooltip";  var divInnerHeader = me.createInner();  divInnerHeader.innerHTML = me.displayText;  me.tooltip.appendChild(divInnerHeader);  document.body.appendChild(me.tooltip); }, createInner: function () {  "use strict";  var me = this;  var divInnerHeader = document.createElement('div');  divInnerHeader.style.width = me.boxWidth + "px";  divInnerHeader.style.height = me.boxHeight + "px";  return divInnerHeader; }, ClearDiv: function () {  "use strict";  var delDiv = document.body.getElementsByClassName("three-tooltip");  for (var i = delDiv.length - 1; i >= 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("<div class='tip-hill-div'>"); ary.push("<h1>品種信息:" + info.letter + "</h1>"); ary.push("<h2>成交量: " + info.child.value); ary.push("</div>"); 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;  //獲取坐標(biāo)  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"  }); }

由于需要考慮動畫,所以對渲染時的柱子位置進行了處理.對這方面不理解的話可以留言討論.

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對武林網(wǎng)的支持。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 监利县| 天长市| 吴旗县| 宾川县| 南康市| 孟村| 武安市| 芦山县| 新昌县| 饶阳县| 漠河县| 临颍县| 保康县| 泉州市| 山东| 沈丘县| 石家庄市| 安乡县| 鄂温| 富川| 五大连池市| 于田县| 孟州市| 宝清县| 芜湖县| 读书| 双鸭山市| 滨海县| 青浦区| 巴青县| 德庆县| 濮阳市| 中宁县| 兴安县| 镇平县| 光山县| 安庆市| 密山市| 和静县| 北流市| 西乌珠穆沁旗|