d3缩放不以径向树为中心的鼠标位置

时间:2015-03-28 18:26:32

标签: d3.js

我尝试将相当标准的d3拖动/缩放功能应用于径向树布局。

问题在于,如果我将zoomhandler定义为......

svg.attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")");

...然后缩放完全跟随鼠标,但整个径向树在第一次缩放时开始在错误的位置(即(0,0)坐标)。

然而,如果我将zoomhandler定义为......

svg.attr("transform", "translate(" + (w/2 + d3.event.translate[0]) +
     "," + (h/2 + d3.event.translate[1]) + ")scale(" + d3.event.scale + ")" );

...然后树行为正确,但缩放不跟随鼠标(事实上,为了放大/缩小树而不移动它,我的鼠标需要定位在0,0屏幕左上角的坐标)

我很欣赏这是一个之前已经讨论过的主题(我认为最值得注意的是Using D3, can semantic zoom be applied to a radial tree?),但我还不清楚如何解决这个问题所以会非常感激来自任何人的任何输入,他们专门解决了使径向树同时向鼠标位置缩放的问题,并同时保持锚定在屏幕中心。谢谢!

这里详细介绍了完整的代码......

<!DOCTYPE html>
<html>
<head>
  <title>Demo</title>
  <script src="http://d3js.org/d3.v3.min.js"></script>
  <style>
  </style>
</head>

<body>

<script>

var w = 1200;
var h = 1000;

var data = [{'parent_id' : '1', 'items_count' : '2'}
            , {'parent_id' : '2', 'items_count' : '4'}
            , {'parent_id' : '3', 'items_count' : '3'}
            , {'parent_id' : '4', 'items_count' : '2'}
            , {'parent_id' : '5', 'items_count' : '1'}
            , {'parent_id' : '6', 'items_count' : '6'}
            , {'parent_id' : '7', 'items_count' : '2'}
            , {'parent_id' : '8', 'items_count' : '4'}
            , {'parent_id' : '9', 'items_count' : '5'}
            , {'parent_id' : '10', 'items_count' : '7'}
            ];

var treeRadius = 300;
var searchCircleRadius = 60;

var circleRadiusScale = d3.scale.linear()
                        .domain([0, d3.max(data, function(d) { return d.items_count; })])
                        .range([10, 40]); 

var dataTree = {
    children: data.map(function(d) { return { parent_id: d.parent_id, items_count: d.items_count}; })
};

var tree = d3.layout.tree()
    .size([360, treeRadius]);

var mainSvg = d3.select("body").append("svg")
    .attr("width", w)
    .attr("height", h);

var svg = mainSvg   
         .append("g")
         .attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");   

var childGroupZoom = svg.append("g");

var zoomListener = d3.behavior.zoom()
  .scaleExtent([0.1, 1.75])
  .on("zoom", zoomHandler);

function zoomHandler() {
   //1) for both of these, the tree starts in centre of screen, drag works nicely, but zoom doesn't follow mouse
   //childGroupZoom.attr("transform", "translate(" + (d3.event.translate[0]) + "," + (d3.event.translate[1]) + ") scale(" + d3.event.scale + ")"); 
   childGroupZoom.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); 

   //2) follows the mouse on zoom but jump to top-left on first zoom/drag (because it's applied to "svg" which already has a translate applied)...
   //svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); //follows mouse on zoom but starts at wrong place

   //3) same as the first category - the tree doesn't jump, but the zoom doesn't follow the mouse
   //svg.attr("transform", "translate(" + (w/2 + d3.event.translate[0]) + "," + (h/2 + d3.event.translate[1]) + ")scale(" + d3.event.scale + ")" ); //starts in centre but doesn't follow mouse!
}

zoomListener(mainSvg); 

var nodes = tree.nodes(dataTree);   

var basicNode = childGroupZoom.selectAll(".node");

var node = basicNode
            .data(nodes)
            .enter().append("g")
            .attr("class", "node")
            .attr("transform", function(d) {
                return "rotate(" + (d.x - 90) + ") translate(" + d.y + ")";
            });

var outlineCircles = node.append("circle")
              .attr("r", function(d,i) { if (i<1) {
                                            return searchCircleRadius;
                                        } else { 
                                            return circleRadiusScale(d.items_count); 
                                        }})
              .attr("stroke", "#0099FF")
              .attr("stroke-width", "3")
              .attr("transform", function(d) {return "rotate(" + (-d.x + 90) + ")";});   

</script>
</body>
</html>

1 个答案:

答案 0 :(得分:2)

var dragListener = d3.behavior.drag()
    .on("drag", function() {
        dragX = d3.event.dx;
        dragY = d3.event.dy;
    });

mainSvg.call(dragListener);

var dragging = 0;   
var dragX = 0, dragY = 0;

dragListener.on("dragstart", function() {
  dragging = 1;
}); 

dragListener.on("dragend", function() {
  dragging = 0;
  dragX = 0;
  dragY = 0;
}); 

function zoomHandler() {
    var pos = d3.mouse(this);
    var scale = d3.event.scale;

    var trans = d3.transform(childGroupZoom.attr("transform"));
    var tpos = trans.translate;
    var tscale = trans.scale;
    var tx = tpos[0];
    var ty = tpos[1];
    var mx = pos[0] - w/2;
    var my = pos[1] - h/2;

    var dx =  (mx - tx - dragX)/tscale[0];
    var dy =  (my - ty - dragY)/tscale[1];
    var dx2 = (mx - dx)/scale - dx;
    var dy2 = (my - dy)/scale - dy;

    var tform = "translate(" + dx + "," + dy + ")scale(" + scale + ")translate(" + dx2 + "," + dy2 + ")"
    childGroupZoom.attr("transform", tform); 
}

注意:框架上的拖动操作也会调用缩放处理程序,这可以通过 dragX dragY 来促进对象的移动。