Nick
Nick

Reputation: 892

D3.js won't drag + attempt at focus

I am working on a simple D3 test and I can't get my nodes to drag for some reason. Referring to this example: (http://bl.ocks.org/mbostock/1804919) what I want is for each of my nodes to start and stay at their starting position but like in the example I want the nodes to be dragable and when they are released to return back to their starting place. What am I missing?

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    .Properties{
        fill: green;
        stroke: black;
        stroke-width: 2px;
    }
    svg {
      background-color: #FFF;
      cursor: default;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      -o-user-select: none;
      user-select: none;
    }
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
        var width = 960, height = 500, colors = d3.scale.category10();
        var svg = null, force = null;
        var circle = null, path = null;
        var nodes = null, links = null;
        var nodesArray = null, linkArray = null;
        var count = 0;
        var element = "body"; var numEdges = 2, numNodes = 5;
        var i = 0; var L = 16, r = 12, lineLimit = 10;
        var d = 2 * r + L;
        var R = (count - 1) * d;
        var m = width / 2;
        var X;
        var drag = d3.behavior.drag();
        svg = d3.selectAll(element).append('svg').attr('width', width).attr('height', height);
        nodes = d3.range(numNodes).map(function () {
            X = m - (R / 2) + (i * d);
            ++i;
            return {
                x: X,
                y: (height) / 3,
                fx: X,
                fy: height / 3,
                id: i-1,
                reflexive: true
            };           
        });
        for (var i = 0; i < numNodes; ++i) {
            d3.select(element).append("h3").text("Node " + i + ": " + nodes[i].id);
        }

        i = -1;
        links = d3.range(numEdges).map(function () {
            i++;
            return {
                //
                source: nodes[i],
                target: nodes[i+1],
                left: false,
                right: true
            }
        });
        for (var i = 0; i < numEdges; ++i) {
            d3.select(element).append("h3").text("Source: " + links[i].source.id + " Target: " + links[i].target.id);
        }

        force = d3.layout.force().size([width, height]).nodes(nodes).links(links).linkDistance(40).linkStrength(0.1).charge(-300);

        linkArray = svg.selectAll('.link').data(links).enter().append('line').attr('class', 'link')
            .attr('x1', function (d) {
                return nodes[d.source.id].x;
            })
            .attr('y1', function (d) { return nodes[d.source.id].y; })
            .attr('x2', function (d) { return nodes[d.target.id].x; })
            .attr('y2', function (d) { return nodes[d.target.id].y; });

        nodeArray = svg.selectAll("circle").data(nodes).enter().append('circle').attr('class', "Properties").attr('r', 12)
            .attr('cx', function (d) { return d.x })
            .attr('cy', function (d) { return d.y })
            .style('cursor', 'pointer').call(drag);

        force.on('tick', tick);
        force.start();

        //Move Nodes towards focus
        function gravity(alpha) {
            return function (d) {
                d.y += (d.fy - d.y) * alpha;
                d.x += (d.fx - d.x) * alpha;
            };
        }

        function tick() {
            alert("in Stepforce" + nodeArray[1].id);

            nodeArray
                .attr('cx', function (d) { return d.x; })
                .attr('cy', function (d) { return d.y; })
                .each(gravity(.2 * e.alpha));

            linkArray.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; });
        }

</script>

Upvotes: 0

Views: 197

Answers (1)

Himmel
Himmel

Reputation: 3709

You're missing the chaining of drag events to your drag variable. You have to call functions for the dragstart, drag, and dragend events.

In your specific case, you want to (1) record the initial position of the circle, (2) translate the circle based on the location of the mouse, and (3) return the circle back to its original position when the drag ends.

For example:

var drag = d3.behavior.drag()
  .on('dragstart', dragstart) // when the drag starts, call dragstart()
  .on('drag', drag) // during drag, call drag()
  .on('dragend', dragend); // call dragend() on release

var originalPosition;
function dragstart() {
    // store starting coordinates
    originalPosition = d3.mouse(this); // returns array with x and y coords -> [x, y]
}

function drag() {
    var m = d3.mouse(this);
    // make the circle's coordinates match those of the mouse
    d3.select(this)
      .attr('cx', m[0])
      .attr('cy', m[1]);
}

function dragend() {
    // send circle back to original position
    d3.select(this)
      .attr('cx', originalPosition[0])
      .attr('cy', originalPosition[1]);
}

You can also add transition() before changing the attributes in dragend() to animate them back to the starting position. Additionally, you might want to send the circles back to their original coordinates, rather than the location of the mouse when the drag started. This should get you started, though.

Upvotes: 1

Related Questions