最终效果图镇楼:
本文通过三个步骤来介绍d3.js。
1、简单的柱状图;
2、多个x轴的柱状图;
3、多个x轴、y轴的柱状图;
学习心得:
d3.js入门相对比较困难,一旦掌握了核心思想,不断熟悉API,就可以做出很灵活、实用的图表。
canvas中,d3帮我们计算好了每个图形的位置,我们再一个一个的画上即可。
不要担心代码看起来很多,一个一个的分析出来,就会发现其实还是有套路的。
一、简单图表
示意图:
代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <canvas id="myCanvas" width="600" height="400"></canvas> <script src="d3.min.js"></script> <script> "use strict"; (function () { function init() { initData(); } function initData() {var oneData = [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 20}, {'name': '技术', 'value': 60} ]; initUI(oneData); } function initUI(data) { var canvas = document.querySelector('#myCanvas'); var context = canvas.getContext('2d'); var margin = {top: 20, right: 20, bottom: 30, left: 40}; var width = canvas.width - margin.left - margin.right; var height = canvas.height - margin.top - margin.bottom; var x0 = d3.scaleBand() .domain(data.map(function (d) { return d.name; })) .rangeRound([0, width]) .padding(0.5); var y0 = d3.scaleLinear() .domain([0, d3.max(data, function (d) { return d.value; })]) .range([height, 0]); context.translate(margin.left, margin.top); context.beginPath(); x0.domain().forEach(function (d) { context.moveTo(x0(d) + x0.bandwidth() / 2, height); context.lineTo(x0(d) + x0.bandwidth() / 2, height + 6); }); context.strokeStyle = 'black'; context.stroke(); context.textAlign = "center"; context.textBaseline = "top"; x0.domain().forEach(function (d) { context.fillText(d, x0(d) + x0.bandwidth() / 2, height + 6); }); context.beginPath(); context.moveTo(0.5, height + 0.5); context.lineTo(width + 0.5, height + 0.5); context.strokeStyle = "black"; context.stroke(); var yTickCount = 10; var yTicks = y0.ticks(yTickCount); context.beginPath(); yTicks.forEach(function (d) { context.moveTo(0, y0(d) + 0.5); context.lineTo(-6, y0(d) + 0 / 5); }); context.strokeStyle = 'black'; context.stroke(); context.textAlign = "right"; context.textBaseline = "middle"; yTicks.forEach(function (d) { context.fillText(d, -9, y0(d)); }); context.beginPath(); context.moveTo(-6.5, 0 + 0.5); context.lineTo(0.5, 0 + 0.5); context.lineTo(0.5, height + 0.5); context.lineTo(-6.5, height + 0.5); context.strokeStyle = "black"; context.stroke(); context.fillStyle = "steelblue"; data.forEach(function (d) { context.fillRect(x0(d.name), y0(d.value), x0.bandwidth(), height - y0(d.value)); }); } init(); }()); </script> </body> </html>
二、多个x轴
示意图:
代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <canvas id="myCanvas" width="600" height="400"></canvas> <script src="d3.min.js"></script> <script> "use strict"; (function () { function init() { initData(); } function initData() {var data = [ { 'name': '东北', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 20}, {'name': '技术', 'value': 60} ] }, { 'name': '华北', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 80}, {'name': '技术', 'value': 60} ] }, { 'name': '西南', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 80}, {'name': '技术', 'value': 60} ] } ]; initUI(data); } function initUI(data) { var canvas = document.querySelector('#myCanvas'); var context = canvas.getContext('2d'); var margin = {top: 20, right: 20, bottom: 45, left: 40}; var width = canvas.width - margin.left - margin.right; var height = canvas.height - margin.top - margin.bottom; var xAxis0 = d3.scaleBand() .domain(data.map(function (d) { return d.name; })) .rangeRound([0, width]); var firstRow = data[0]; var xAxis1 = d3.scaleBand() .domain(firstRow.value.map(function (d) { return d.name; })) .rangeRound([0, xAxis0.bandwidth()]) .padding(0.5); var yAxis0 = d3.scaleLinear() .domain([0, d3.max(data, function (d0) { return d3.max(d0.value, function (d1) { return d1.value; }) })]) .range([height, 0]); context.translate(margin.left, margin.top); context.beginPath(); context.textAlign = "center"; context.textBaseline = "top"; xAxis0.domain().forEach(function (d) { context.fillText(d, xAxis0(d) + xAxis0.bandwidth() / 2, height + 23); }); data.forEach(function (d0) { var pX0 = xAxis0(d0.name); d0.value.forEach(function (d1) { context.moveTo(pX0 + xAxis1(d1.name) + xAxis1.bandwidth() / 2, height); context.lineTo(pX0 + xAxis1(d1.name) + xAxis1.bandwidth() / 2, height + 6); context.fillText(d1.name, pX0 + xAxis1(d1.name) + xAxis1.bandwidth() / 2, height + 6); }); }); context.strokeStyle = 'black'; context.stroke(); context.beginPath(); context.moveTo(0.5, height + 0.5); context.lineTo(width + 0.5, height + 0.5); context.strokeStyle = "black"; context.stroke(); var yTickCount = 10; var yTicks = yAxis0.ticks(yTickCount); context.beginPath(); yTicks.forEach(function (d) { context.moveTo(0, yAxis0(d) + 0.5); context.lineTo(-6, yAxis0(d) + 0 / 5); }); context.strokeStyle = 'black'; context.stroke(); context.textAlign = "right"; context.textBaseline = "middle"; yTicks.forEach(function (d) { context.fillText(d, -9, yAxis0(d)); }); context.beginPath(); context.moveTo(-6.5, 0 + 0.5); context.lineTo(0.5, 0 + 0.5); context.lineTo(0.5, height + 0.5); context.lineTo(-6.5, height + 0.5); context.strokeStyle = "black"; context.stroke(); context.fillStyle = "steelblue"; data.forEach(function (d0) { var pX0 = xAxis0(d0.name); d0.value.forEach(function (d1) { var pX1 = pX0 + xAxis1(d1.name); var pY1 = yAxis0(d1.value); var pWidth = xAxis1.bandwidth(); var pHeight = height - yAxis0(d1.value); context.fillRect(pX1, pY1, pWidth, pHeight); }) }); } init(); }()); </script> </body> </html>
三、多个x、y轴
示意图:
代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <canvas id="myCanvas" width="600" height="400"></canvas> <script src="d3.min.js"></script> <script> "use strict"; (function () { function init() { initData(); } function initData() {var data = [ { 'name': '利润', 'value': [ { 'name': '东北', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 50}, {'name': '技术', 'value': 60} ] }, { 'name': '华北', 'value': [ {'name': '办公用品', 'value': 90}, {'name': '家具', 'value': 70}, {'name': '技术', 'value': 60} ] }, { 'name': '西南', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 80}, {'name': '技术', 'value': 50} ] } ] }, { 'name': '销售额', 'value': [ { 'name': '东北', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 20}, {'name': '技术', 'value': 60} ] }, { 'name': '华北', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 80}, {'name': '技术', 'value': 60} ] }, { 'name': '西南', 'value': [ {'name': '办公用品', 'value': 100}, {'name': '家具', 'value': 80}, {'name': '技术', 'value': 60} ] } ] } ]; initUI(data); } function initUI(data) { var canvas = document.querySelector('#myCanvas'); var context = canvas.getContext('2d'); var margin = {top: 20, right: 20, bottom: 45, left: 40}; var width = canvas.width - margin.left - margin.right; var height = canvas.height - margin.top - margin.bottom; var firstRow = data[0].value; var firstFirstRow = firstRow[0].value; var xAxis0 = d3.scaleBand() .domain(firstRow.map(function (d) { return d.name; })) .rangeRound([0, width]); var xAxis1 = d3.scaleBand() .domain(firstFirstRow.map(function (d) { return d.name; })) .rangeRound([0, xAxis0.bandwidth()]) .padding(0.5); var yAxis0 = d3.scaleBand() .domain(data.map(function (d) { return d.name; })) .range([height, 0]) .paddingInner(0.1); var yAxis1 = d3.scaleLinear() .domain([0, d3.max(data, function (d0) { return d3.max(d0.value, function (d1) { return d3.max(d1.value, function (d2) { return d2.value; }) }) })]) .range([yAxis0.bandwidth(), 0]); context.translate(margin.left, margin.top); context.beginPath(); context.textAlign = "center"; context.textBaseline = "top"; xAxis0.domain().forEach(function (d) { context.fillText(d, xAxis0(d) + xAxis0.bandwidth() / 2, height + 23); }); firstRow.forEach(function (xData0) { var pX0 = xAxis0(xData0.name); xData0.value.forEach(function (xData1) { context.moveTo(pX0 + xAxis1(xData1.name) + xAxis1.bandwidth() / 2, height); context.lineTo(pX0 + xAxis1(xData1.name) + xAxis1.bandwidth() / 2, height + 6); context.fillText(xData1.name, pX0 + xAxis1(xData1.name) + xAxis1.bandwidth() / 2, height + 6); }) }); context.strokeStyle = 'black'; context.stroke(); context.beginPath(); data.forEach(function (yData0) { var pY0 = yAxis0(yData0.name); var y = +(pY0 + yAxis0.bandwidth()).toFixed(0) + 0.5; context.moveTo(0.5, y); context.lineTo(width + 0.5, y); }); context.strokeStyle = "black"; context.stroke(); var yTickCount = 10; var yTicks = yAxis1.ticks(yTickCount); context.beginPath(); data.forEach(function (yData0) { var pY0 = yAxis0(yData0.name); for (var idx = 0; idx < yData0.name.length; idx++) { context.fillText(yData0.name[idx], -30, pY0 + yAxis0.bandwidth() / 2 - 15 + idx * 15); } yTicks.forEach(function (d) { var y = +(pY0 + yAxis1(d)).toFixed(0) + 0.5; context.moveTo(0, y); context.lineTo(-6, y); }); }); context.strokeStyle = 'black'; context.stroke(); context.textAlign = "right"; context.textBaseline = "middle"; data.forEach(function (yData0) { var pY0 = yAxis0(yData0.name); yTicks.forEach(function (d) { var y = pY0 + yAxis1(d); context.fillText(d, -9, y); }); }); context.beginPath(); context.moveTo(-6.5, 0 + 0.5); context.lineTo(0.5, 0 + 0.5); context.lineTo(0.5, height + 0.5); context.lineTo(-6.5, height + 0.5); context.strokeStyle = "black"; context.stroke(); context.fillStyle = "steelblue"; data.forEach(function (yData0) { var pY0 = yAxis0(yData0.name); yData0.value.forEach(function (xData0) { var pX0 = xAxis0(xData0.name); xData0.value.forEach(function (xData1) { var pX1 = pX0 + xAxis1(xData1.name); var pY1 = pY0 + yAxis1(xData1.value); var pWidth = xAxis1.bandwidth(); var pHeight = yAxis0.bandwidth() - yAxis1(xData1.value); context.fillRect(pX1, pY1, pWidth, pHeight); }) }) }); } init(); }()); </script> </body> </html>
源码下载:src