我认为问题很简单。我有这个网站可以让你理解我的意思: http://arda-maps.org/familytree/ 因此,如果您在屏幕上添加一些人,您就可以拖动和缩放视图。
缩放完全没问题。缩放的持续时间很长。但我不喜欢拖动的持续时间,并希望在那里禁用它。基本上这是代码:
g.transition().duration(450).attr("transform", "translate(" + zoombuttonTranslate + ")scale(" + zoombuttonScale + ")");
所以问题是如何在拖动事件中禁用转换/持续时间?这有可能吗?
答案 0 :(得分:2)
我认为这很接近...... 只需要验证它对节点上的拖动行为会很好。
d3.event.sourceEvent.type
检查mousemove d3.transform
var width = 600, height = 200-16,
margin = {top: 25, right: 5, bottom: 5, left: 5},
w = width - margin.left - margin.right,
h = height - margin.top - margin.bottom,
zoom = d3.behavior.zoom().scaleExtent([0.4, 4])
.on("zoom", zoomed),
svg = d3.select("#viz").attr({width: width, height: height})
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom),
transText = svg.append("text")
.text("transform = translate ( margin.left , margin.top )")
.style("fill", "#5c5c5c")
.attr("dy", "-.35em")
surface = svg.append("rect")
.attr({width: w, height: h})
.style({"pointer-events": "all", fill: "#ccc", "stroke-width": 3, "stroke": "#fff"}),
surfaceText = svg.append("text")
.text("pointer-events: all")
.style("fill", "#5c5c5c")
.attr({"dy": "1em", "dx": ".2em"})
content = svg.append("g").attr("id", "content")
.attr("transform", "translate(0,0)"),
contentText = content.append("text")
.text("transform = translate ( d3.event.translate ) scale ( d3.event.scale )")
.style("fill", "#5c5c5c")
.attr({"dy": 50, "dx": 20})
content.selectAll("rect")
.data([[20,60],[140,60]])
.enter().append("rect")
.attr({height: 50, width: 50})
.style({"stroke-width": 3, "stroke": "#ccc"})
.each(function(d){
d3.select(this).attr({x: d[0], y: d[1]});
});
function zoomStart(){
}
function zoomed(){
return d3.event.sourceEvent.buttons ? zoomDrag.call(this) : zoomScale.call(this)
}
function zoomDrag(){
var t = d3.transform(content.attr("transform"));
t.translate = d3.event.translate;
content.attr("transform", t.toString());
}
function zoomScale(){
var t = d3.transform(content.attr("transform"));
t.translate = d3.event.translate; t.scale = d3.event.scale;
content.transition().duration(450).attr("transform", t.toString());
}

