javascript - 如何计算力向箭头图中不同大小圆的修改路径?

标签 javascript d3.js svg geometry force-layout

我的问题是对here中的力定向图的进一步修改。这里,从每个源到目标,目标端都有一个箭头,这样效果很好,但是如果我们坚持所有圆的半径都是 5 的理论。

但是在我的例子中,我修改了示例,所有节点将根据参数具有不同的半径,因此在这种情况下,箭头隐藏在目标后面,因为半径很大,因此路径和箭头隐藏在旁边,我不是寻找将路径和箭头放在前面的解决方案,而是我正在尝试找到新点。用外行语言来说,我需要从路径中减去半径单位,以便在外圆上得到一个点。

示例 fiddle 是 HERE

我想在tick函数中需要对此代码进行一些修改

  function tick() {
    path.attr("d", function(d) {
        var dx = d.target.x - d.source.x,
                dy = d.target.y - d.source.y,
                dr = Math.sqrt(dx * dx + dy * dy);
        return "M" +
                d.source.x + "," +
                d.source.y + "A" +
                dr + "," + dr + " 0 0,1 " +
                d.target.x + "," +
                d.target.y;
   });

我尝试将此代码修改为 HERE但不是一个很好的解决办法,它的行为很奇怪,任何人都可以评论我们如何计算这一点

最佳答案

这是一个“经典”解决方案(基于 this answer ):

path.attr("d", function(d) {

    diffX = d.target.x - d.source.x;
    diffY = d.target.y - d.source.y;

    pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));

    offsetX = (diffX * d.target.radius) / pathLength;
    offsetY = (diffY * d.target.radius) / pathLength;


    var dx = (d.target.x - offsetX) - d.source.x,
        dy = (d.target.y - offsetY) - d.source.y,
        dr = Math.sqrt(dx * dx + dy * dy);
    return "M" +
        d.source.x + "," +
        d.source.y + "A" +
        dr + "," + dr + " 0 0,1 " +
        (d.target.x - offsetX) + "," +
        (d.target.y - offsetY);
});

此数学运算的唯一问题是弧线从源到目标的直线开始和结束。这给人的印象是箭头有点“向右”。

这是演示:

// get the data
var graph = {
    "nodes": [{
        "name": "Ilya I",
        "group": 0
    }, {
        "name": "Betty  B",
        "group": 1
    }, {
        "name": "Andy N",
        "group": 2
    }, {
        "name": "Harper P",
        "group": 3
    }, {
        "name": "Holly V",
        "group": 4
    }, {
        "name": "Elijah W",
        "group": 5
    }, {
        "name": "Kalvin L",
        "group": 6
    }, {
        "name": "Chris  D",
        "group": 7
    }, {
        "name": "Alexa U",
        "group": 8
    }],
    "links": [{
        "source": 2,
        "target": 5,
        "value": 1,
        "type": "arrow"
    }, {
        "source": 2,
        "target": 6,
        "value": 3,
        "type": "arrow"
    }, {
        "source": 2,
        "target": 7,
        "value": 1,
        "type": "arrow"
    }, {
        "source": 2,
        "target": 8,
        "value": 1,
        "type": "arrow"
    }, {
        "source": 3,
        "target": 2,
        "value": 1,
        "type": "arrow"
    }, {
        "source": 3,
        "target": 4,
        "value": 1,
        "type": "arrow"
    }, {
        "source": 3,
        "target": 6,
        "value": 2,
        "type": "arrow"
    }, {
        "source": 3,
        "target": 7,
        "value": 1,
        "type": "arrow"
    }, {
        "source": 3,
        "target": 8,
        "value": 3,
        "type": "arrow"
    }, {
        "source": 5,
        "target": 5,
        "value": 1,
        "type": "arrow"
    }, {
        "source": 6,
        "target": 2,
        "value": 1,
        "type": "arrow"
    }]
};

var nodecolor = d3.scale.category20();

var nodes = {};

// Compute the distinct nodes from the links.
var links = graph.links;

var width = 500,
    height = 400;

var force = d3.layout.force()
    .nodes(graph.nodes)
    .links(links)
    .size([width, height])
    .linkDistance(function(d) {
        return 1 / d.value * 250;
    })
    .charge(-500)
    .on("tick", tick)
    .start();

