2017-03-11 33 views
1

在我的d3条形图中,我应该有Y轴动态刻度和网格线作为整数值(十进制值没有刻度线和网格线)。此外,在最高的酒吧顶部应该有刻度线和网格线。d3条形图y轴刻度线和最大值以上的网格线

在我的示例图中,我将最大值传递给y轴域函数,我如何确保再多一行,并在最大值未达到下一个范围时打勾。

Fiddle One这里第二杆具有53总价值,所以我期待在55

Fiddle Two对于相同的图表一个更蜱和线,如果数据低于10,它带有在小数线像2.5,3.5等点。如何排除这些线。

这里的滴答是根据价值计算的。

// Setup svg using Bostock's margin convention 

var margin = {top: 20, right: 160, bottom: 35, left: 30}; 

var width = 960 - margin.left - margin.right, 
    height = 500 - margin.top - margin.bottom; 

var svg = d3.select("body") 
    .append("svg") 
    .attr("width", width + margin.left + margin.right) 
    .attr("height", height + margin.top + margin.bottom) 
    .append("g") 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 


/* Data in strings like it would be if imported from a csv */ 

var data = [ 
    { year: "2006", redDelicious: "10", mcintosh: "15", oranges: "9", pears: "6" }, 
    { year: "2007", redDelicious: "12", mcintosh: "18", oranges: "9", pears: "14" }, 
    { year: "2008", redDelicious: "05", mcintosh: "20", oranges: "8", pears: "2" }, 
    { year: "2009", redDelicious: "01", mcintosh: "15", oranges: "5", pears: "4" }, 
    { year: "2010", redDelicious: "02", mcintosh: "10", oranges: "4", pears: "2" }, 
    { year: "2011", redDelicious: "03", mcintosh: "12", oranges: "6", pears: "3" }, 
    { year: "2012", redDelicious: "04", mcintosh: "15", oranges: "8", pears: "1" }, 
    { year: "2013", redDelicious: "06", mcintosh: "11", oranges: "9", pears: "4" }, 
    { year: "2014", redDelicious: "10", mcintosh: "13", oranges: "9", pears: "5" }, 
    { year: "2015", redDelicious: "16", mcintosh: "19", oranges: "6", pears: "9" }, 
    { year: "2016", redDelicious: "19", mcintosh: "17", oranges: "5", pears: "7" }, 
]; 

var parse = d3.time.format("%Y").parse; 


// Transpose the data into layers 
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) { 
    return data.map(function(d) { 
    return {x: parse(d.year), y: +d[fruit]}; 
    }); 
})); 


// Set x, y and colors 
var x = d3.scale.ordinal() 
    .domain(dataset[0].map(function(d) { return d.x; })) 
    .rangeRoundBands([10, width-10], 0.02); 

var y = d3.scale.linear() 
    .domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; }); })]) 
    .range([height, 0]); 

var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"]; 


// Define and draw axes 
var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient("left") 
    .tickSize(-width, 0, 0) 
    .tickFormat(d3.format("d")); 

var xAxis = d3.svg.axis() 
    .scale(x) 
    .orient("bottom") 
    .tickFormat(d3.time.format("%Y")); 

svg.append("g") 
    .attr("class", "y axis") 
    .call(yAxis); 

svg.append("g") 
    .attr("class", "x axis") 
    .attr("transform", "translate(0," + height + ")") 
    .call(xAxis); 


// Create groups for each series, rects for each segment 
var groups = svg.selectAll("g.cost") 
    .data(dataset) 
    .enter().append("g") 
    .attr("class", "cost") 
    .style("fill", function(d, i) { return colors[i]; }); 

var rect = groups.selectAll("rect") 
    .data(function(d) { return d; }) 
    .enter() 
    .append("rect") 
    .attr("x", function(d) { return x(d.x); }) 
    .attr("y", function(d) { return y(d.y0 + d.y); }) 
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); }) 
    .attr("width", x.rangeBand()) 
    .on("mouseover", function() { tooltip.style("display", null); }) 
    .on("mouseout", function() { tooltip.style("display", "none"); }) 
    .on("mousemove", function(d) { 
    var xPosition = d3.mouse(this)[0] - 15; 
    var yPosition = d3.mouse(this)[1] - 25; 
    tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")"); 
    tooltip.select("text").text(d.y); 
    }); 


// Draw legend 
var legend = svg.selectAll(".legend") 
    .data(colors) 
    .enter().append("g") 
    .attr("class", "legend") 
    .attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; }); 

legend.append("rect") 
    .attr("x", width - 18) 
    .attr("width", 18) 
    .attr("height", 18) 
    .style("fill", function(d, i) {return colors.slice().reverse()[i];}); 

