有没有办法最小化分组水平条形图中范围之间的填充?
我正在为 AngularJS 构建一个水平条形图指令,目前我已经很接近了,但我对间距不太满意。如果有更好的方法来构建垂直范围,我将非常感谢任何提示。到目前为止,这是我的结果:
angular.module('MissionControlApp').directive('d3GroupedHorizontalBarChart2', ['d3', '$timeout', function(d3, $timeout) {
return {
restrict: 'E',
scope: {
data: '=',
onClick: '&d3OnClick'
},
link: function(scope, ele) {
var refreshScope = function() {
scope.$apply();
};
var svg = d3.select(ele[0])
.append("svg")
.attr("width", "100%");
// on window resize, re-render d3 canvas
window.onresize = function() {
return scope.$apply();
};
scope.$watch(function(){
return angular.element(window)[0].innerWidth;
}, function(){
return scope.render(scope.data);
}
);
// watch for data changes and re-render
scope.$watch("data", function(newVals) {
if(!newVals) return;
return scope.render(newVals);
}, true);
// define render function for grouped bar charts
scope.render = function(data){
if(!data) return;
// remove all previous items before render
svg.selectAll("*").remove();
// setup variables
var margin = {top: 25, right: 40, bottom: 70, left: 150};
var width = d3.select(ele[0])._groups[0][0].offsetWidth - margin.left - margin.right;
var height = (scope.data.length * 60);
svg.attr('height', height + margin.top + margin.bottom);
var y0 = d3.scaleBand()
.rangeRound([0, height])
.paddingInner(0.01);
var y1 = d3.scaleBand()
.padding(0.01);
var x = d3.scaleLinear()
.rangeRound([0, width]);
var color = d3.scaleLinear()
.domain([0, 25, 50, 75, 100])
.range(["#51b75d", "#90eb9d","#ffff8c","#f5c93f","#c45c44"])
.interpolate(d3.interpolateHcl);
var xAxis = d3.axisBottom(x)
.tickSizeInner(-(height-5))
.tickPadding(8);
var keys = d3.keys(data[0]).filter(function(key) { return key !== "user"; });
y0.domain(data.map(function(d) { return d.user; }));
y1.domain(keys).rangeRound([0, y0.bandwidth()]);
x.domain([0, 100]);
// Define bars
var bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + margin.left + "," + (y0(d.user) + (y0.bandwidth()/2) + margin.top - y1.bandwidth()) + ")"; });
var barEnter = bar.selectAll("rect")
.data(function(d) { return d.values; })
.enter();
barEnter.append("rect")
.attr("height", y1.bandwidth())
.attr("y", function(d) {return y1(d.name); })
.attr("x", 0)
.attr("value", function(d){return d.name;})
.attr("width", 0)
.attr("fill", function(d) { return color(d.value); })
.on("mouseover", function() { d3.select(this).style("cursor", "pointer");})
.on("mouseout", function() { d3.select(this).style("cursor", "default");})
.on("click", function(d){
scope.onClick({item: d});
d3.select(".selectedBlueFill").classed("selectedBlueFill", false);
d3.select(this).classed("selectedBlueFill", true);
$timeout(refreshScope, 0, false); // flush the digest cycle
})
.transition()
.duration(1000)
.attr("width", function(d) { return x(d.value); });
barEnter.append("text")
.attr("fill", "#000")
.attr("y", function(d){return y1(d.name) + (y1.bandwidth() / 2);})
.attr("x", function(d){return x(d.value);})
.attr("dx", 5)
.attr("dy", ".35em")
.text(function(d){return parseFloat(d.value).toFixed(0) + "%";})
.attr("fill-opacity", 0)
.transition()
.duration(1500)
.attr("fill-opacity", 1);
// Set up x axis
svg.append("g")
.attr("class", "axisHorizontal")
.attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
.call(xAxis);
// Set up y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3.axisLeft(y0));
// Draw the legend
// Create the gradient for the legend
svg.append("defs")
.append("linearGradient")
.attr("id", "legend-traffic")
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "100%").attr("y2", "0%")
.selectAll("stop")
.data(color.range())
.enter().append("stop")
.attr("offset", function(d,i) { return i/(color.range().length-1); })
.attr("stop-color", function(d) { return d; });
// Legend variables
var legendWidth = width * 0.6;
var legendHeight = 10;
// Legend container
var legendSvg = svg.append('g')
.attr("class", "legendWrapper")
.attr("transform", "translate(" + ((width + margin.left + margin.right)/2) + "," + (height + margin.top + margin.bottom) + ")");
// Draw the rectangle
legendSvg.append("rect")
.attr("class", "legendRect")
.attr("x", -legendWidth/2)
.attr("y", -30)
.attr("width", legendWidth)
.attr("height", legendHeight)
.attr("fill", "url(#legend-traffic)");
// Append title
legendSvg.append("text")
.attr("class", "legendTitle")
.attr("x", 0)
.attr("y", -35)
.attr("text-anchor", "middle")
.text("Worksets Opened %");
// Set scale for x-axis
var xScale = d3.scaleLinear()
.range([0, legendWidth])
.domain([0,100]);
// Define x-axis
var legendAxis = d3.axisBottom(xScale).ticks(5);
// Set up x-axis
legendSvg.append("g")
.attr("class", "axisLegend")
.attr("transform", "translate(" + (-legendWidth/2) + "," + (legendHeight-30) + ")")
.call(legendAxis);
};
}
};
}]);
但是我得到的结果是内部间距很大。我将 .paddingInner
属性设置为 0.001,但最终还是会出现大间距......想法?
最佳答案
我们的想法是创建群组,其中每个群组都拥有该群组的独特条形图。
现在,一旦你有了组,你就可以用一些数学来改变间距,如下面的代码片段所示:
var bar = chart
.selectAll("g")
.data(zippedData)
.enter().append("g")
.attr("transform", function(d, i) {
//here barHeight is the width of the bars.
return "translate(" + spaceForLabels + ","
+ (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
});
通过更改 gapBetweenGroups
的值,您可以调节组的距离。
工作代码 here
关于html - d3 v4 内部填充设置为 .001 实际上不会导致范围之间的小填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48651078/