如何解决如何在 d3.js 中反转图中节点的方向
所以我使用 d3.js 来显示节点图。现在父母从左边开始,孩子在右边。有什么办法可以反转那个方向,让孩子在左边,父母在右边。
下面是显示树节点的函数渲染树。例如,我将 renderTree 称为 vm.renderTree(vm.tree,"#tree-container");
vm.renderTree = function (treeData,treeId) {
var totalNodes = 0;
var maxLabelLength = 0;
var selectednode = null;
var draggingNode = null;
var panSpeed = 200;
var panBoundary = 20;
var i = 0;
var duration = 750;
var root;
var viewerWidth = $(document).width();
var viewerHeight = $(document).height();
vm.tree = d3.layout.tree()
.size([viewerHeight,viewerWidth]);
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.y,d.x];
});
function visit(parent,visitFn,childrenFn) {
if (!parent)
return;
visitFn(parent);
var children = childrenFn(parent);
if (children) {
var count = children.length;
for (var i = 0; i < count; i++) {
visit(children[i],childrenFn);
}
}
}
visit(treeData,function (d) {
totalNodes++;
if (typeof d.lename !== "undefined") {
if (treeId == "#tree-container-legalTree") {
maxLabelLength = Math.max(d.lename.length,maxLabelLength);
}
else {
maxLabelLength = Math.max(d.name.length,maxLabelLength);
}
}
},function (d) {
return d.children && d.children.length > 0 ? d.children : null;
});
var sortTree = function () {
vm.tree.sort(function (a,b) {
if (typeof d !== "undefined") {
if (treeId == "#tree-container-legalTree") {
return b.lename.toLowerCase() < a.lename.toLowerCase() ? 1 : -1;
}
else {
return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
}
}
});
};
sortTree();
var pan = function (domNode,direction) {
var speed = panSpeed;
if (panTimer) {
clearTimeout(panTimer);
var translateCoords = d3.transform(svgGroup.attr("transform"));
if (direction == 'left' || direction == 'right') {
translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0] - speed;
translateY = translateCoords.translate[1];
}
else if (direction == 'up' || direction == 'down') {
translateX = translateCoords.translate[0];
translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1] - speed;
}
scaleX = translateCoords.scale[0];
scaleY = translateCoords.scale[1];
scale = zoomListener.scale();
svgGroup.transition().attr("transform","translate(" + translateX + "," + translateY + ")scale(" + scale + ")");
d3.select(domNode).select('g.node').attr("transform"," + translateY + ")");
zoomListener.scale(zoomListener.scale());
zoomListener.translate([translateX,translateY]);
panTimer = setTimeout(function () {
pan(domNode,speed,direction);
},50);
}
};
function zoom() {
svgGroup.attr("transform","translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var zoomListener = d3.behavior.zoom().scaleExtent([0.1,3]).on("zoom",zoom);
var initiateDrag = function (d,domNode) {
draggingNode = d;
d3.select(domNode).select('.ghostCircle').attr('pointer-events','none');
d3.selectAll('.ghostCircle').attr('class','ghostCircle show');
d3.select(domNode).attr('class','node activeDrag');
svgGroup.selectAll("g.node").sort(function (a,b) {
if (a.id != draggingNode.id)
return 1;
else
return -1;
});
if (nodes.length > 1) {
links = vm.tree.links(nodes);
nodePaths = svgGroup.selectAll("path.link")
.data(links,function (d) {
return d.target.id;
}).remove();
nodesExit = svgGroup.selectAll("g.node")
.data(nodes,function (d) {
return d.id;
}).filter(function (d,i) {
if (d.id == draggingNode.id) {
return false;
}
return true;
}).remove();
}
parentLink = vm.tree.links(vm.tree.nodes(draggingNode.parent));
svgGroup.selectAll('path.link').filter(function (d,i) {
if (d.target.id == draggingNode.id) {
return true;
}
return false;
}).remove();
dragStarted = null;
};
var baseSvg = d3.select(treeId).append("svg")
.attr("width",viewerWidth)
.attr("height",viewerHeight)
.attr("class","overlay")
.call(zoomListener);
dragListener = d3.behavior.drag()
.on("dragstart",function (d) {
if (d == root) {
return;
}
dragStarted = true;
nodes = vm.tree.nodes(d);
d3.event.sourceEvent.stopPropagation();
})
.on("drag",function (d) {
if (d == root) {
return;
}
if (dragStarted) {
domNode = vm;
initiateDrag(d,domNode);
}
var relCoords = d3.mouse($('svg').get(0));
if (relCoords[0] < panBoundary) {
panTimer = true;
pan(vm,'left');
}
else if (relCoords[0] > ($('svg').width() - panBoundary)) {
panTimer = true;
pan(vm,'right');
}
else if (relCoords[1] < panBoundary) {
panTimer = true;
pan(vm,'up');
}
else if (relCoords[1] > ($('svg').height() - panBoundary)) {
panTimer = true;
pan(vm,'down');
}
else {
try {
clearTimeout(panTimer);
}
catch (e) {
}
}
d.x0 += d3.event.dy;
d.y0 += d3.event.dx;
var node = d3.select(vm);
node.attr("transform","translate(" + d.y0 + "," + d.x0 + ")");
updateTempConnector();
}).on("dragend",function (d) {
if (d == root) {
return;
}
domNode = vm;
if (selectednode) {
var index = draggingNode.parent.children.indexOf(draggingNode);
if (index > -1) {
draggingNode.parent.children.splice(index,1);
}
if (typeof selectednode.children !== 'undefined' || typeof selectednode._children !== 'undefined') {
if (typeof selectednode.children !== 'undefined') {
selectednode.children.push(draggingNode);
}
else {
selectednode._children.push(draggingNode);
}
}
else {
selectednode.children = [];
selectednode.children.push(draggingNode);
}
expand(selectednode);
sortTree();
endDrag();
}
else {
endDrag();
}
});
function endDrag() {
selectednode = null;
d3.selectAll('.ghostCircle').attr('class','ghostCircle');
d3.select(domNode).attr('class','node');
d3.select(domNode).select('.ghostCircle').attr('pointer-events','');
updateTempConnector();
if (draggingNode !== null) {
update(root);
centerNode(draggingNode);
draggingNode = null;
}
}
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
var overCircle = function (d) {
selectednode = d;
updateTempConnector();
};
var outCircle = function (d) {
selectednode = null;
updateTempConnector();
};
var updateTempConnector = function () {
var data = [];
if (draggingNode !== null && selectednode !== null) {
data = [{
source: {
x: selectednode.y0,y: selectednode.x0
},target: {
x: draggingNode.y0,y: draggingNode.x0
}
}];
}
var link = svgGroup.selectAll(".templink").data(data);
link.enter().append("path")
.attr("class","templink")
.attr("d",d3.svg.diagonal())
.attr('pointer-events','none');
link.attr("d",d3.svg.diagonal());
link.exit().remove();
};
function centerNode(source) {
scale = zoomListener.scale();
x = -source.y0;
y = -source.x0;
x = 150;
y = y * scale + viewerHeight / 2;
d3.select('g').transition()
.duration(duration)
.attr("transform","translate(" + x + "," + y + ")scale(" + scale + ")");
zoomListener.scale(scale);
zoomListener.translate([x,y]);
}
function toggleChildren(d) {
if (d.children) {
d._children = d.children;
d.children = null;
}
else if (d._children) {
d.children = d._children;
d._children = null;
}
return d;
}
function click(d) {
if (d3.event.defaultPrevented)
return;
d = toggleChildren(d);
update(d);
centerNode(d);
}
var update = function (source) {
var levelWidth = [1];
var childCount = function (level,n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= level + 1)
levelWidth.push(0);
levelWidth[level + 1] += n.children.length;
n.children.forEach(function (d) {
childCount(level + 1,d);
});
}
};
childCount(0,root);
var newHeight = d3.max(levelWidth) * 25;
vm.tree = vm.tree.size([newHeight,viewerWidth]);
var nodes = vm.tree.nodes(root).reverse(),links = vm.tree.links(nodes);
nodes.forEach(function (d) {
d.y = (d.depth * (maxLabelLength * 10));
});
node = svgGroup.selectAll("g.node")
.data(nodes,function (d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node.enter().append("g")
.call(dragListener)
.attr("class","node")
.attr("transform",function (d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on('click',click);
nodeEnter.append("circle")
.attr('class','nodeCircle')
.attr("r",0)
.style("fill",function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("x",function (d) {
return d.children || d._children ? -10 : 10;
})
.attr("dy",".35em")
.attr('class','nodeText')
.attr("text-anchor",function (d) {
return d.children || d._children ? "end" : "start";
})
.text(function (d) {
if (vm.showName == "LE Name") {
if (treeId == "#tree-container-legalTree") {
return d.lename;
}
return d.name;
}
})
.style("fill-opacity",0);
nodeEnter.append("circle")
.attr('class','ghostCircle')
.attr("r",30)
.attr("opacity",0.2)
.style("fill","red")
.attr('pointer-events','mouSEOver')
.on("mouSEOver",function (node) {
overCircle(node);
})
.on("mouSEOut",function (node) {
outCircle(node);
});
node.select('text')
.attr("x",function (d) {
return d.children || d._children ? -10 : 10;
})
.attr("text-anchor",function (d) {
return d.children || d._children ? "end" : "start";
})
.text(function (d) {
if (vm.showName == "LE Name") {
if (treeId == "#tree-container-legalTree") {
return d.lename;
}
return d.name;
}
});
node.select("circle.nodeCircle")
.attr("r",4.5)
.style("fill",function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform",function (d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("text")
.style("fill-opacity",1);
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform",function (d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r",0);
nodeExit.select("text")
.style("fill-opacity",0);
var link = svgGroup.selectAll("path.link")
.data(links,function (d) {
return d.target.id;
});
link.enter().insert("path","g")
.attr("class","link")
.attr("d",function (d) {
var o = {
x: source.x0,y: source.y0
};
return diagonal({
source: o,target: o
});
});
link.transition()
.duration(duration)
.attr("d",diagonal);
link.exit().transition()
.duration(duration)
.attr("d",function (d) {
var o = {
x: source.x,y: source.y
};
return diagonal({
source: o,target: o
});
})
.remove();
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
};
var svgGroup = baseSvg.append("g");
root = treeData;
root.x0 = viewerHeight / 2;
root.y0 = 0;
update(root);
centerNode(root);
};
以下是treeData的例子
{
"id": 1,"code": "a","name": "b","type": "t","leId": 2,"leName": "d","children": [
{
"id": 2,"code": "e","name": "f","type": "g","leId": 4,"lename": "e","childrenCount": 0
}
],"childrenCount": 1
}
解决方法
这是一个反向树的片段。
用 d.y
替换 height - d.y
,交换文本位置 (start
/ end
) 并修改对链接的 diagonal
调用:
var data = [
{ "name" : "Level 2: A","parent":"Top Level" },{ "name" : "Top Level","parent":"null" },{ "name" : "Son of A","parent":"Level 2: A" },{ "name" : "Daughter of A",{ "name" : "Level 2: B","parent":"Top Level" }
];
// *********** Convert flat data into a nice tree ***************
// create a name: node map
var dataMap = data.reduce(function(map,node) {
map[node.name] = node;
return map;
},{});
// create the tree array
var treeData = [];
data.forEach(function(node) {
// add to parent
var parent = dataMap[node.parent];
if (parent) {
// create child array if it doesn't exist
(parent.children || (parent.children = []))
// add node to child array
.push(node);
} else {
// parent is null or missing
treeData.push(node);
}
});
// ************** Generate the tree diagram *****************
var margin = {top: 20,right: 120,bottom: 20,left: 120},width = 960 - margin.right - margin.left,height = 500 - margin.top - margin.bottom;
var i = 0;
var tree = d3.layout.tree()
.size([height,width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y,d.x]; });
var svg = d3.select("body").append("svg")
.attr("width",width + margin.right + margin.left)
.attr("height",height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
update(root);
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Declare the nodes…
var node = svg.selectAll("g.node")
.data(nodes,function(d) { return d.id || (d.id = ++i); });
// Enter the nodes.
var nodeEnter = node.enter().append("g")
.attr("class","node")
.attr("transform",function(d) {
return "translate(" + (height - d.y) + "," + d.x + ")"; });
nodeEnter.append("circle")
.attr("r",10)
.style("fill","#fff");
nodeEnter.append("text")
.attr("x",function(d) {
return d.children || d._children ? 13 : -13; })
.attr("dy",".35em")
.attr("text-anchor",function(d) {
return d.children || d._children ? "start" : "end"; })
.text(function(d) { return d.name; })
.style("fill-opacity",1);
// Declare the links…
var link = svg.selectAll("path.link")
.data(links,function(d) { return d.target.id; });
// Enter the links.
link.enter().insert("path","g")
.attr("class","link")
.attr("d",d => {
console.log(d);
const source = {x: d.source.x,y: height - d.source.y};
const target = {x: d.target.x,y: height - d.target.y};
return diagonal({source,target});
//diagonal({x: d.x,y: height - d.y})
});
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text { font: 12px sans-serif; }
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.7/d3.min.js"></script>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。