legend.append("text") 
    .attr("x", width + 5) 
    .attr("y", 9) 
    .attr("dy", ".35em") 
    .style("text-anchor", "start") 
    .text(function(d, i) { 
    switch (i) { 
     case 0: return "Anjou pears"; 
     case 1: return "Naval oranges"; 
     case 2: return "McIntosh apples"; 
     case 3: return "Red Delicious apples"; 
    } 
    }); 


// Prep the tooltip bits, initial display is hidden 
var tooltip = svg.append("g") 
    .attr("class", "tooltip") 
    .style("display", "none"); 

tooltip.append("rect") 
    .attr("width", 30) 
    .attr("height", 20) 
    .attr("fill", "white") 
    .style("opacity", 0.5); 

tooltip.append("text") 
    .attr("x", 15) 
    .attr("dy", "1.2em") 
    .style("text-anchor", "middle") 
    .attr("font-size", "12px") 
    .attr("font-weight", "bold"); 

回答

2

问题1

你可能不得不使用tickValues通过自己定制的蜱。如果你是一般满意d3是怎么做的,你可以添加额外的检查,以延长数组:

var ticks = y.ticks(), 
    lastTick = ticks[ticks.length-1], 
    newLastTick = lastTick + (ticks[1] - ticks[0]); 
if (lastTick<y.domain()[1]){ 
    ticks.push(newLastTick); 
} 
y.domain([y.domain()[0], newLastTick]); //<-- adjust domain for further value 

// Define and draw axes 
var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient("left") 
    .tickSize(-width, 0, 0) 
    .tickFormat(function(d) { return d }) 
    .tickValues(ticks); 

运行代码:

// Setup svg using Bostock's margin convention 
 

 
var margin = {top: 15, right: 160, bottom: 35, left: 30}; 
 

 
var width = 960 - margin.left - margin.right, 
 
    height = 500 - margin.top - margin.bottom; 
 

 
var svg = d3.select("body") 
 
    .append("svg") 
 
    .attr("width", width + margin.left + margin.right) 
 
    .attr("height", height + margin.top + margin.bottom) 
 
    .append("g") 
 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 
 

 

 
/* Data in strings like it would be if imported from a csv */ 
 

 
var data = [ 
 
    { year: "2006", redDelicious: "10", mcintosh: "15", oranges: "9", pears: "6" }, 
 
    { year: "2007", redDelicious: "12", mcintosh: "18", oranges: "9", pears: "14" }, 
 
    { year: "2008", redDelicious: "05", mcintosh: "20", oranges: "8", pears: "2" }, 
 
    { year: "2009", redDelicious: "01", mcintosh: "15", oranges: "5", pears: "4" }, 
 
    { year: "2010", redDelicious: "02", mcintosh: "10", oranges: "4", pears: "2" }, 
 
    { year: "2011", redDelicious: "03", mcintosh: "12", oranges: "6", pears: "3" }, 
 
    { year: "2012", redDelicious: "04", mcintosh: "15", oranges: "8", pears: "1" }, 
 
    { year: "2013", redDelicious: "06", mcintosh: "11", oranges: "9", pears: "4" }, 
 
    { year: "2014", redDelicious: "10", mcintosh: "13", oranges: "9", pears: "5" }, 
 
    { year: "2015", redDelicious: "16", mcintosh: "19", oranges: "6", pears: "9" }, 
 
    { year: "2016", redDelicious: "19", mcintosh: "17", oranges: "5", pears: "7" }, 
 
]; 
 

 
var parse = d3.time.format("%Y").parse; 
 

 

 
// Transpose the data into layers 
 
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) { 
 
    return data.map(function(d) { 
 
    return {x: parse(d.year), y: +d[fruit]}; 
 
    }); 
 
})); 
 

 

 
// Set x, y and colors 
 
var x = d3.scale.ordinal() 
 
    .domain(dataset[0].map(function(d) { return d.x; })) 
 
    .rangeRoundBands([10, width-10], 0.02); 
 

 
var y = d3.scale.linear() 
 
    .domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; }); })]) 
 
    .range([height, 0]); 
 

 
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"]; 
 

 
var ticks = y.ticks(), 
 
    lastTick = ticks[ticks.length-1], 
 
    newLastTick = lastTick + (ticks[1] - ticks[0]); 
 
if (lastTick<y.domain()[1]){ 
 
    ticks.push(newLastTick); 
 
} 
 
y.domain([y.domain()[0], newLastTick]); 
 

 
// Define and draw axes 
 
var yAxis = d3.svg.axis() 
 
    .scale(y) 
 
    .orient("left") 
 
    .tickSize(-width, 0, 0) 
 
    .tickFormat(function(d) { return d }) 
 
    .tickValues(ticks); 
 

 

 
