我正在尝试在D3.js版本4而不是版本3下实现Directed Graph Editor,并且还实现缩放和平移。我也试图将它作为聚合物组件实现。
我在下面的github存储库https://github.com/powerxhub/emh-d3-force-directed-graph.git中实现了这个。
我有缩放功能,但节点和链接的选择不起作用。我错过了什么?
组件的主体如下:
<script>
var nodeEnter;
var nodeExit;
var linkEnter;
var g;
var color;
Polymer({
is: 'emh-d3-force-directed-graph',
properties: {
graph: {
type: Object,
notify: true,
value: { nodes: [
{id: 0, reflexive: false, group: 0 },
{id: 1, reflexive: true, group: 1 },
{id: 2, reflexive: false, group: 0 }
], links: [{ source:0, target: 1 }, { source:1, target: 2 }, { source:2, target: 0 }] },
observer: '_graphChanged'
},
selected_node: { type: Object, notify: true },
mousedown_node: { type: Object },
selected_link: { type: Object, notify: true },
mousedown_link: { type: Object },
mouseup_link: { type: Object }
},
created: function() {
console.log(this.localName + '#' + this.id + ' was created');
},
ready: function() {
console.log(this.localName + '#' + this.id + ' has local DOM initialized');
},
attached: function() {
console.log(this.localName + '#' + this.id + ' was attached');
var aa = this.$.polymerForce.getBoundingClientRect();
var svg_height = aa.height;
var svg_width = aa.width;
g = d3.select(this.$.polymerForce)
.append('g')
.style("pointer-events", "all")
.on("mousemove", this.mousemove)
.on("mousedown", this.mousedown)
.on("mouseup", this.mouseup)
;
d3.select(this.$.polymerForce).append("rect")
.attr("width", svg_width)
.attr("height", svg_height)
.style("fill", "none")
.style("pointer-events", "all")
.call(d3.zoom()
.on("zoom", zoomed));
function zoomed() {
g.attr("transform", d3.event.transform);
}
color = d3.scaleOrdinal(d3.schemeCategory20);
this.force = d3.forceSimulation(this.graph.nodes)
.force("link", d3.forceLink(this.graph.links).id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(svg_width / 2, svg_height / 2));
if (this.force) {
this.redraw();
}
},
detached: function() {
console.log(this.localName + '#' + this.id + ' was detached');
},
_graphChanged: function() {
console.log("foo");
if (this.force) {
redraw();
}
},
mousedown: function() {
if (!this.mousedown_node && !this.mousedown_link) {
// allow panning if nothing is selected
g.call(d3.zoom().on("zoom"), rescale);
return;
}
},
mousemove: function() {
if (!this.mousedown_node) return;
// update drag line
this.drag_line
.attr("x1", this.mousedown_node.x)
.attr("y1", this.mousedown_node.y)
.attr("x2", d3.svg.mouse(this)[0])
.attr("y2", d3.svg.mouse(this)[1]);
},
mouseup: function() {
if (this.mousedown_node) {
// hide drag line
this.drag_line
.attr("class", "drag_line_hidden")
if (!this.mouseup_node) {
// add node
var point = d3.mouse(this),
nodeEnter = {x: point[0], y: point[1]},
n = this.graph.nodes.push(nodeEnter);
// select new node
this.selected_node = nodeEnter;
this.selected_link = null;
// add link to mousedown node
this.graph.links.push({source: mousedown_node, target: nodeEnter});
}
redraw();
}
// clear mouse event vars
this.mousedown_node = null;
this.mouseup_node = null;
this.mousedown_link = null;
//this.resetMouseVars();
},
resetMouseVars: function() {
this.mousedown_node = null;
this.mouseup_node = null;
this.mousedown_link = null;
},
tick: function() {
linkEnter.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
nodeEnter.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
},
// rescale g
rescale: function() {
trans=d3.event.translate;
scale=d3.event.scale;
g.attr("transform",
"translate(" + trans + ")"
+ " scale(" + scale + ")");
},
// redraw force layout
redraw: function() {
linkEnter = g.selectAll(".link")
.data(this.graph.links).enter()
.append("line")
.attr("class", "link")
.attr("stroke", "#666")
.attr("stroke-width", "5")
.style("pointer-events", "all")
.on("mousedown",
function(d) {
this.mousedown_link = d;
if (this.mousedown_link == this.selected_link)
this.selected_link = null;
else
this.selected_link = this.mousedown_link;
this.selected_node = null;
this.redraw();
}
)
;
linkEnter
.classed("link_selected", function(d) { return d === this.selected_link; });
linkExit = g.selectAll(".link")
.data(this.graph.links).exit().remove();
nodeEnter = g.selectAll(".node")
.data(this.graph.nodes)
.enter().append("circle")
.attr("fill", function(d) { return color(d.group); })
.attr("r", 5)
.attr("class", "node")
.style("pointer-events", "all")
.on("mousedown",
function(d) {
// disable zoom
//g.call(d3.behavior.zoom().on("zoom"), null);
this.mousedown_node = d;
if (this.mousedown_node == this.selected_node)
this.selected_node = null;
else
this.selected_node = this.mousedown_node;
this.selected_link = null;
// reposition drag line
this.drag_line
.attr("class", "link")
.attr("x1", mousedown_node.x)
.attr("y1", mousedown_node.y)
.attr("x2", mousedown_node.x)
.attr("y2", mousedown_node.y);
redraw();
}
)
.on("mousedrag",
function(d) {
// redraw();
}
)
.on("mouseup",
function(d) {
if (this.mousedown_node) {
this.mouseup_node = d;
if (this.mouseup_node == this.mousedown_node) { resetMouseVars(); return; }
// add link
var link = {source: mousedown_node, target: mouseup_node};
this.graph.links.push(link);
// select new link
this.selected_link = link;
this.selected_node = null;
// enable zoom
g.call(d3.zoom().on("zoom"), this.rescale);
redraw();
}
}
)
;
nodeExit = g.selectAll(".node")
.data(this.graph.nodes)
.exit().transition()
.attr("r", 0)
.remove();
if (d3.event) {
// prevent browser's default behavior
d3.event.preventDefault();
}
this.force
.nodes(this.graph.nodes)
.on("tick", this.tick);
},
spliceLinksForNode: function(node) {
toSplice = this.graph.links.filter(
function(l) {
return (l.source === node) || (l.target === node);
}
);
toSplice.map(
function(l) {
this.graph.links.splice(this.graph.links.indexOf(l), 1);
}
);
},
keydown: function() {
if (!this.selected_node && !this.selected_link) return;
switch (d3.event.keyCode) {
case 8: // backspace
case 46: { // delete
if (this.selected_node) {
this.graph.nodes.splice(this.graph.nodes.indexOf(this.selected_node), 1);
spliceLinksForNode(this.selected_node);
}
else if (this.selected_link) {
this.graph.links.splice(this.graph.links.indexOf(this.selected_link), 1);
}
this.selected_link = null;
this.selected_node = null;
redraw();
break;
}
}
}
});
答案 0 :(得分:0)
瞥了一眼你的代码,我的预感是你为this
混合上下文。在您的所有d3鼠标悬停回调函数中,您尝试调用聚合物属性和方法,但您的上下文已更改,因此this.selected_node = null
等实际上并未引用您的Polymer属性。要解决此问题,您可以将Polymer this
绑定到回调函数:
.on("mousedown", function(d) {
//your callback code
}.bind(this) );
现在,在您的回调中调用this
将引用聚合物this
。