首页前端开发其他前端知识d3.js绘制立体柱图的操作步骤是什么

d3.js绘制立体柱图的操作步骤是什么

时间2024-03-28 15:54:03发布访客分类其他前端知识浏览901
导读:这篇文章给大家介绍了“d3.js绘制立体柱图的操作步骤是什么”的相关知识,讲解详细,步骤过程清晰,对大家进一步学习和理解“d3.js绘制立体柱图的操作步骤是什么”有一定的帮助,希望大家阅读完这篇文章能有所收获。下面就请大家跟着小编的思路一起...
这篇文章给大家介绍了“d3.js绘制立体柱图的操作步骤是什么”的相关知识,讲解详细,步骤过程清晰,对大家进一步学习和理解“d3.js绘制立体柱图的操作步骤是什么”有一定的帮助,希望大家阅读完这篇文章能有所收获。下面就请大家跟着小编的思路一起来学习一下吧。


关于d3.js

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

另外感谢司机大傻(声音像张学友一样性感的一流装逼手)和司机呆(呆萌女神)等人对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

当前使用d3.v4+版本

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

图表所需数据

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

 }

 }

渲染

最终我们还要鼠标进行交互,所以先添加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  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;

  //获取坐标
  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绘制立体柱图的操作步骤是什么”的文章就介绍到这了,感谢各位的阅读,更多相关d3.js绘制立体柱图的操作步骤是什么内容,欢迎关注网络资讯频道,小编将为大家输出更多高质量的实用文章!

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

柱形图

若转载请注明出处: d3.js绘制立体柱图的操作步骤是什么
本文地址: https://pptw.com/jishu/655042.html
Java封装和继承不理解?两个案例帮到你 react native怎么上传图片到服务器

游客 回复需填写必要信息