var xAxis = d3.svg.axis() 
 
    .scale(x) 
 
    .orient("bottom") 
 
    .tickFormat(d3.time.format("%Y")); 
 

 
svg.append("g") 
 
    .attr("class", "y axis") 
 
    .call(yAxis); 
 

 
svg.append("g") 
 
    .attr("class", "x axis") 
 
    .attr("transform", "translate(0," + height + ")") 
 
    .call(xAxis); 
 

 

 
// Create groups for each series, rects for each segment 
 
var groups = svg.selectAll("g.cost") 
 
    .data(dataset) 
 
    .enter().append("g") 
 
    .attr("class", "cost") 
 
    .style("fill", function(d, i) { return colors[i]; }); 
 

 
var rect = groups.selectAll("rect") 
 
    .data(function(d) { return d; }) 
 
    .enter() 
 
    .append("rect") 
 
    .attr("x", function(d) { return x(d.x); }) 
 
    .attr("y", function(d) { return y(d.y0 + d.y); }) 
 
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); }) 
 
    .attr("width", x.rangeBand()) 
 
    .on("mouseover", function() { tooltip.style("display", null); }) 
 
    .on("mouseout", function() { tooltip.style("display", "none"); }) 
 
    .on("mousemove", function(d) { 
 
    var xPosition = d3.mouse(this)[0] - 15; 
 
    var yPosition = d3.mouse(this)[1] - 25; 
 
    tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")"); 
 
    tooltip.select("text").text(d.y); 
 
    }); 
 

 

 
// Draw legend 
 
var legend = svg.selectAll(".legend") 
 
    .data(colors) 
 
    .enter().append("g") 
 
    .attr("class", "legend") 
 
    .attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; }); 
 
    
 
legend.append("rect") 
 
    .attr("x", width - 18) 
 
    .attr("width", 18) 
 
    .attr("height", 18) 
 
    .style("fill", function(d, i) {return colors.slice().reverse()[i];}); 
 
    
 
legend.append("text") 
 
    .attr("x", width + 5) 
 
    .attr("y", 9) 
 
    .attr("dy", ".35em") 
 
    .style("text-anchor", "start") 
 
    .text(function(d, i) { 
 
    switch (i) { 
 
     case 0: return "Anjou pears"; 
 
     case 1: return "Naval oranges"; 
 
     case 2: return "McIntosh apples"; 
 
     case 3: return "Red Delicious apples"; 
 
    } 
 
    }); 
 

 

 
// Prep the tooltip bits, initial display is hidden 
 
var tooltip = svg.append("g") 
 
    .attr("class", "tooltip") 
 
    .style("display", "none"); 
 
    
 
tooltip.append("rect") 
 
    .attr("width", 30) 
 
    .attr("height", 20) 
 
    .attr("fill", "white") 
 
    .style("opacity", 0.5); 
 

 
tooltip.append("text") 
 
    .attr("x", 15) 
 
    .attr("dy", "1.2em") 
 
    .style("text-anchor", "middle") 
 
    .attr("font-size", "12px") 
 
    .attr("font-weight", "bold");
svg { 
 
    font: 10px sans-serif; 
 
    shape-rendering: crispEdges; 
 
    } 
 

 
    .axis path, 
 
    .axis line { 
 
    fill: none; 
 
    stroke: #000; 
 
    } 
 
    
 
    path.domain { 
 
    stroke: none; 
 
    } 
 
    
 
    .y .tick line { 
 
    stroke: #ddd; 
 
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

问题2

同样,你可以让d3拿出蜱,然后过滤掉那些你不想要的:

var ticks = y.ticks().filter(function(d){ 
    return parseInt(d) === d; // is it a integer? 
}) 

// Define and draw axes 
var yAxis = d3.svg.axis() 
.scale(y) 
.orient("left") 
.tickSize(-width, 0, 0) 
.tickValues(ticks); 

运行代码:

// Setup svg using Bostock's margin convention 
 

 
var margin = {top: 20, right: 160, bottom: 35, left: 30}; 
 

 
var width = 960 - margin.left - margin.right, 
 
    height = 500 - margin.top - margin.bottom; 
 

 
var svg = d3.select("body") 
 
    .append("svg") 
 
    .attr("width", width + margin.left + margin.right) 
 
    .attr("height", height + margin.top + margin.bottom) 
 
    .append("g") 
 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 
 

 

 
/* Data in strings like it would be if imported from a csv */ 
 

 
var data = [ 
 
    { year: "2006", redDelicious: "1", mcintosh: "3" }, 
 
    { year: "2007", redDelicious: "2", mcintosh: "1"}, 
 
    { year: "2008", redDelicious: "0", mcintosh: "2"} 
 
]; 
 

 
var parse = d3.time.format("%Y").parse; 
 

 

 
// Transpose the data into layers 
 
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) { 
 
    return data.map(function(d) { 
 
    return {x: parse(d.year), y: +d[fruit]}; 
 
    }); 
 
})); 
 

 

 
// Set x, y and colors 
 
