Vasilij Altunin
Vasilij Altunin

Reputation: 822

D3: update path link position

I creating Fimily Tree, it all seems works fine, except links. They did not updated when i drag node. Link end just mode to different direction and did not follow node.

Also when i click on node it stroke changes, but also changes text strokes, that inside rect, i need to change only rect stroke.

I prepare Feedly

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">

    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
  </head>

  <body>

<script>



var margin = {top: 100, right: 50, bottom: 50, left: 50},
    width = 1800 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom,
    boxWidth = 140,
    boxHeight = 80;
    boxHSpace = 200,
    boxVSpace = 70,
    links = [];

var svg = d3.select("body")
    .attr("bgcolor", "#2c2c2c")
    .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 datas;

datas = setPositions(buildDataAssocArray(getData()));

var node = svg.selectAll(".node")
    .data(d3.entries(datas))
   .enter()
    .append("g");


function dragmove(d) {
    var x = d3.event.x;
    var y = d3.event.y;

    d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
    console.log(d);
    datas[d.key].x=x;
    datas[d.key].y=y;

    link.attr("d", connect);
}

// Define drag beavior
    var drag = d3.behavior.drag()
        .on("drag", dragmove)
        .origin(function() {
            var t = d3.transform(d3.select(this).attr("transform"));
            return {x: t.translate[0], y: t.translate[1]};
        });

    node.call(drag)
     .on("click", click);

    var selected;
    function click(){       
       if(!selected){
         selected = this;
         d3.select(selected).style('stroke', 'white');
      } 
      else {
         d3.select(selected).style('stroke', 'black');
         selected = this;
         d3.select(selected).style('stroke', 'white');
      }
    }     


    node.append("rect")
        .attr("width", boxWidth)
        .attr("height", boxHeight)
        .attr("fill", "tan")
        .attr("x", function(d) { return d.value.x - 70; })
        .attr("y", function(d) { return height - d.value.y - 40; })
        .style("cursor", "pointer");


    node.append("text")
        .attr("font-size", "16px")
        .attr("fill", "black")
        .attr("x", function(d) { return d.value.x; })
        .attr("y", function(d) { return height - d.value.y - 15; })
        .style("text-anchor", "middle")
        .text(function(d) { return d.value.name; })


    var link = svg.selectAll(".link")
        .data(getLinks())
       .enter()
        .insert("path", "g")
        .attr("fill", "none")
        .attr("stroke", "#000")
        .attr("stroke", "#000")
        .attr("shape-rendering", "crispEdges")
        .attr("d", connect);

    function connect(d, i) {
        console.log(d);
        return     "M" + d.source.x + "," + (height - d.source.y)
                 + "V" + (height - (3*d.source.y + 4*d.target.y)/7)
                 + "H" + d.target.x
                 + "V" + (height - d.target.y);
    };

    function getLinks() {
        return links ;
    }


function setPositions(datas)
{
    var fams=getFamily();


        var x=200 ,y=100;

    fams.forEach(function(item, i, arr) {


        item.childs.forEach(function(child, i1, arr1) {
            console.log(child);
            datas[child].x = x;
            datas[child].y = y;
            links.push({"source" : datas[child], "target" : datas[item.husb]});
            links.push({"source" : datas[child], "target" : datas[item.wife]});
        });

        x+=100;
        y+=100;

        datas[item.husb].x = x-100;
        datas[item.husb].y = y+100;
        datas[item.wife].x = x+100;
        datas[item.wife].y = y+100;

        x+=100;
        y+=100;

    });

    return datas;
}


function getFamily() {
    return [
    {
        "husb":   "003",
        "wife":   "002",
        "childs": ["001"]
        }
];
}

function buildDataAssocArray(adatas) {
    var datas = adatas;
    var dArray = [];
    datas.forEach(function(item, i, arr) {
                item.x=0;
                item.y=0;
        dArray[item.id] = item;
    });
    return dArray;
}

//console.log(buildDataAssocArray());

function getData() {
    return [
     {  
        "id" : "001",
        "name": "Алтунин Василий",
        "mother" : "002",
        "father" : "003",
        "child"  : null,
     },
    {
        "id" : "002",
        "name": "Алтунина Елена",
        "mother" : "004",
        "father" : "005",
        "child"  : "001",
    },
    {
        "id" : "003",
        "name": "Алтунин Владимир",
        "mother" : "006",
        "father" : "007",
        "child"  : "001",
    },
   ];
};

</script>

  </body>
</html>

SOLVED:

Here result feedle

Upvotes: 2

Views: 2183

Answers (2)

JMP
JMP

Reputation: 4467

You can directly access the rect as a childNodes object:

function click() {
    if (!selected) {
        selected = this;
        d3.select(selected.childNodes[0]).style('stroke', 'white');
    } else {
        d3.select(selected.childNodes[0]).style('stroke', 'black');
        selected = this;
        d3.select(selected.childNodes[0]).style('stroke', 'white');
    }
}

JSFiddle

Upvotes: 1

Cyril Cherian
Cyril Cherian

Reputation: 32327

In your drag function:

Register the drag x and y inavriable dx and dy respectively.

function dragmove(d) {
    var x = d3.event.x;
    var y = d3.event.y;

    d3.select(this).attr("transform", "translate(" + x + "," + y + ")");

    datas[d.key].dx =d3.event.x;
    datas[d.key].dy =d3.event.y;
    link.attr("d", connect);
}

Now when you make the d attribute for the line consider the dx and dy computed in the drag function.

function connect(d, i) {
    var dsx=d.source.dx ? d.source.dx:0;
    var dsy=d.source.dy ? d.source.dy:0;
    var dtx=d.target.dx ? d.target.dx:0;
    var dty=d.target.dy ? d.target.dy:0;
    return     "M" + (d.source.x + dsx) + "," + (height - d.source.y +dsy)
             + "V" + (height - (3*(d.source.y+dsy) + 4*(d.target.y +dty))/7)
             + "H" + (d.target.x+dtx)
             + "V" + (height - (d.target.y- dty));
};

however i suggest you to use d3.interpolate rather than doing the calculation in connect function.

working code here

Upvotes: 4

Related Questions