【问题标题】:Get arrowheads to point at outer edge of node in D3让箭头指向 D3 中节点的外边缘
【发布时间】:2013-05-15 15:23:12
【问题描述】:

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

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

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”属性指定了箭头应该显示在离行尾多远的地方。我怎样才能使它取决于它指向的节点的半径?如果我不能这样做,我可以改为更改线条本身的端点吗?我猜我会在这个函数中这样做,它会在一切移动时重置线条的端点:

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 + ")";
        });
    }

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

【问题讨论】:

标签: svg d3.js


【解决方案1】:

感谢 Lars Kotthoff,我按照另一个问题的建议完成了这项工作!首先,我从使用线切换到路径。我认为我实际上不必这样做,但它使我更容易理解我正在查看的其他示例,因为它们使用了路径。

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

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() 函数来考虑这个半径。这需要一些简单的几何图形...

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的比率。

我还将箭头的 refX 值更改为 10。我不知道为什么这是必要的,但现在它似乎工作了!

【讨论】:

  • 您的问题中绘制的箭头具有> 的形式,从原点“上方”开始((-5,0)),与x 轴((10,0))有一条线,结束“下方”起源((5,0))。注意 SVG 不遵循笛卡尔坐标系,左上角是视口原点。 refX=10 弥补了水平位移,确保箭头指向路径的尽头(仍然需要去掉半径长度)。
  • @FrancesKR 您能否分享椭圆或矩形类型节点的指南?我面临着类似的问题。
【解决方案2】:

我回答了同样的问题over here。答案使用向量数学,它对其他计算也非常有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    • 2016-01-03
    • 1970-01-01
    • 2013-10-25
    相关资源
    最近更新 更多