使用d3树布局更新模型的最简单方法是什么。
这是一个例子 http://jsfiddle.net/mnk/vfro9tkz/
var data = {
name: 'Music',
children: [{
name: 'Rock',
children: [{
name: 'Two'
}, {
name: 'Three',
children: [{
name: 'A'
}, {
name: 'Bonjourallo'
}, {
name: 'Coco coco coco coco'
}]
}, {
name: 'Four'
}]
}, {
name: 'Rap',
children: [{
name: 'Hip-Hop/Rap'
}]
}]
};
var svg = d3.select('body').append('svg');
svg.attr('width', 700)
.attr('height', 400);
var tree = d3.layout.tree().size([600, 300]);
function update(model) {
var nodes = tree.nodes(model);
var links = tree.links(nodes);
nodes.forEach(function(d, i) {
d.index = d.parent ? d.parent.children.indexOf(d) : 0;
d.width = getNameLength(d.name);
if (!hasNephewOrChildren(d)) {
d.x = getHorizontalPosition(d)
d.y = (d.parent ? d.parent.y : 0) + 40;
d.mode = 'horizontal';
} else {
d.x = d.depth * 60;
d.y = getVerticalPosition(d);
d.mode = 'vertical';
}
});
var node = svg.selectAll('.node')
.data(nodes)
.enter()
.append('g').attr('class', 'node')
.attr('opacity', 1)
.attr('visibility', function(d) {
return d.depth ? 'visible' : 'hidden'
})
.attr('transform', function(d, i) {
return 'translate(' + d.x + ',' + d.y + ')'
});
var lineFunction = d3.svg.line()
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
})
.interpolate("linear");
var paths = svg.selectAll('g.node').append("path")
.attr("d", function(d) {
return lineFunction(generatePath(d));
})
.attr("stroke", "#aaa")
.attr("stroke-width", 1)
.attr("fill", "none");
function generatePath(d) {
var points = [];
if (d.depth > 1) {
if (d.mode === 'horizontal') {
points.push({
x: d.parent.x - d.x + d.parent.width,
y: -25
});
points.push({
x: d.width / 2,
y: -25
});
points.push({
x: d.width / 2,
y: 0
});
} else {
points.push({
x: d.parent.x - d.x + d.parent.width / 2,
y: d.parent.y - d.y + 30
});
points.push({
x: d.parent.x - d.x + d.parent.width / 2,
y: 15
});
points.push({
x: d.parent.x - d.x + d.parent.width / 2 + 15,
y: 15
});
}
}
return points;
}
node.append('rect')
.attr('class', 'rect')
.attr('width', function(d, i) {
return d.width
})
.attr('height', 30)
.attr('rx', 15)
node.append('text')
.text(function(d) {
return d.name
})
.attr('x', 10)
.attr('y', 20);
var close = node.append('g')
.attr('class', 'remove-icon-group')
.on('click', function(d) {
console.log('todo remove d and all childrens.');
// update(model);
})
.attr('transform', function(d, i) {
return 'translate(' + (d.width - 15) + ',15)'
});
close.append('circle')
.attr('class', 'remove-icon')
.attr('r', 10)
close.append('line')
.attr('x1', -4)
.attr('x2', 4)
.attr('y1', -4)
.attr('y2', 4)
.attr('stroke', '#a0a0a0')
.attr('stroke-width', 1);
close.append('line')
.attr('x1', 4)
.attr('x2', -4)
.attr('y1', -4)
.attr('y2', 4)
.attr('stroke', '#a0a0a0')
.attr('stroke-width', 1);
}
update(data);
function getLastDescendant(d) {
if (d.children && d.children.length) {
return getLastDescendant(d.children[d.children.length - 1]);
}
return d;
}
function hasNephewOrChildren(d) {
var siblings = d.parent ? d.parent.children : [d];
var hasChildren = false;
siblings.forEach(function(sibling) {
if (sibling.children && sibling.children.length) {
hasChildren = true;
}
});
return hasChildren;
}
function getHorizontalPosition(d) {
if (d.index === 0) {
return d.parent ? d.parent.x + 60 : 0;
}
var prevSibling = d.parent.children[d.index - 1];
return prevSibling.x + prevSibling.width + 10;
}
function getVerticalPosition(d) {
var prevY = (d.parent ? d.parent.y : -40);
if (d.index) {
var prevSibling = d.parent.children[d.index - 1];
var lastDescendant = getLastDescendant(prevSibling);
prevY = lastDescendant.y;
}
return prevY + 40;
}
function getNameLength(str) {
var length = str.length * 8;
return length < 60 ? 60 + 30 : length + 30;
}
答案 0 :(得分:3)
你非常接近。您已将所有图纸代码提取到update
,并且您已经注意到您需要再次调用它。您需要弄清楚如何修改模型以响应用户点击,然后使用新模型调用update
。
您遇到的事情是,当您再次呼叫update
时,某些DOM节点已经在屏幕上。也就是说,输入选择将为空,但更新选择不会。最简单,最丑陋的方法是删除并重新添加所有节点:
svg.selectAll('.node').remove();
svg.selectAll('.node')
.data(nodes)
.enter()
.append("g") // and so on
在General Update Pattern中解释了更好的方法(请务必查看全部三个)。您还应该阅读.enter() docs的最后一段。