我有一个使用d3和svg表示的网格。我试图选择相邻(相邻)瓷砖到网格上的任何特定瓷砖。通过网格上的x和y坐标访问切片。 我的感觉相当混乱,并没有完全符合我的要求,我不希望选择点击的瓷砖,或者对角线的瓷砖。
solange
jsfiddle - https://jsfiddle.net/wkencq2w/15/
我想知道的是 - 有没有办法通过两个属性选择数据,如下所示:
var w = 960,
h = 500,
z = 20,
x = w / z,
y = h / z;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(d3.range(x * y))
.enter().append("rect")
.attr("width", z)
.attr("height", z)
.attr("clicked", false)
.attr('x', horizontalpos)
.attr('y', verticalpos)
.on("click", test)
.style("stroke", "rgb(6,120,155)")
.style("stroke-width", 2)
.style("fill", "rgb(255, 255, 255)")
function translate(d) {
return "translate(" + (d % x) * z + "," + Math.floor(d / x) * z + ")";
}
function verticalpos(d) {
return ((d % x) * z);
}
function horizontalpos(d) {
return (Math.floor(d / x) * z );
}
function test(){
var d = d3.selectAll("[x='40']").filter("[y='40']");
d3.selectAll("[x=" + "'"+ (parseInt(d.attr("x")) +20).toString() +"'" +"],[x=" + "'"+ (parseInt(d.attr("x")) -20).toString() +"'" +"],"+ "[x=" + "'"+ (parseInt(d.attr("x"))).toString() +"'" +"]")
.filter("[y=" + "'"+ (parseInt(d.attr("y"))).toString() +"'" +"],[y=" + "'"+ (parseInt(d.attr("y")) +20).toString() +"'" +"]"+",[y=" + "'"+ (parseInt(d.attr("y")) -20).toString() +"'" +"]")
.transition()
.style("fill", "black");
}
这对我不起作用,但其背后的逻辑是我想如何选择数据。
答案 0 :(得分:3)
因为它是D3,我不是基于位置计算而是基于数据绑定来做到这一点。这将大大简化问题并减少代码的数量和复杂性。一种可能的方法是定义具有x和y属性的二维对象数组,然后将其绑定到D3选择:
var grid = d3.range(y).map(function(dy) {
return d3.range(x).map(function(dx) {
return {x: dx, y: dy};
});
});
var g = svg.selectAll("g")
.data(grid)
.enter().append("g") // Group each row's rects in a svg:g
.selectAll("rect") // Do a nested selection
.data(function(d) { return d; }) // Bind the sub-array for this row
从这种方法中受益最多的部分是你的test()
函数,它现在可以对绑定到每个矩形的数据起作用,而不是必须获取属性值并使用它们进行计算。
function test(d) {
var clicked = d3.select(this).datum(); // Object bound to the rect.
d3.selectAll("rect").filter(function(d) {
// Do the filtering based on data rather than on positions.
return d.x === clicked.x && Math.abs(d.y - clicked.y) === 1 ||
d.y === clicked.y && Math.abs(d.x - clicked.x) === 1;
})
.transition()
.style("fill", "black");
}
请查看此JSFiddle以获取完整示例。
答案 1 :(得分:3)
您可以通过将两个属性放在一起来选择数据,例如: [X = '40 '] [Y = '40']。这与css运算符一起允许生成一个css选择字符串,为您提供所要求的内容。
var w = 960,
h = 500,
z = 20,
x = w / z,
y = h / z;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(d3.range(x * y))
.enter().append("rect")
.attr("width", z)
.attr("height", z)
.attr("clicked", false)
.attr('x', horizontalpos)
.attr('y', verticalpos)
.on("click", test)
.style("stroke", "rgb(6,120,155)")
.style("stroke-width", 2)
.style("fill", "rgb(255, 255, 255)")
function translate(d) {
return "translate(" + (d % x) * z + "," + Math.floor(d / x) * z + ")";
}
function verticalpos(d) {
return ((d % x) * z);
}
function horizontalpos(d) {
return (Math.floor(d / x) * z );
}
function test(d) {
x = parseInt(d3.select(this).attr("x"));
y = parseInt(d3.select(this).attr("y"));
var selector = ""
for (var dx=-20;dx<=20;dx+=20) {
for (var dy=-20;dy<=20;dy+=20) {
selector += "[x='"+ (x + dx) +"'][y='"+ (y + dy) +"'],"
}
}
// cut off the final extraneous comma
selector = selector.substring(0, selector.length - 1);
d3.selectAll(selector)
.transition()
.style("fill", "black");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
或者如果你只想要一个没有中心的十字架,你可以在问题中描述......
var w = 960,
h = 500,
z = 20,
x = w / z,
y = h / z;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(d3.range(x * y))
.enter().append("rect")
.attr("width", z)
.attr("height", z)
.attr("clicked", false)
.attr('x', horizontalpos)
.attr('y', verticalpos)
.on("click", test)
.style("stroke", "rgb(6,120,155)")
.style("stroke-width", 2)
.style("fill", "rgb(255, 255, 255)")
function translate(d) {
return "translate(" + (d % x) * z + "," + Math.floor(d / x) * z + ")";
}
function verticalpos(d) {
return ((d % x) * z);
}
function horizontalpos(d) {
return (Math.floor(d / x) * z );
}
function test(d) {
x = parseInt(d3.select(this).attr("x"));
y = parseInt(d3.select(this).attr("y"));
var selector = ""
var deltas = [[-20, 0], [20, 0], [0, 20], [0, -20]];
for (var i=0;i < deltas.length;i++) {
selector += "[x='"+ (x + deltas[i][0]) +"'][y='"+ (y + deltas[i][1]) +"'],"
}
// cut off the final extraneous comma
selector = selector.substring(0, selector.length - 1);
d3.selectAll(selector)
.transition()
.style("fill", "black");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
答案 2 :(得分:2)
我尝试使用过滤器解决此问题:
function test(d){
var x = d3.select(this);
var x1 = (parseInt(x.attr("x")) +20);
var x2 = (parseInt(x.attr("x")) -20);
var y1 = (parseInt(x.attr("y")) +20);
var y2 = (parseInt(x.attr("y")) -20);
var f = d3.selectAll("rect")[0].filter(function(d){
//left rect
if (d3.select(d).attr("x") == x1 && d3.select(d).attr("y") == parseInt(x.attr("y")))
return true;
//right rect
if (d3.select(d).attr("x") == x2 && d3.select(d).attr("y") == parseInt(x.attr("y")))
return true;
//bottom rect
if (d3.select(d).attr("y") == y1 && d3.select(d).attr("x") == parseInt(x.attr("x")))
return true;
//top rect
if (d3.select(d).attr("y") == y2 && d3.select(d).attr("x") == parseInt(x.attr("x")))
return true;
return false;
});
//select all filtered and make their fill black
d3.selectAll(f).transition().delay(100).style("fill", "black");
}
工作代码here
希望这有帮助!