本文參照了echarts羅列了幾種經常使用的柱狀圖在d3中的畫法html
d3的全部佈局都不會幫你做圖,只是幫你按需處理的數據。數組
使用d3.randomBates
構造一組1000個具備貝茨分佈規律而且在1~0的隨機數,使用d3.histogram()
直方圖佈局處理數據。統計數據在每一個x軸頻段出現的頻率。而後做出柱狀圖。app
處理後的數據echarts
x0、x1
:起始及終止的x座標,length
:出現的頻數,數組裏其餘的即爲在此頻段的數據。dom
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> </title> </head> <body> <div id="test-svg"> </div> </body> <script src="https://d3js.org/d3.v5.js"> </script> <script> window.onload = function() { //建立具備貝茨分佈規律的隨機數,0~1 var datas = d3.range(1000).map(function() { return d3.randomBates(10)(); }); var width = 800, height = 400, padding = { top: 10, right: 40, bottom: 40, left: 40 }; var svg = d3.select("#test-svg") .append('svg') .attr('width', width + 'px') .attr('height', height + 'px'); // x軸 var xScale = d3.scaleLinear() .range([padding.left, width - padding.right]); // 將x軸,0~1,軸分紅20個刻度 [0,0.05,0.1,0.15 ...,1.00] var xAxis = d3.axisBottom() .scale(xScale) .ticks(20); svg.append('g') .call(xAxis) .attr("transform", "translate(0," + (height - padding.bottom) + ")"); // 構造一個直方圖佈局,返回隨機數在每一個x軸刻度區域出現的次數 var his = d3.histogram() .domain(xScale.domain()) .thresholds(xScale.ticks(20)) (datas); // y軸 var yScale = d3.scaleLinear() .domain([0, d3.max(his, function(d) { return d.length; })]) .range([height - padding.bottom, padding.top]); var yAxis = d3.axisLeft() .scale(yScale) .ticks(10); svg.append('g') .call(yAxis) .attr("transform", "translate(" + padding.left + ",0)"); var bar = svg.selectAll(".bar") .data(his) .join("g") .attr("class", "bar") .attr("transform", function(d) { return "translate(" + xScale(d.x0) + "," + yScale(d.length) + ")"; }); // 構造柱 bar.append("rect") .attr("x", 1) .attr("width", xScale(his[0].x1) - xScale(his[0].x0) - 1) .attr("height", function(d) { return height - yScale(d.length) - padding.bottom; }); bar.append("text") .attr("dy", ".75em") .attr("y", 6) .attr("x", (xScale(his[0].x1) - xScale(his[0].x0)) / 2) .attr("text-anchor", "middle") .attr("font-size", "8px") .attr("fill", "White") .text(function(d) { return d.length; }); } </script> </html>
Bar Simple
的原始數據,和上述相似直接處理數據做圖。<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="test-svg"> </div> </body> <script src="https://d3js.org/d3.v5.js"></script> <script> window.onload = function() { // 原始數據 var datax = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; var datay = [120, 200, 150, 80, 70, 110, 130]; var width = 800, height = 400, padding = { top: 10, right: 40, bottom: 40, left: 40 }; var svg = d3.select("#test-svg") .append('svg') .attr('width', width + 'px') .attr('height', height + 'px'); // x軸 var xScale = d3.scaleOrdinal() .domain(datax) .range([100, 200, 300, 400, 500, 600, 700]); var xAxis = d3.axisBottom() .scale(xScale); svg.append('g') .call(xAxis) .attr("transform", "translate(0," + (height - padding.bottom) + ")") .selectAll("text") .attr("dx", "50px"); // y軸 var yScale = d3.scaleLinear() .domain([0, d3.max(datay)]) .range([height - padding.bottom, padding.top]); var yAxis = d3.axisLeft() .scale(yScale) .ticks(10); svg.append('g') .call(yAxis) .attr("transform", "translate(" + 100 + ",0)"); var bar = svg.selectAll(".bar") .data(datay) .enter().append("g") .attr("class", "bar") .attr("transform", function(d, i) { return "translate(" + xScale(i * 100) + "," + yScale(d) + ")"; }); bar.append("rect") .attr("x", 1) .attr("width", 100) .attr("height", function(d) { return height - yScale(d) - padding.bottom; }) .attr("stroke", "White"); bar.append("text") .attr("dy", ".75em") .attr("y", 6) .attr("x", 50) .attr("text-anchor", "middle") .attr("font-size", "8px") .attr("fill", "White") .text(function(d) { return d; }); } </script> </html>
在原來的基礎上仿照echarts 添加了圖例和對應的點擊事件。svg
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="test-svg"> </div> </body> <script src="https://d3js.org/d3.v5.js"></script> <script> window.onload = function() { var datax = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; var datay = [ [120, 200, 150, 80, 70, 110, 130], [100, 100, 130, 70, 10, 120, 135], [160, 120, 120, 90, 20, 140, 130] ]; // 用來保存點擊時的狀態 var legendStatus = [true, true, true]; var width = 800, height = 400, padding = { top: 40, right: 40, bottom: 40, left: 40 }; var sp = d3.schemeSet2; var svg = d3.select("#test-svg") .append('svg') .attr('width', width + 'px') .attr('height', height + 'px'); // x軸 var xScale = d3.scaleOrdinal() .domain(datax) .range([100, 200, 300, 400, 500, 600, 700]); var xAxis = d3.axisBottom() .scale(xScale) .tickSize(10); svg.append('g') .call(xAxis) .attr("transform", "translate(0," + (height - padding.bottom) + ")") .selectAll("text") .attr("dx", "50px"); // y軸 var yScale = d3.scaleLinear() .domain([0, d3.max(d3.merge([datay[0], datay[1], datay[2]]))]) .range([height - padding.bottom, padding.top]); var yAxis = d3.axisLeft() .scale(yScale) .ticks(10); svg.append('g') .call(yAxis) .attr("transform", "translate(" + 100 + ",0)"); // 這裏使用了forEach 考慮到計算柱子偏移量可能會方便一點 也能夠直接 /** * * var container = svg.selectAll(".container") * .data(datay) * .join("g") * .attr("class", ".container"); * */ datay.forEach(function(item, index) { var bar = svg.selectAll(".bar" + index) .data(item) .enter().append("g") .attr("class", "bar" + index) .attr("transform", function(d, i) { var _d = (100 / datay.length) * (index); return "translate(" + (xScale(i * 100) + _d) + "," + yScale(d) + ")"; }); // 柱 bar.append("rect") .attr("x", 1) .attr("width", (100 / datay.length)) .attr("height", function(d) { return height - yScale(d) - padding.bottom; }) .attr("stroke", "White") .attr("fill", sp[index]); bar.append("text") .attr("dy", ".75em") .attr("y", 6) .attr("x", 100 / (datay.length * 2)) .attr("text-anchor", "middle") .attr("font-size", "8px") .attr("fill", "White") .text(function(d) { return d; }); // 圖例 var legend = svg.append('g'); var line = legend.append('line') .attr('x1', 0) .attr('y1', 2) .attr('x2', 15) .attr('y2', 2) .attr('stroke', sp[index]) .attr('stroke-width', 5); var text = legend.append('text') .attr('class', 'legend-label') .attr("dy", -13) .style("text-anchor", "start") .text("data" + index) .attr('fill', "Black") .attr('font-size', '13') .attr("transform", "translate(" + 18 + "," + 20 + ")"); // 圖例對應的點擊事件 legend.attr("transform", "translate(" + (padding.left * 3 + index * 100) + "," + padding.top / 2 + ")") .on("click", function() { var _this = d3.select(this); var _i = parseInt(_this.select("text").text().split("data")[1]); if(legendStatus[_i]) { _this.selectAll("line").attr("stroke", "#d3d3d3"); _this.selectAll("text").attr("fill", "#d3d3d3"); svg.selectAll(".bar" + _i) .attr("display", "none"); } else { _this.selectAll("line").attr("stroke", sp[_i]); _this.selectAll("text").attr("fill", "#Black"); svg.selectAll(".bar" + _i) .attr("display", "show"); } legendStatus[_i] = !legendStatus[_i]; }); }); } </script> </html>
d3.stack()
堆疊佈局來處理原始數據,而後做圖。<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> </title> </head> <body> <div id="test-svg"> </div> </body> <script src="https://d3js.org/d3.v5.js"> </script> <script> window.onload = function() { var data = [{ apples: 3840, bananas: 1920, cherries: 960, dates: 400 }, { apples: 1600, bananas: 1440, cherries: 960, dates: 400 }, { apples: 640, bananas: 960, cherries: 640, dates: 400 }, { apples: 320, bananas: 480, cherries: 640, dates: 400 }]; var width = 800, height = 400, padding = { top: 40, right: 40, bottom: 40, left: 40 }; var colors = d3.schemeSet2; var svg = d3.select("#test-svg") .append('svg') .attr('width', width + 'px') .attr('height', height + 'px'); // 使用堆疊佈局處理數據 var stack = d3.stack() .keys(["apples", "bananas", "cherries", "dates"]) .order(d3.stackOrderNone) .offset(d3.stackOffsetNone); var series = stack(data); // x軸 var xScale = d3.scaleOrdinal() .domain(series.map(function(d) { return d.key; })) .range([100, 200, 300, 400]); var xAxis = d3.axisBottom() .scale(xScale) .tickSize(10); svg.append('g') .call(xAxis) .attr("transform", "translate(0," + (height - padding.bottom) + ")") .selectAll("text") .attr("font-size", "10px") .attr("dx", "50px"); // 求出y軸最大值 var maxNum = d3.max(series, function(d) { return d3.max(d, function(dd) { return dd[1]; }) }); // y軸 var yScale = d3.scaleLinear() .domain([0, maxNum]) .range([height - padding.bottom, padding.top]); var yAxis = d3.axisLeft() .scale(yScale) .ticks(10); svg.append('g') .call(yAxis) .attr("transform", "translate(" + 100 + ",0)"); // 畫柱 var bar = svg.selectAll(".bar") .data(series) .join("g") .attr("class", "bar") .selectAll("rect") .data(function(d) { return d; }); bar.join("rect") .attr("x", 1) .attr("width", 100) .attr("height", function(d, i) { return height - yScale(d[1] - d[0]) - padding.bottom; }) .attr("transform", function(d, i) { return "translate(" + xScale(i * 100) + "," + yScale(d[1]) + ")"; }) .attr("stroke", "White") .attr("fill", function(d, i) { return colors[i]; }); bar.join("text") .attr("dy", ".75em") .attr("y", 6) .attr("x", 50) .attr("text-anchor", "middle") .attr("font-size", "8px") .attr("fill", "White") .text(function(d) { return d[1] - d[0]; }) .attr("transform", function(d, i) { return "translate(" + xScale(i * 100) + "," + yScale(d[1]) + ")"; }); } </script> </html>