2014-09-02 51 views
2

Here is a sample of the D3.js visualization,我現在工作:極限阻力運動,以圓形SVG邊界

enter image description here

這裏,灰色圓圈是SVG容器。我想限制綠色氣泡拖到灰色圓圈邊界。

我發現this example,我用它在我的代碼,但我得到了兩個錯誤:

Uncaught TypeError: Cannot read property 'apply' of undefined // points to d3.v3.min.js:3 
Uncaught TypeError: Cannot read property 'each' of undefined // points to line X marked in the code below 

看來我不能強制佈局中使用.call(drag)

我如何得到這個工作? jsFiddle

JS:

var data = { 
    name: "layout", 
    children: [ 
     {name: "AxisLayout", size: 6725}, 
     {name: "BundledEdgeRouter", size: 3727}, 
     {name: "CircleLayout", size: 9317}, 
     {name: "CirclePackingLayout", "size": 12003}, 
     {name: "DendrogramLayout", "size": 4853}, 
     {name: "ForceDirectedLayout", "size": 8411}, 
     {name: "IcicleTreeLayout", "size": 4864}, 
     {name: "IndentedTreeLayout", "size": 3174}, 
     {name: "Layout", "size": 7881}, 
     {name: "NodeLinkTreeLayout", "size": 12870}, 
     {name: "PieLayout", "size": 2728}, 
     {name: "RadialTreeLayout", "size": 12348}, 
     {name: "RandomLayout", "size": 870}, 
     {name: "StackedAreaLayout", "size": 9121}, 
     {name: "TreeMapLayout", "size": 9191} 
    ] 
}; 

var margin = { 
    top: 0, 
    right: 0, 
    bottom: 0, 
    left: 0 
}, 
width = 400 - margin.left - margin.right, 
    height = 400 - margin.top - margin.bottom; 

var n = data.children.length, 
    m = 1, 
    padding = 6, 
    radius = d3.scale.sqrt().range([0, 12]), 
    color = d3.scale.category10().domain(d3.range(m)), 
    x = d3.scale.ordinal().domain(d3.range(m)).rangePoints([0, width], 1); 

var nodes = d3.range(n).map(function() { 
    var i = Math.floor(Math.random() * m), //color 
     v = (i + 1)/m * -Math.log(Math.random()); //value 
    return { 
     radius: radius(v), 
     color: color(i), 
     cx: x(i), 
     cy: height/2, 
    }; 

}); 

nodes.forEach(function(item, index){ 
    item.radius = 20; 
}); 

//console.dir(nodes); 

var force = d3.layout.force() 
    .nodes(nodes) 
    .size([width, height]) 
    .gravity(0) 
    .charge(0) 
    .on("tick", tick) 
    .start(); 

var svg = d3.select("#chart").append("svg") 
    .attr("width", width + margin.left + margin.right) 
    .attr("height", height + margin.top + margin.bottom) 
    .append("g") 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

var circle = svg.selectAll("circle") 
    .data(nodes) 
    .enter().append("circle") 
    .attr("r", function (d) { 
    return d.radius; 
    }) 
    .style("fill", function (d,i) { 
     return "green"; 
    }) 
    .call(force.drag) 
    .call(drag); 

var drag = d3.behavior.drag() 
    .origin(function(d) { return d; }) 
    .on("drag", dragmove); 

function tick(e) { 
    circle.each(gravity(.2 * e.alpha)) // line X 
     .each(collide(.5)) 
     .attr("cx", function (d) { 
     return d.x; 
    }) 
     .attr("cy", function (d) { 
     return d.y; 
    }); 
} 

// Move nodes toward cluster focus. 
function gravity(alpha) { 
    return function (d) { 
     d.y += (d.cy - d.y) * alpha; 
     d.x += (d.cx - d.x) * alpha; 
    }; 
} 

// Resolve collisions between nodes. 
function collide(alpha) { 
    var quadtree = d3.geom.quadtree(nodes); 
    return function (d) { 
     var r = d.radius + radius.domain()[1] + padding, 
      nx1 = d.x - r, 
      nx2 = d.x + r, 
      ny1 = d.y - r, 
      ny2 = d.y + r; 
     quadtree.visit(function (quad, x1, y1, x2, y2) { 
      if (quad.point && (quad.point !== d)) { 
       var x = d.x - quad.point.x, 
        y = d.y - quad.point.y, 
        l = Math.sqrt(x * x + y * y), 
        r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding; 
       if (l < r) { 
        l = (l - r)/l * alpha; 
        d.x -= x *= l; 
        d.y -= y *= l; 
        quad.point.x += x; 
        quad.point.y += y; 
       } 
      } 
      return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; 
     }); 
    }; 
} 

function dragmove(){ 
    console.log("dragging.."); 
} 

回答

5

好吧,在評論了長時間的討論後,我想出了這個最終的解決方案:

var pointerEl = document.getElementById("pointer"); 
var canvasEl = document.getElementById("canvas"); 
var canvas = { 
    width: canvasEl.offsetWidth, 
    height: canvasEl.offsetHeight, 
    top: canvasEl.offsetTop, 
    left: canvasEl.offsetLeft 
}; 
canvas.center = [canvas.left + canvas.width/2, canvas.top + canvas.height/2]; 
canvas.radius = canvas.width/2; 


window.onmousemove = function(e) { 
    var result = limit(e.x, e.y); 
     pointer.style.left = result.x + "px"; 
     pointer.style.top = result.y + "px"; 
} 

function limit(x, y) { 
    var dist = distance([x, y], canvas.center); 
    if (dist <= canvas.radius) { 
     return {x: x, y: y}; 
    } 
    else { 
     x = x - canvas.center[0]; 
     y = y - canvas.center[1]; 
     var radians = Math.atan2(y, x) 
      return { 
       x: Math.cos(radians) * canvas.radius + canvas.center[0], 
       y: Math.sin(radians) * canvas.radius + canvas.center[1] 
      } 
     } 
    } 

function distance(dot1, dot2) { 
    var x1 = dot1[0], 
     y1 = dot1[1], 
     x2 = dot2[0], 
     y2 = dot2[1]; 
    return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); 
} 

jsfiddle

該解決方案採用這個其他jsfiddle

2

,你能做些什麼,它把一個約束拖動事件:在這個例子中,我已經建立了一個長方形的約束

http://jsfiddle.net/InferOn/5wssqqdw/1/

var drag = force.drag() 
     .on("drag", dragmove); 

(我給你任務放置圓的約束:)):

function dragmove(d) { 
     if (d.py > 300) d.py = 300; 
     if (d.py < 100) d.py = 100; 

     if (d.px > 300) d.px = 300; 
     if (d.px < 100) d.px = 100; 

    } 
+0

我該如何放入圓形約束?我google搜索,但力量得到任何提示。對不起,D3.js – 2014-09-02 11:54:50

+1

@indieblue剛剛計算圓心之間的歐氏距離,並將其與約束半徑減去小圓的半徑進行比較。 – Baz 2014-09-02 12:25:59

+0

@巴茲我已經計算了歐幾里德距離,在[這個小提琴]中的第70行(http://jsfiddle.net/rdesai/5wssqqdw/3/)。約束半徑減去小圓的半徑爲'180'。我沒有得到你的意思來比較這一點。另外,我應該如何設置小圓圈的「px」和「py」? – 2014-09-03 06:20:16