使箭头指向 D3 中节点的外边缘 [英] Get arrowheads to point at outer edge of node in D3

查看:39
本文介绍了使箭头指向 D3 中节点的外边缘的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 D3 的新手,我正在尝试创建交互式网络可视化.我已经复制了 this 示例的大部分内容,但我已将曲线更改为直线使用SVG线"而不是路径",我还根据节点代表的数据缩放节点.问题是我的箭头(用 SVG 标记创建)在线条的末端.由于一些节点很大,箭头隐藏在它们后面.我希望我的箭头正好显示在它们指向的节点的外边缘.

I'm new to D3 and I'm trying to create an interactive network visualization. I've copied large parts of this example, but I have changed the curved lines to straight ones by using SVG "lines" rather than "paths", and I've also scaled the nodes according to the data they represent. The problem is that my arrowheads (created with SVG markers) are at the ends of the lines. Since some of the nodes are large, the arrows get hidden behind them. I'd like my arrowheads to show up right at the outside edge of the node they point to.

以下是我创建标记和链接的方式:

Here is how I'm creating the markers and links:

svg.append("svg:defs").selectAll("marker")
    .data(["prereq", "coreq"])
    .enter().append("svg:marker")
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
    .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

var link = svg.selectAll(".link")
    .data(force.links())
    .enter().append("line")
    .attr("class", "link")
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });

我注意到refX"属性指定了箭头应该显示在离线尾多远的地方.我怎样才能让它依赖于它指向的节点的半径?如果我不能这样做,我可以改为更改线条本身的端点吗?我猜我会在这个函数中这样做,它会在一切移动时重置线条的端点:

I noticed that the "refX" attribute specifies how far from the end of the line the arrowhead should show up. How can I make this dependent on the radius of the node it's pointing to? If I can't do that, could I instead change the endpoints of the lines themselves? I'm guessing I would do that in this function, which resets the endpoints of the lines as everything moves:

function tick() {
        link
            .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; });

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

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

哪种方法更有意义,我将如何实施?

Which approach makes more sense, and how would I implement it?

推荐答案

谢谢 Lars Kotthoff,根据另一个问题的建议,我得到了这个!首先,我从使用线切换到路径.我不认为我实际上必须这样做,但它使我更容易遵循我正在查看的其他示例,因为它们使用了路径.

Thanks Lars Kotthoff, I got this to work following the advice from the other question! First I switched from using lines to paths. I don't think I actually had to do that, but it made it easier to follow the other examples I was looking at because they used paths.

然后,我向我的节点添加了一个半径"字段.我只是在设置半径属性时这样做,将其添加为实际字段而不是立即返回值:

Then, I added a "radius" field to my nodes. I just did this when I set the radius attribute, by adding it as an actual field rather than returning the value immediately:

var circle = svg.append("svg:g").selectAll("circle")
                    .data(force.nodes())
                    .enter().append("svg:circle")
                    .attr("r", function(d) {
                        if (d.logic != null) {
                            d.radius = 5;
                        } else {
                            d.radius = node_scale(d.classSize);
                        }
                        return d.radius;

然后我编辑了我的 tick() 函数以考虑这个半径.这需要一些简单的几何...

I then edited my tick() function to take this radius into account. This required a bit of simple geometry...

function tick(e) {

        path.attr("d", function(d) {
            // Total difference in x and y from source to target
            diffX = d.target.x - d.source.x;
            diffY = d.target.y - d.source.y;

            // Length of path from center of source node to center of target node
            pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));

            // x and y distances from center to outside edge of target node
            offsetX = (diffX * d.target.radius) / pathLength;
            offsetY = (diffY * d.target.radius) / pathLength;

            return "M" + d.source.x + "," + d.source.y + "L" + (d.target.x - offsetX) + "," + (d.target.y - offsetY);
        });

基本上,路径形成的三角形,总x变化(diffX),总y变化(diffY)与目标节点内部的路径段(即节点半径)形成的三角形相似),目标节点内部的 x 变化 (offsetX),目标节点内部的 y 变化 (offsetY).这意味着目标节点半径与总路径长度的比值等于 offsetX 与 diffX 的比值以及 offsetY 与 diffY 的比值.

Basically, the triangle formed by the path, it's total x change (diffX), and it's total y change (diffY) is a similar triangle to that formed by the segment of the path inside the target node (i.e. the node radius), the x change inside the target node (offsetX), and the y change inside the target node (offsetY). This means that the ratio of the target node radius to the total path length is equal to the ratio of offsetX to diffX and to the ratio of offsetY to diffY.

我还将箭头的 refX 值更改为 10.我不确定为什么这是必要的,但现在它似乎起作用了!

I also changed the refX value to 10 for the arrows. I'm not sure why that was necessary but now it seems to work!

这篇关于使箭头指向 D3 中节点的外边缘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