var x = d3.scale.ordinal() 
 
    .domain(dataset[0].map(function(d) { return d.x; })) 
 
    .rangeRoundBands([10, width-10], 0.02); 
 

 
var y = d3.scale.linear() 
 
    .domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; }); })]) 
 
    .range([height, 0]); 
 

 
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"]; 
 

 
var ticks = y.ticks().filter(function(d){ 
 
\t return parseInt(d) === d; 
 
}) 
 

 
// Define and draw axes 
 
var yAxis = d3.svg.axis() 
 
    .scale(y) 
 
    .orient("left") 
 
    .tickSize(-width, 0, 0) 
 
    .tickValues(ticks); 
 
    
 
var xAxis = d3.svg.axis() 
 
    .scale(x) 
 
    .orient("bottom") 
 
    .tickFormat(d3.time.format("%Y")); 
 

 
svg.append("g") 
 
    .attr("class", "y axis") 
 
    .call(yAxis); 
 

 
svg.append("g") 
 
    .attr("class", "x axis") 
 
    .attr("transform", "translate(0," + height + ")") 
 
    .call(xAxis); 
 

 

 
// Create groups for each series, rects for each segment 
 
var groups = svg.selectAll("g.cost") 
 
    .data(dataset) 
 
    .enter().append("g") 
 
    .attr("class", "cost") 
 
    .style("fill", function(d, i) { return colors[i]; }); 
 

 
var rect = groups.selectAll("rect") 
 
    .data(function(d) { return d; }) 
 
    .enter() 
 
    .append("rect") 
 
    .attr("x", function(d) { return x(d.x); }) 
 
    .attr("y", function(d) { return y(d.y0 + d.y); }) 
 
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); }) 
 
    .attr("width", x.rangeBand()) 
 
    .on("mouseover", function() { tooltip.style("display", null); }) 
 
    .on("mouseout", function() { tooltip.style("display", "none"); }) 
 
    .on("mousemove", function(d) { 
 
    var xPosition = d3.mouse(this)[0] - 15; 
 
    var yPosition = d3.mouse(this)[1] - 25; 
 
    tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")"); 
 
    tooltip.select("text").text(d.y); 
 
    }); 
 

 

 
// Draw legend 
 
var legend = svg.selectAll(".legend") 
 
    .data(colors) 
 
    .enter().append("g") 
 
    .attr("class", "legend") 
 
    .attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; }); 
 
    
 
legend.append("rect") 
 
    .attr("x", width - 18) 
 
    .attr("width", 18) 
 
    .attr("height", 18) 
 
    .style("fill", function(d, i) {return colors.slice().reverse()[i];}); 
 
    
 
legend.append("text") 
 
    .attr("x", width + 5) 
 
    .attr("y", 9) 
 
    .attr("dy", ".35em") 
 
    .style("text-anchor", "start") 
 
    .text(function(d, i) { 
 
    switch (i) { 
 
     case 0: return "Anjou pears"; 
 
     case 1: return "Naval oranges"; 
 
     case 2: return "McIntosh apples"; 
 
     case 3: return "Red Delicious apples"; 
 
    } 
 
    }); 
 

 

 
// Prep the tooltip bits, initial display is hidden 
 
var tooltip = svg.append("g") 
 
    .attr("class", "tooltip") 
 
    .style("display", "none"); 
 
    
 
tooltip.append("rect") 
 
    .attr("width", 30) 
 
    .attr("height", 20) 
 
    .attr("fill", "white") 
 
    .style("opacity", 0.5); 
 

 
tooltip.append("text") 
 
    .attr("x", 15) 
 
    .attr("dy", "1.2em") 
 
    .style("text-anchor", "middle") 
 
    .attr("font-size", "12px") 
 
    .attr("font-weight", "bold");
svg { 
 
    font: 10px sans-serif; 
 
    shape-rendering: crispEdges; 
 
    } 
 

 
    .axis path, 
 
    .axis line { 
 
    fill: none; 
 
    stroke: #000; 
 
    } 
 
    
 
    path.domain { 
 
    stroke: none; 
 
    } 
 
    
 
    .y .tick line { 
 
    stroke: #ddd; 
 
    } 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

+0

在这里,我可以只能看到一个问题,在SVG边界结束时,有时会在SVG容器内额外进行顶部y轴刻度裁剪。任何解决此问题的方法?我的容器高度和宽度是使用容器元素访问的,而不是硬编码的。 – devo

+0

@devo,很好的问题。你应该在填充ticks后再调整'y.domain'。查看问题1上面的更新答案。 – Mark