我正在使用 D3 v4 构建一棵树。
fiddle : https://jsfiddle.net/a6pLqpxw/
我现在正在尝试添加对从选定节点动态添加(和删除)子节点的支持。
但是,如果不执行完全重绘,我无法让图表重绘。我修改了可折叠 TreeMap 代码中的代码:https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd
具体来说,以下 block 不会为其子项重新计算布局。
document.getElementById('add-child').onclick = function() {
console.log(selected);
selected.children.push({
type: 'resource-delete',
name: new Date().getTime(),
attributes: [],
children: []
});
update(selected);
};
有没有人有任何在 D3.js v4 中动态添加/删除节点到树的好例子?
最佳答案
我想出了这个Adding new Node dynamically to D3 Tree v4. 的解决方案。
D3 v4 树需要节点。
使用 d3.hierarchy(..) 从您的树数据 (json) 创建节点并将其推送到它的 parent.children 数组中并更新树。
代码片段
//Adding a new node (as a child) to selected Node (code snippet)
var newNode = {
type: 'node-type',
name: new Date().getTime(),
children: []
};
//Creates a Node from newNode object using d3.hierarchy(.)
var newNode = d3.hierarchy(newNode);
//later added some properties to Node like child,parent,depth
newNode.depth = selected.depth + 1;
newNode.height = selected.height - 1;
newNode.parent = selected;
newNode.id = Date.now();
//Selected is a node, to which we are adding the new node as a child
//If no child array, create an empty array
if(!selected.children){
selected.children = [];
selected.data.children = [];
}
//Push it to parent.children array
selected.children.push(newNode);
selected.data.children.push(newNode.data);
//Update tree
update(selected);
// ### DATA MODEL START
var data = {
type: 'action',
name: '1',
attributes: [],
children: [{
type: 'children',
name: '2',
attributes: [{
'source-type-property-value': 'streetlight'
}],
children: [{
type: 'parents',
name: '3',
attributes: [{
'source-type-property-value': 'cable'
}],
children: [{
type: 'resource-delete',
name: '4',
attributes: [],
children: []
}]
}, {
type: 'children',
name: '5',
attributes: [{
'source-type-property-value': 'lantern'
}],
children: []
}]
}]
};
// ### DATA MODEL END
// Set the dimensions and margins of the diagram
var margin = {top: 20, right: 90, bottom: 30, left: 90},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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 + ")");
var i = 0, duration = 750, root;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
// Assigns parent, children, height, depth
root = d3.hierarchy(data, function(d) {
return d.children;
});
root.x0 = height / 2;
root.y0 = 0;
update(root);
var selected = null;
function update(source) {
// Assigns the x and y position for the nodes
var treeData = treemap(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function(d){
d.y = d.depth * 180
});
// ### LINKS
// Update the links...
var link = svg.selectAll('line.link').
data(links, function(d) {
return d.id;
});
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().
append('line').
attr("class", "link").
attr("stroke-width", 2).
attr("stroke", 'black').
attr('x1', function(d) {
return source.y0;
}).
attr('y1', function(d) {
return source.x0;
}).
attr('x2', function(d) {
return source.y0;
}).
attr('y2', function(d) {
return source.x0;
});
var linkUpdate = linkEnter.merge(link);
linkUpdate.transition().
duration(duration).
attr('x1', function(d) {
return d.parent.y;
}).
attr('y1', function(d) {
return d.parent.x;
}).
attr('x2', function(d) {
return d.y;
}).
attr('y2', function(d) {
return d.x;
});
// Transition back to the parent element position
linkUpdate.transition().
duration(duration).
attr('x1', function(d) {
return d.parent.y;
}).
attr('y1', function(d) {
return d.parent.x;
}).
attr('x2', function(d) {
return d.y;
}).
attr('y2', function(d) {
return d.x;
});
// Remove any exiting links
var linkExit = link.exit().
transition().
duration(duration).
attr('x1', function(d) {
return source.x;
}).
attr('y1', function(d) {
return source.y;
}).
attr('x2', function(d) {
return source.x;
}).
attr('y2', function(d) {
return source.y;
}).
remove();
// ### CIRCLES
// Update the nodes...
var node = svg.selectAll('g.node')
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new modes at the parent's previous position.
var nodeEnter = node.enter().
append('g').
attr('class', 'node').
attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
}).
on('click', click);
// Add Circle for the nodes
nodeEnter.append('circle').
attr('class', 'node').
attr('r', 25).
style("fill", function(d) {
return "#0e4677";
});
// Update
var nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition().
duration(duration).
attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
// Update the node attributes and style
nodeUpdate.select('circle.node').
attr('r', 25).
style("fill", function(d) {
return "#0e4677";
}).
attr('cursor', 'pointer');
// Remove any exiting nodes
var nodeExit = node.exit().
transition().
duration(duration).
attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
}).
remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle').attr('r', 0);
// Store the old positions for transition.
nodes.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
// Toggle children on click.
function click(d) {
selected = d;
document.getElementById('add-child').disabled = false;
document.getElementById('remove').disabled = false;
update(d);
}
}
document.getElementById('add-child').onclick = function() {
//creates New OBJECT
var newNodeObj = {
type: 'resource-delete',
name: new Date().getTime(),
attributes: [],
children: []
};
//Creates new Node
var newNode = d3.hierarchy(newNodeObj);
newNode.depth = selected.depth + 1;
newNode.height = selected.height - 1;
newNode.parent = selected;
newNode.id = Date.now();
if(!selected.children){
selected.children = [];
selected.data.children = [];
}
selected.children.push(newNode);
selected.data.children.push(newNode.data);
update(selected);
};
<script src="https://d3js.org/d3.v4.min.js"></script>
<button id="add-child" disabled="disabled">Add Child</button>
关于javascript - 将节点添加到 D3 树 v4,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43140325/
当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/
我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它
我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以
当谈到运行时自省(introspection)和动态代码生成时,我认为ruby没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资
我注意到类定义,如果我打开classMyClass,并在不覆盖的情况下添加一些东西我仍然得到了之前定义的原始方法。添加的新语句扩充了现有语句。但是对于方法定义,我仍然想要与类定义相同的行为,但是当我打开defmy_method时似乎,def中的现有语句和end被覆盖了,我需要重写一遍。那么有什么方法可以使方法定义的行为与定义相同,类似于super,但不一定是子类? 最佳答案 我想您正在寻找alias_method:classAalias_method:old_func,:funcdeffuncold_func#similartoca
我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
我正在开发一个创建网络博客的RubyonRails项目。我希望将一个名为featured的boolean数据库字段添加到Post模型中。该字段应该可以通过我添加的事件管理界面进行编辑。我使用了以下代码,但我什至没有在网站上显示另一列。$railsgeneratemigrationaddFeaturedfeatured:boolean$rakedb:migrate我是RubyonRails的新手,非常感谢任何帮助。我的index.html.erb文件中的相关代码(views):FeaturedPost架构.rb:ActiveRecord::Schema.define(:version=>
假设我有一个这样的单例类:classSettingsincludeSingletondeftimeout#lazy-loadtimeoutfromconfigfile,orwhateverendend现在,如果我想知道使用什么超时,我需要编写如下内容:Settings.instance.timeout但我宁愿将其缩短为Settings.timeout使这项工作有效的一个明显方法是将设置的实现修改为:classSettingsincludeSingletondefself.timeoutinstance.timeoutenddeftimeout#lazy-loadtimeoutfromc
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion