我正在寻找一种限制图表中列宽的方法,我相信这应该相对简单,但我找不到一种方法。
我正在从一些动态数据中填充图表,其中列数可以非常显着地变化 - 介于1到20之间。
例如:csv的样本
Location,Col1
"Your house",20
Location,Col1,Col2,Col3,Col4,Col5
"My House",12,5,23,1,5
这个工作正常,并且col宽度是动态的,但是当数据中只有一列时,我最终得到一个宽度为756的条(整个图表),我不喜欢它的样子。
我想做的事情是,无论数据列数是多少,都只有一个宽度为100px的最大列。
下面是我的图表脚本
非常感谢,
<script>
var margin = {
top : 40,
right : 80,
bottom : 80,
left : 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear().range([ 0, width ]);
var y = d3.scale.linear().range([ height, 0 ]);
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .05);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var chart = d3.select("body").append("svg")
.attr("class","chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var legendChart = d3.select("body").append("svg")
.attr("class","chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("/sampledata.csv.txt", function(error, data) {
// Use the first row of csv for header names
var reasonNames = d3.keys(data[0]).filter(function(key) {
return key !== "Location";
});
//console.log(reasonNames);
data.forEach(function(d) {
d.reasons = reasonNames.map(function(name) {
return {
name : name,
value : +d[name]
};
});
//console.log(d.reasons);
});
x0.domain(data.map(function(d) {return d.Location; }));
x1.domain(reasonNames).rangeRoundBands([0, x0.rangeBand()]);
console.log(x0.rangeBand());
y.domain([0, d3.max(data, function(d) { return d3.max(d.reasons, function(d) { return d.value; }); })]);
var maxVal = d3.max(data, function(d) { return d3.max(d.reasons, function(d) { return d.value; }); });
//console.log(maxVal);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
//.tickFormat(d3.format(".2s"));
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var location = chart.selectAll(".name")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Location) + ",0)"; });
location.selectAll("rect")
.data(function(d) { return d.reasons; })
.enter().append("rect")
.attr("width", x1.rangeBand()-2)
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d,i) { return "#"+3+"9"+i; /*color(d.name);*/ });
chart.selectAll("text")
.data(data)
.enter().append("text")
.attr("x", function(d) { return x1(d.name)+ x.rangeBand() / 2; })
.attr("y", function(d) { return y(d.value); })
.attr("dx", -3) // padding-right
.attr("dy", ".35em") // vertical-align: middle
.attr("text-anchor", "end") // text-align: right
.text("String");
var legend = legendChart.selectAll(".legend")
.data(reasonNames.slice().reverse())
.enter()
.append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")";
});
legend.append("rect")
//.attr("x", width - 18)
.attr("x", 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {/*console.log(i);*/return "#" + 3 + "9" + i;
});
legend.append("text")
//.attr("x", width - 24)
.attr("x", 48)
.attr("y", 9).attr("dy",".35em")
//.style("text-anchor", "end")
//.text(function(d,i) { return String.fromCharCode((65+i))+i; });
.text(function(d) { return d; });
});
</script>
答案 0 :(得分:8)
实现这一目标的最简单方法是更改行
.attr("width", x1.rangeBand()-2)
到
.attr("width", Math.min(x1.rangeBand()-2, 100))
您可能还想调整起始位置和/或填充。
答案 1 :(得分:4)
如果有人卡在上面,则调整起始位置的代码:
.attr("x", function(d, i) { return x1(d.seriesName) + (x1.rangeBand() - 100)/2 ;})
P.S。 :参考Lars的答案。
答案 2 :(得分:2)
设置列的绝对最大宽度不允许正确渲染不同的屏幕分辨率,div大小等。
就我而言,我只是希望当列数本身很小时,列看起来不那么大 通过更改最大宽度(所有列适合的位置),内部和外部填充,我发现使用缩放定义更容易,更直接。
var w = 600
// var w = parseInt(d3.select(parentID).style('width'), 10) // retrieve the div width dynamically
var inner_padding = 0.1
var outer_padding = 0.8
var xScale = d3.scale.ordinal().rangeRoundBands([0, w], inner_padding, outer_padding)
渲染绘图时,我只是运行了一个switch / if-else语句,它指定了不同的填充值。要绘制的列数越少,我使用的outer_padding(以及最终的内部填充)值就越大。
这样,我保留了地块responsive。
答案 3 :(得分:1)
我可以使用上面的答案更改条形的宽度。但不幸的是,当图表中有一个条形并且它使用最大宽度设置时,我的X轴标签没有对齐。
var tradeGroup = svg.selectAll("g.trade")
.data(trades)
.enter()
.append("svg:g")
.attr("class", "trade")
.style("fill", function (d, i) {
return self.color(self.color.domain()[i]);
})
.style("stroke", function (d, i) {
return d3.rgb(self.color(self.color.domain()[i])).darker();
});
var aWidth = Math.min.apply(null, [x.rangeBand(), 100]);
// Add a rect for each date.
var rect = tradeGroup.selectAll("rect")
.data(Object)
.enter()
.append("svg:rect")
.attr("x", function (d) {
return x(d.x);
})
.attr("y", function (d) { return y( (d.y || 0) + (d.y0 || 0)); })
.attr("height", function (d) { return y(d.y0 || 0) - y((d.y || 0) + (d.y0 || 0)); })
.attr("width", Math.min.apply(null, [x.rangeBand(), 100]));
答案 4 :(得分:1)
为完整起见,完整答案如下:
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", (d) -> x1(d.name) + (x1.rangeBand() - d3.min([x1.rangeBand(), 100]))/2)
.attr("width", d3.min([x1.rangeBand(), 100]))
.attr("y", (d) -> y(d.grade) )
.attr("height", (d)-> height - y(d.value) )
(coffeescript语法)
请注意,这包括完整答案,'宽度'和'x'设置。此外,'x'设置考虑了当100宽度不是最小值时。
答案 5 :(得分:0)
我想分享一下我对此提出的答案略有不同。我不想硬编码最大条形宽度,因为1)它无法响应不同的屏幕尺寸,并且2)还需要使用x-coordinate属性或接受一些不规则的间距。
相反,我只是根据条变得太宽的点设置了最小条数(在我的情况下,我发现少于12条使我的图表看起来很奇怪)。然后,如果条形数量少于该数量,则按比例调整scaleBand的range属性。因此,例如,如果最小值设置为12,并且数据中只有5个项目,而不是将每个项目渲染为全宽的1/5,我将范围缩小为原始宽度的5/12
基本上是这样的:
// x is a scaleBand() that was previously defined, and this would run on update
var minBarSlots = 12;
if (data.length < minBarSlots) {
x.range([0, width*(data.length/minBarSlots)])
}
else {
x.range([0, width])
}`