我正在尝试在D3中创建范围条形图(请参见a similar example)。
我发现了this answer,我能够完全按照自己的需要对其进行自定义,只是它使用了D3的较旧版本,数据是单独数组的愚蠢格式,并且没有响应(为什么那么多D3东西没有响应?)。
我放弃了这一尝试,并从头开始。现在,我主要使用最新的D3和上述SO问题的一些思想来工作,但是数据显示不正确。当我调整大小时,这些条会意外地增长和收缩,随着宽度的增加,最终会出现负宽度值。
我非常感谢您帮助我了解条形尺寸做错了什么。请注意,要观察不正确的行为,您必须扩展代码段并调整窗口大小。
let data = [
{ field: "Field Name 1", salary: [42, 65], bonusRange: [null, null] },
{ field: "Field Name 2", salary: [28, 58], bonusRange: [null, null] },
{ field: "Field Name 3", salary: [33, 57], bonusRange: [null, null] },
{ field: "Field Name 4", salary: [33, 50], bonusRange: [null, null] },
{ field: "Field Name 5", salary: [32, 44], bonusRange: [38, 52] },
{ field: "Field Name 6", salary: [27, 40], bonusRange: [null, null] }
];
const chartDiv = document.getElementById("chart");
const svg = d3.select(chartDiv).append("svg");
function redraw() {
svg.selectAll("g").remove();
const margin = 20;
const width = chartDiv.clientWidth - margin * 2;
const height = chartDiv.clientHeight - margin * 2;
svg
.attr("width", width)
.attr("height", height)
.attr("transform", `translate(${margin}, ${margin})`);
const xScale = d3
.scaleLinear()
.range([120, width - 10])
.domain([20, 70]);
const xAxis = d3.axisBottom(xScale);
const yScale = d3
.scaleLinear()
.range([0, height - margin])
.domain([-0.5, (data.length - 1) * 1.1]);
const yAxis = d3
.axisLeft(yScale)
.ticks(data.length)
.tickFormat(function(d, i) {
return data[i].field;
});
svg
.append("g")
.attr("class", "axis")
.call(xAxis)
.attr("transform", `translate(0, ${height - margin})`);
svg
.append("g")
.attr("class", "axis")
.call(yAxis)
.attr("transform", "translate(120, 0)");
svg
.append("g")
.attr("id", "bars")
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.style("fill", "#f61166")
.attr("class", "bar")
.attr("x", function(d) {
return xScale(d.salary[0]);
})
.attr("height", 25)
.attr("width", function(d) {
return xScale(d.salary[1] - d.salary[0]);
})
.attr("y", function(d, i) {
return yScale(i) - margin * 0.5;
});
}
redraw();
window.addEventListener("resize", redraw);
* {
box-sizing: border-box;
}
body {
padding: 10px;
}
#chart {
outline: 1px solid fuchsia;
height: 0;
overflow: hidden;
padding-top: 59.75%;
background: white;
position: relative;
}
#chart svg {
position: absolute;
top: 0;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="chart" class=""></div>
答案 0 :(得分:1)
您的代码需要大量重构,通常,只要您想更改数据(或在这种情况下,窗口大小为 ps”,就应该遵循d3 update pattern。我不确定100%确定这是一个好主意,因为我从来没有像这样使我的图表“响应”,但似乎效果很好)
换句话说,当您要更新时,不要使用svg.selectAll("g").remove();
,显然可以,但这并不是d3的习惯。
我从redraw函数中删除了所有附加项,并将它们放在包装函数中,以便我们可以按类名或变量引用它们。
我还将您的yScale变量设置为一个比例带,以便更轻松地根据数据调整图表的大小。
const yScale = d3.scaleBand()
.domain(data.map(d => d.field))
.padding(0.1)
.paddingOuter(0.1)
.paddingInner(0.1);
我在下面添加了一个代码段,希望应该清楚如何完成此操作。
let data = [
{ field: "Field Name 1", salary: [42, 65], bonusRange: [null, null] },
{ field: "Field Name 2", salary: [28, 58], bonusRange: [null, null] },
{ field: "Field Name 3", salary: [33, 57], bonusRange: [null, null] },
{ field: "Field Name 4", salary: [33, 50], bonusRange: [null, null] },
{ field: "Field Name 5", salary: [32, 44], bonusRange: [38, 52] },
{ field: "Field Name 6", salary: [27, 40], bonusRange: [null, null] }
];
chart(data);
function chart(data) {
var svg = d3.select("#chart"),
margin = {top: 55, bottom: 0, left: 85, right: 0},
width = parseInt(svg.style("width")) - margin.left - margin.right,
height = parseInt(svg.style("height")) - margin.top - margin.bottom;
const xScale = d3.scaleLinear()
.domain([20, 70]);
const yScale = d3.scaleBand()
.domain(data.map(d => d.field))
.padding(0.1)
.paddingOuter(0.1)
.paddingInner(0.1);
const xAxis = svg.append("g")
.attr("class", "x-axis")
const yAxis = svg.append("g")
.attr("class", "y-axis")
redraw(width, height);
function redraw(width, height) {
yScale.range([margin.top, height - margin.bottom])
svg.selectAll(".y-axis")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale)
.ticks(data.length)
.tickFormat(function(d, i) {
return data[i].field;
}));
xScale.range([margin.left, width - margin.right]);
svg.selectAll(".x-axis").transition().duration(0)
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale));
var bar = svg.selectAll(".bar")
.data(data)
bar.exit().remove();
bar.enter().append("rect")
.attr("class", "bar")
.style("fill", "#f61166")
.merge(bar)
.transition().duration(0)
.attr("width", function(d) {
return xScale(d.salary[1] - d.salary[0]) - xScale(0);
})
.attr("height", yScale.bandwidth())
.attr("y", d => yScale(d.field))
.attr("x", function(d) {
return xScale(d.salary[0]);
});
}
d3.select(window).on('resize', function() {
width = parseInt(svg.style("width")) - margin.left - margin.right,
height = parseInt(svg.style("height")) - margin.top - margin.bottom;
redraw(width, height);
});
}
body {
padding: 25px 25px 25px 25px;
font: 18px arial;
}
#chart {
outline: 1px solid fuchsia;
position: absolute;
width: 95%;
height: 95%;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg id="chart"></svg>