我一直试图解决这个图形问题已经持续了几个星期......对于D3.js来说还是一个新手,所以看起来他们可能很简单的事情仍然无法解决。
以下是我正在尝试做的事情的说明:
目标:
我想展示行业节点/大小节点和行业节点之间的关系 产品节点。
当我悬停Product节点时,我想突出显示 每个的链接,来源(行业或规模)和目标(产品) 相关关系。
当我悬停行业或规模节点时,我想突出显示所有相关产品的链接。
问题
如何绘制链接?我知道它以某种方式涉及使用d3.map ...但无法弄明白。
如何突出显示节点和链接(目标2和3)?
如果有更好,更有效的方法来获得这种布局和行为,请告诉我 - 努力学习技巧!
小提琴从简化的数据集渲染基本布局: http://jsfiddle.net/9hGbD/
数据目前看起来像这样:
var data = {
"Product": [
{
"type": "product",
"name": "Product 1"
},
{
"type": "product",
"name": "Product 2"
},
{
"type": "product",
"name": "Product 3"
},
{
"type": "product",
"name": "Product 4"
},
{
"type": "product",
"name": "Product 5"
}
],
"Industry": [
{
"type": "industry",
"name": "Industry 1"
},
{
"type": "industry",
"name": "Industry 2"
},
{
"type": "industry",
"name": "Industry 3"
},
{
"type": "industry",
"name": "Industry 4"
},
{
"type": "industry",
"name": "Industry 5"
}
],
"Size": [
{
"type": "size",
"name": "Size 1"
},
{
"type": "size",
"name": "Size 2"
},
{
"type": "size",
"name": "Size 3"
},
{
"type": "size",
"name": "Size 4"
},
{
"type": "size",
"name": "Size 5"
}
],
"links": [
{
"source": "Industry 1",
"target": "Product 1"
},
{
"source": "Industry 3",
"target": "Product 1"
},
{
"source": "Industry 5",
"target": "Product 1"
},
{
"source": "Industry 2",
"target": "Product 2"
},
...etc..
]
};
我正在使用的javascript 看起来像这样:
function renderRelationshipGraph(){
var width = 800,
boxWidth = 200,
boxHeight = 20,
gap = 4,
margin = {top: 16, right: 16, bottom: 16, left: 16},
height = (data.Product.length * (boxHeight + gap)) + margin.top + margin.bottom;
var pNodes = [];
var iNodes = [];
var sNodes = [];
var links = [];
data.Product.forEach(function(d, i) {
d.x = ((width-margin.left-margin.right)/3)/2 - boxWidth/2;
d.y = margin.top + (boxHeight+ 4)*i;
pNodes.push(d);
});
data.Industry.forEach(function(d, i) {
d.x = 0;
d.y = margin.top + (boxHeight+ 4)*i;
iNodes.push(d);
});
data.Size.forEach(function(d, i) {
d.x = ((width-margin.left-margin.right)/3) - boxWidth;
d.y = margin.top + (boxHeight+ 4)*i;
sNodes.push(d);
});
var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
svg.append("g")
.attr("class", "industries");
svg.append("g")
.attr("class", "products")
.attr("transform", "translate("+ (width-margin.left-margin.right)/3 + ", 0)");
svg.append("g")
.attr("class", "sizes")
.attr("transform", "translate("+ 2*((width-margin.left-margin.right)/3) + ", 0)");
var products = svg.select(".products");
var product = products.selectAll("g")
.data(pNodes)
.enter()
.append("g")
.attr("class", "unit");
product.append("rect")
.attr("x", function(d) {return d.x;})
.attr("y", function(d) {return d.y;})
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "product")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function() { d3.select(this).classed("active", true); })
.on("mouseout", function() { d3.select(this).classed("active", false); });
product.append("text")
.attr("class", "label")
.attr("x", function(d) {return d.x + 14;})
.attr("y", function(d) {return d.y + 15;})
.text(function(d) {return d.name;});
var industries = svg.select(".industries");
var industry = industries.selectAll("g")
.data(iNodes)
.enter()
.append("g")
.attr("class", "unit");
industry.append("rect")
.attr("x", function(d) {return d.x;})
.attr("y", function(d) {return d.y;})
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "industry")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function() { d3.select(this).classed("active", true); })
.on("mouseout", function() { d3.select(this).classed("active", false); });
industry.append("text")
.attr("class", "label")
.attr("x", function(d) {return d.x + 14;})
.attr("y", function(d) {return d.y + 15;})
.text(function(d) {return d.name;});
var sizes = svg.select(".sizes");
var size = sizes.selectAll("g")
.data(sNodes)
.enter()
.append("g")
.attr("class", "unit");
size.append("rect")
.attr("x", function(d) {return d.x;})
.attr("y", function(d) {return d.y;})
.attr("width", boxWidth)
.attr("height", boxHeight)
.attr("class", "size")
.attr("rx", 6)
.attr("ry", 6)
.on("mouseover", function() { d3.select(this).classed("active", true); })
.on("mouseout", function() { d3.select(this).classed("active", false); });
size.append("text")
.attr("class", "label")
.attr("x", function(d) {return d.x + 14;})
.attr("y", function(d) {return d.y + 15;})
.text(function(d) {return d.name;});
}
renderRelationshipGraph();
感谢您的帮助!
答案 0 :(得分:1)
好的,所以我试着让这个例子起作用,因为我遇到了同样的问题。为了后代,我也在这里回答。也许有人会遇到同样的问题或找到一个更简单的解决方案 我是javascript的新手(过去一周试图学习一点)所以这可能不是最好的解决方案。 jsfiddle中提供了文件和结果。我不会提供整个代码,只会指出更改。
为了能够添加任意数量的列,我更改了json
文件,并将type
替换为级别lvl
。我还将Industriy
,Product
和size
替换为名为Nodes
的类别。
javascript文件中创建的svg
中的每个矩形和线都使用id
进行设置,以便稍后可以引用它(更改颜色时)。
相关代码是
data.Nodes.forEach(function (d, i) {
d.x = margin.left + d.lvl * (boxWidth + gap.width);
d.y = margin.top + (boxHeight + gap.height) * count[d.lvl];
d.id = "n" + i;
count[d.lvl] += 1;
Nodes.push(d);
});
data.links.forEach(function (d) {
links.push({
source: find(d.source),
target: find(d.target),
id: "l" + find(d.source).id + find(d.target).id
});
});
此处使用的方法find
是一个函数,用于查找传递给它的名称的节点。
mouseover
和mouseout
事件更新如下:
.on("mouseover", function () {
mouse_action(d3.select(this).datum(), true, true);
})
.on("mouseout", function () {
mouse_action(d3.select(this).datum(), false, true);
});
它使用一种方法mouse_action
,它接受起始节点及其将处于的状态(活动或非活动)。在此方法中,我们访问每个链接,处理它并更改其状态。该方法在鼠标输入的节点上以两个方向(左和右)遍历,并且仅在其他节点上向左或向右遍历。
function mouse_action(val, stat, direction) {
"use strict";
d3.select("#" + val.id).classed("active", stat);
links.forEach(function (d) {
if (direction == "root") {
if (d.source.id === val.id) {
d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
if (d.target.lvl < val.lvl)
mouse_action(d.target, stat, "left");
else if (d.target.lvl > val.lvl)
mouse_action(d.target, stat, "right");
}
if (d.target.id === val.id) {
d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
if (direction == "root") {
if(d.source.lvl < val.lvl)
mouse_action(d.source, stat, "left");
else if (d.source.lvl > val.lvl)
mouse_action(d.source, stat, "right");
}
}
}else if (direction == "left") {
if (d.source.id === val.id && d.target.lvl < val.lvl) {
d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
mouse_action(d.target, stat, direction);
}
if (d.target.id === val.id && d.source.lvl < val.lvl) {
d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
mouse_action(d.source, stat, direction);
}
}else if (direction == "right") {
if (d.source.id === val.id && d.target.lvl > val.lvl) {
d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
mouse_action(d.target, stat, direction);
}
if (d.target.id === val.id && d.source.lvl > val.lvl) {
d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
mouse_action(d.source, stat, direction);
}
}
});
}