svg {
outline: 1px solid #282f51;
pointer-events: all;
}
g {
outline: 1px solid red;
shape-rednering: "geometricPrecision";
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="viz"></svg>
&#13;
由于FDG必须位于画布容器内,因此必须停止节点级事件传播到画布。这是在OP代码中通过使用自定义拖动行为,在 dragstart 上停止传播并添加一些force.drag
行为(加上设置d.fixed
= true {{1 force.drag ). This is great if you don't mind losing some of the
force.drag`行为。
features like sticking nodes on mouseover. This is nice for capturing small, energetic nodes though. So, in order to get the best of both worlds, you can hook the
添加到节点force.drag
以添加自定义行为最后两点允许在需要时轻松释放固定节点。
force.drag
//hook force.drag behaviour
var stdDragStart = force.drag().on("dragstart.force");
force.drag()
.on("dragstart", function(d){
//prevent dragging on the nodes from dragging the canvas
d3.event.sourceEvent.stopPropagation();
stdDragStart.call(this, d);
});
&#13;
//debug panel/////////////////////////////////////////////////////////////////////////////
var alpha = d3.select("#alpha").text("waiting..."),
cog = d3.select("#wrapAlpha").insert("i", "#fdg").classed("fa fa-cog fa-spin", true).datum({instID: null}),
fdgInst = d3.select("#fdg");
elapsedTime = ElapsedTime("#panel", {margin: 0, padding: 0})
.message(function (id) {
return 'fps : ' + d3.format(" >8.3f")(1/this.aveLap())
});
elapsedTime.consoleOn = true;
alpha.log = function(e, instID) {
elapsedTime.mark().timestamp();
alpha.text(d3.format(" >8.4f")(e.alpha));
fdgInst.text("fdg instance: " + instID);
};
d3.select("#update").on("click", (function() {
var dataSet = false;
return function() {
//fdg.force.stop();
fdg(dataSets[(dataSet = !dataSet, +dataSet)])
}
})());
//////////////////////////////////////////////////////////////////////////////////////////
var dataSets = [{
"nodes" : [
{"name": "node1", "r": 10},
{"name": "node2", "r": 10},
{"name": "node3", "r": 30},
{"name": "node4", "r": 15}
],
"edges": [
{"source": 2, "target": 0},
{"source": 2, "target": 1},
{"source": 2, "target": 3}
]
},
{
"nodes":[
{"name": "node1", "r": 20},
{"name": "node2", "r": 10},
{"name": "node3", "r": 30},
{"name": "node4", "r": 15},
{"name": "node5", "r": 10},
{"name": "node6", "r": 10}
],
"edges":[
{"source": 2, "target": 0},
{"source": 2, "target": 1},
{"source": 2, "target": 3},
{"source": 2, "target": 4},
{"source": 2, "target": 5}
]
}
],
svg = SVG({width: 600, height: 200-34, margin: {top: 25, right: 5, bottom: 5, left: 5}}, "#viz"),
fdg = FDG(svg, alpha.log);
fdg(dataSets[0]);
function SVG (size, selector){
//delivers an svg background with zoom/drag context in the selector element
//if height or width is NaN, assume it is a valid length but ignore margin
var margin = size.margin || {top: 0, right: 0, bottom: 0, left: 0},
unitW = isNaN(size.width), unitH = isNaN(size.height),
w = unitW ? size.width : size.width - margin.left - margin.right,
h = unitH ? size.height : size.height - margin.top - margin.bottom,
zoomed = function(){return this},
zoom = d3.behavior.zoom().scaleExtent([0.4, 4])
.on("zoom", function(d, i, j){
zoomed.call(this, d, i, j);
}),
svg = d3.select(selector).selectAll("svg").data([["transform root"]]);
svg.enter().append("svg");
svg.attr({width: size.width, height: size.height});
var g = svg.selectAll("#zoom").data(id),
gEnter = g.enter().append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom)
.attr({class: "outline", id: "zoom"}),
zoomText = gEnter.append("text")
.text("transform = translate ( margin.left , margin.top )")
.style("fill", "#5c5c5c")
.attr("dy", "-.35em"),
surface = gEnter.append("rect")
.attr({width: w, height: h})
.style({"pointer-events": "all", fill: "#ccc", "stroke-width": 3, "stroke": "#fff"}),
surfaceText = gEnter.append("text")
.text("pointer-events: none")
.style("fill", "#5c5c5c")
.attr({"dy": "1em", "dx": ".2em"});
g.h = h;
g.w = w;
g.onZoom = function(cb){zoomed = cb;};
return g;
}
function FDG (svg, tickLog) {
var instID = Date.now();
force = d3.layout.force()
.size([svg.w, svg.h])
.charge(-1000)
.linkDistance(50)
.on("end", function(){
// manage dead instances of force
// only stop if this instance is the current owner
if(cog.datum().instID != instID) return true;
cog.classed("fa-spin", false);
elapsedTime.stop();
})
.on("start", function(){
// mark as active and brand the insID to establish ownership
cog.classed("fa-spin", true).datum().instID = instID;
elapsedTime.start();
});
function fdg(data) {
force
.nodes(data.nodes)
.links(data.edges)
.on("tick", (function(instID) {
return function(e) {
if(tickLog) tickLog.call(this, e, instID);
lines.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;
});
node.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")"
});
}
})(instID))
.start();
svg.onZoom(zoomed);
hookDrag(force.drag(), "dragstart.force", function(d) {
// prevent dragging on the nodes from dragging the canvas
var e = d3.event.sourceEvent;
e.stopPropagation();
d.fixed = e.shiftKey || e.touches && (e.touches.length > 1);
});
hookDrag(force.drag(), "dragend.force", function(d) {
// prevent dragging on the nodes from dragging the canvas
var e = d3.event.sourceEvent;
d.fixed = e.shiftKey || d.fixed;
});
var content = svg.selectAll("g#fdg").data([data]);
content.enter().append("g").attr({"id": "fdg", class: "outline"});
var contentText = content.selectAll(".contentText")
.data(["transform = translate ( d3.event.translate ) scale ( d3.event.scale )"])
.enter().append("text").classed("contentText", true)
.text(id)
.style("fill", "#5c5c5c")
.attr({"dy": 20, "dx": 20});
var lines = content.selectAll(".links")
.data(linksData),
linesEnter = lines.enter()
.insert("line", d3.select("#nodes") ? "#nodes" : null)
.attr("class", "links")
.attr({stroke: "steelblue", "stroke-width": 3});
var nodes = content.selectAll("#nodes")
.data(nodesData),
nodesEnter = nodes.enter().append("g")
.attr("id", "nodes"),
node = nodes.selectAll(".node")
.data(id),
newNode = node.enter().append("g")
.attr("class", "node")
.call(force.drag),
circles = newNode.append("circle")
.attr({class: "content"})
.attr("r", function(d) {return d.r})
.style({"fill": "red", opacity: 0.8});
lines.exit().remove();
node.exit().remove();
function nodesData(d) {
return [d.nodes];
}
function linksData(d) {
return d.edges;
}
function hookDrag(target, event, hook) {
//hook force.drag behaviour
var stdDragStart = target.on(event);
target.on(event, function(d) {
hook.call(this, d);
stdDragStart.call(this, d);
});
}
function zoomed(){
var e = d3.event.sourceEvent,
isWheel = e && ((e.type == "mousewheel") || (e.type == "wheel"));
force.alpha(0.01);
return isWheel ? zoomWheel.call(this) : zoomInst.call(this)
}
function zoomInst(){
var t = d3.transform(content.attr("transform"));
t.translate = d3.event.translate; t.scale = d3.event.scale;
content.attr("transform", t.toString());
}
function zoomWheel(){
var t = d3.transform(content.attr("transform"));
t.translate = d3.event.translate; t.scale = d3.event.scale;
content.transition().duration(450).attr("transform", t.toString());
}
fdg.force = force;
};
return fdg
}
function id(d){return d;}
&#13;
svg {
outline: 1px solid #282f51;
pointer-events: all;
overflow: visible;
}
g.outline {
outline: 1px solid red;
}
#panel div {
display: inline-block;
margin: 0 .25em 3px 0;
}
#panel div div {
white-space: pre;
}
div#inputDiv {
white-space: normal;
display: inline-block;
}
.node {
cursor: default;
}
text {
font-size: 8px;
}
&#13;