我真的很讨厌问这个问题,特别是因为我知道已经问过很多遍了-我已经阅读了这些帖子。但是我的问题仍然存在-我根本不了解这种机制是如何工作的。我是d3js的新手,正在流星中使用 v3.x ;我已经看完了教程,并做了一些工作,但是无法使用新数据进行更新。同样,我很抱歉再次提出这个建议,但是我所读过的其他文章都没有解决这个问题。
这是一个代码片段,我将所有无关紧要的内容都去了,专注于核心功能:
var w = 800;
var h = 800;
var intensity = 25;
var margin = {
top: 75,
right: 100,
bottom: 75,
left: 60
};
var svg = d3.select('#heatmap')
.append('svg')
.attr('width', w + margin.left + margin.right)
.attr('height', h + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// get csv data, x & y coords, etc...
createHeatmap = function(csv, x, y) {
var data = d3.csv.parseRows(csv).map(function(row) {
return row.map(function(d) {
return +d;
});
});
// set some values
var min = 0;
var max = d3.max(data, function(d, i) {
return i + 1;
});
var rectSize = 4;
// set the scales for both axes
...
// set up the axes
...
// define colorScale
...
// create heatmap
svg.selectAll('g')
.data(data)
.enter()
.append('g')
.selectAll('rect')
.data(function(d, i, j) {
return d;
})
.enter() // start drawing rects
.append('rect')
.attr('x', function(d, i, j) {
return (i * rectSize);
})
.attr('y', function(d, i, j) {
return (j * rectSize);
})
.attr('width', w / max)
.attr('height', h / max)
.style('fill', function(d, i, j) {
return colorScale(d * intensity);
});
// append axes, scales, labels, etc.
}
// create heatmap
createHeatmap(csv, x, y);
我的问题是,当我将新数据传递到createHeatmap()时,我不理解为什么图表不更新热图。
我在调试器中逐步进行了调试,一切工作均与最初创建热图时所期望的一样,该图正确呈现。当我发送新数据时,就是神秘的开始。调试器在d3js本身(不在我的代码中)深处显示,enter()具有完整的od null值数组,而不是我要传递的数据。该数据一直存在。因此,当d3js处理空数据时,它自然会返回一个空对象,因此不会发生更新。
很明显,我没有正确地执行更新,但是对于需要做些什么来纠正它一无所知。
任何建议都将不胜感激。
谢谢!
更新: 安德鲁,感谢您的回复。我尝试了您的第一个建议,修改了您的示例以适合我的数据,但是它不会随新数据一起更新。
我的更改:
var w = 800;
var h = 800;
var intensity = 25;
var margin = {
top: 75,
right: 100,
bottom: 75,
left: 60
};
var svg = d3.select('#heatmap')
.append('svg')
.attr('width', w + margin.left + margin.right)
.attr('height', h + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// get csv data, x & y coords, etc...
createHeatmap = function(csv, x, y) {
var data = d3.csv.parseRows(csv).map(function(row) {
return row.map(function(d) {
return +d;
});
});
// set some values
var min = 0;
var max = d3.max(data, function(d, i) {
return i + 1;
});
var rectSize = 4;
// set the scales for both axes
...
// set up the axes
...
// define colorScale
...
// append group of svg elements bound to data
var rows = svg.selectAll('g')
.data(data);
// enter new rows where needed
rows.enter().append('g');
// select all rects
var rects = rows.selectAll('rect')
.data(function(d, i, j) {
return d;
});
// enter new rects:
rects.enter().append('rect')
.attr('x', function(d, i, j) {
return (i * rectSize);
})
.attr('y', function(d, i, j) {
return (j * rectSize);
})
.attr('width', w / max)
.attr('height', h / max)
.style('fill', function(d, i, j) {
return colorScale(d * intensity);
});
添加的代码段:
var csv = "'3, 6, 0, 8'\n'1, 9, 0, 4'\n'3, 0, 1, 8'\n'4, 0, 2, 7";
csv = csv.replace(/'/g,'');
var button = d3.select('button')
.on('click', function() {
createHeatmap(update());
});
var w = 120;
var h = 120;
var intensity = 10;
var margin = {
top: 25,
right: 25,
bottom: 25,
left: 25
};
var svg = d3.select('#heatmap')
.append('svg')
.attr('width', w + margin.left + margin.right)
.attr('height', h + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
createHeatmap(csv);
function createHeatmap(csv) {
console.log(csv);
var data = d3.csv.parseRows(csv).map(function(row) {
return row.map(function(d) {
return +d;
});
});
var min = 0;
var max = d3.max(data, function(d, i) {
return i + 1;
});
var rectSize = 30;
// define a colorScale with domain and color range
var colorScale = d3.scale.linear()
.domain([0,0.5,1])
.range(['red', 'green', 'blue']);
// append group of svg elements bound to data
var rows = svg.selectAll('g')
.data(data);
// enter new rows where needed
rows.enter().append('g');
// select all rects
var rects = rows.selectAll('rect')
.data(function(d, i, j) {
return d;
});
// enter new rects:
rects.enter().append('rect')
.attr('x', function(d, i, j) {
return (i * rectSize);
})
.attr('y', function(d, i, j) {
return (j * rectSize);
})
.attr('width', w / max)
.attr('height', h / max)
.style('fill', function(d, i, j) {
return colorScale(d * intensity);
});
}
function update() {
var data = "'0, 1, 9, 5'\n'4, 0, 7, 2'\n'6, 3, 0, 8'\n'5, 3, 7, 0";
data = data.replace(/'/g,'');
return data;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<button>Update</button>
<div id="heatmap"></div>
答案 0 :(得分:1)
问题出在方法链接中。
第一次运行时,事情应该会按预期运行:
// create heatmap
svg.selectAll('g') // 1. select all g elements
.data(data) // 2. assign data
.enter() // 3. enter and append a g for each item in the data array
.append('g') // that doesn't have a corresponding element in the DOM (or more accurately, the selection)
.selectAll('rect') // 4. For each newly entered g, select child rectangles
.data(function(d, i, j) { // 5. assign data to child selection.
return d;
})
.enter() // 6. Enter and append a rect for each item in the child g's data array
.append("rect") // that doesn't have a corresponding element in the DOM.
.... // 7. Style
在第一次运行时,我们选择了所有g
,没有一个,因此enter选择将为数据数组中的每个项目都具有一个元素:我们正在输入所有内容。与子矩形相同:进行选择时不存在子矩形,因此您将所有内容输入子数据数组。
在第二次运行中,您使用svg.selectAll("g")
选择了您第一次创建的所有g
-如果数据数组的项目数相同,则无需输入任何内容。您不想添加任何内容:第二次enter().append()
(不是在任何情况下都用.append()附加了更多元素)。
基本上是第二遍,您正在修改一个空选择。
您想更新。在第二遍中,输入选择为空时,更新选择具有所有现有的g
。
有几种方法可以做到这一点,一种是断开链接:
这是第3版解决方案:
var rows = svg.selectAll("g")
.data(data);
// enter new rows where needed
rows.enter().append("g")
// Select all rects
var rects = rows.selectAll("rect")
.data(function(d) { return d; })
// Enter new rects:
rects.enter().append("rect")
// Update rects (all rects, not just the newly entered):
rects.attr()...
以下代码段使用此模式,它根据需要输入新的rect
和g
。然后,随后更新所有rect
和g
。 这利用了d3v3中的魔力,其中更新选择和回车选择在内部合并,而d3v4,v5中不是这种情况,我将在下面显示。
var button = d3.select("button")
.on("click", function() {
update(random());
})
var svg = d3.select("div")
.append("svg");
var color = d3.scale.linear()
.domain([0,0.5,1])
.range(["red","orange","yellow"])
update(random());
function update(data) {
var rows = svg.selectAll("g")
.data(data);
// enter new rows where needed
rows.enter()
.append("g")
.attr("transform", function(d,i) {
return "translate("+[0,i*22]+")";
})
// Select all rects:
var rects = rows.selectAll("rect")
.data(function(d) { return d; })
// Enter new rects:
rects.enter().append("rect")
// Update rects:
rects.attr("fill", function(d) {
return color(d);
})
.attr("x", function(d,i) { return i*22; })
.attr("width", 20)
.attr("height", 20);
console.log("entered rows:" + rows.enter().size());
console.log("entered rects:" + rects.enter().size());
}
function random() {
return d3.range(5).map(function() {
return d3.range(5).map(function() {
return Math.random();
})
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<button>Update</button>
<div></div>
v4 / v5 :
对于我建议升级到的v4 / v5,此模式有些不同,因为您必须明确合并回车和更新选择:
var button = d3.select("button")
.on("click", function() {
update(random());
})
var svg = d3.select("div")
.append("svg");
var color = d3.scaleLinear()
.domain([0,0.5,1])
.range(["red","orange","yellow"])
update(random());
function update(data) {
var rows = svg.selectAll("g")
.data(data);
// enter new rows where needed
rows = rows.enter()
.append("g")
.merge(rows) // merge with existing rows
.attr("transform", function(d,i) {
return "translate("+[0,i*22]+")";
})
// Select all rects:
var rects = rows.selectAll("rect")
.data(function(d) { return d; })
// Enter new rects:
rects = rects.enter().append("rect")
.merge(rects);
// Update rects:
rects.attr("fill", function(d) {
return color(d);
})
.attr("x", function(d,i) { return i*22; })
.attr("width", 20)
.attr("height", 20);
}
function random() {
return d3.range(5).map(function() {
return d3.range(5).map(function() {
return Math.random();
})
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Update</button>
<div></div>
更新
您的代码段几乎包含了所有更改,但是您仍然需要分解第二个选择,即矩形选择,以便输入新的矩形,然后更新所有矩形:
var csv = "'3, 6, 0, 8'\n'1, 9, 0, 4'\n'3, 0, 1, 8'\n'4, 0, 2, 7";
csv = csv.replace(/'/g,'');
var button = d3.select('button')
.on('click', function() {
createHeatmap(update());
});
var w = 120;
var h = 120;
var intensity = 10;
var margin = {
top: 25,
right: 25,
bottom: 25,
left: 25
};
var svg = d3.select('#heatmap')
.append('svg')
.attr('width', w + margin.left + margin.right)
.attr('height', h + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
createHeatmap(csv);
function createHeatmap(csv) {
console.log(csv);
var data = d3.csv.parseRows(csv).map(function(row) {
return row.map(function(d) {
return +d;
});
});
var min = 0;
var max = d3.max(data, function(d, i) {
return i + 1;
});
var rectSize = 30;
// define a colorScale with domain and color range
var colorScale = d3.scale.linear()
.domain([0,0.5,1])
.range(['red', 'green', 'blue']);
// append group of svg elements bound to data
var rows = svg.selectAll('g')
.data(data);
// enter new rows where needed
rows.enter().append('g');
// select all rects
var rects = rows.selectAll('rect')
.data(function(d, i, j) {
return d;
});
// enter new rects:
rects.enter().append('rect');
// CHANGES HERE:
// Broke chain so that update actions aren't carried out on the enter selection:
rects.attr('x', function(d, i, j) {
return (i * rectSize);
})
.attr('y', function(d, i, j) {
return (j * rectSize);
})
.attr('width', w / max)
.attr('height', h / max)
.style('fill', function(d, i, j) {
return colorScale(d * intensity);
});
}
function update() {
var data = "'0, 1, 9, 5'\n'4, 0, 7, 2'\n'6, 3, 0, 8'\n'5, 3, 7, 0";
data = data.replace(/'/g,'');
return data;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<button>Update</button>
<div id="heatmap"></div>