// Set the range
var v = d3.scale.linear().range([0, 100]);

// Scale the range of the data
v.domain([0, d3.max(links, function(d) {
    return d.value;
})]);

// asign a type per value to encode opacity
links.forEach(function(link) {
    if (v(link.value) <= 25) {
        link.type = "twofive";
    } else if (v(link.value) <= 50 && v(link.value) > 25) {
        link.type = "fivezero";
    } else if (v(link.value) <= 75 && v(link.value) > 50) {
        link.type = "sevenfive";
    } else if (v(link.value) <= 100 && v(link.value) > 75) {
        link.type = "onezerozero";
    }
});

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

// build the arrow.
svg.append("svg:defs").selectAll("marker")
    .data(["end"]) // Different link/path types can be defined here
    .enter().append("svg:marker") // This section adds in the arrows
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 10)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
    .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
    .data(force.links())
    .enter().append("svg:path")
    .attr("class", function(d) {
        return "link " + d.type;
    })
    .attr("marker-end", "url(#end)");

// define the nodes
var node = svg.selectAll(".node")
    .data(force.nodes())
    .enter().append("g")
    .attr("class", "node")
    //.on("click", click)
    //.on("dblclick", dblclick)
    .call(force.drag);

// add the nodes
node.append("circle")
    .attr("r", function(d) {
        d.radius = d.group * 5;
        return d.radius
    })
    .style("fill", function(d) {
        return nodecolor(d.group);
    });

// add the text
node.append("text")
    .attr("x", 12)
    .attr("dy", ".35em")
    .text(function(d) {
        return d.name;
    });

// add the curvy lines
function tick() {
    path.attr("d", function(d) {

        diffX = d.target.x - d.source.x;
        diffY = d.target.y - d.source.y;

        pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));

        offsetX = (diffX * d.target.radius) / pathLength;
        offsetY = (diffY * d.target.radius) / pathLength;


        var dx = (d.target.x - offsetX) - d.source.x,
            dy = (d.target.y - offsetY) - d.source.y,
            dr = Math.sqrt(dx * dx + dy * dy);
        return "M" +
            d.source.x + "," +
            d.source.y + "A" +
            dr + "," + dr + " 0 0,1 " +
            (d.target.x - offsetX) + "," +
            (d.target.y - offsetY);
    });

    node.attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });
}

// action to take on mouse click
function click() {
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 22)
        .style("fill", "steelblue")
        .style("stroke", "lightsteelblue")
        .style("stroke-width", ".5px");
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 16)
        .style("fill", function(d) {
            return nodecolor(d.group);
        });
}

// action to take on mouse double click
function dblclick() {
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 6)
        .style("fill", function(d) {
            return nodecolor(d.group);
        });
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 12)
        .style("stroke", "none")
        .style("fill", "black")
        .style("stroke", "none")
        .style("font", "10px sans-serif");
}
path.link {
        fill: none;
        stroke: #666;
        stroke-width: 1.5px;
    }

    path.link.twofive {
        opacity: 0.25;
    }

    path.link.fivezero {
        opacity: 0.50;
    }

    path.link.sevenfive {
        opacity: 0.75;
    }

    path.link.onezerozero {
        opacity: 1.0;
    }

    circle {
        fill: #ccc;
        stroke: #fff;
        stroke-width: 1.5px;
    }

    text {
        fill: #000;
        pointer-events: none;
    }
    #content {
        padding: 7px;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

关于javascript - 如何计算力向箭头图中不同大小圆的修改路径?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40841815/

相关文章:

javascript - 使用 OData 和 IQueryable 的风险

javascript - 为什么我的单选按钮列表选择列表中的多个项目?

javascript - DOM 节点清理在 d3 中如何工作?

javascript - D3力布局仅显示一个链接

javascript - 使用 Iron-router 从 Meteor 生成独立的 SVG

javascript - 我想创建一个 js 文件并且必须动态加载 img 标签

php - 关于php函数的问题

javascript - D3 多层时间轴

javascript - 为什么在 D3 中将 map 不透明度设置为 0 时整个 SVG 消失了?

svg - 是否可以导入调色板并让描边、填充和渐变颜色停止颜色引用调色板?