如何在拖动节点时使d3js强制有向图不那么抖动?

死锁

当我将一个节点拖到另一点时,我希望力导向图保持平静。此刻,将一个小节点拖到远离中心的位置会导致整个图形不受控制地抖动。由于节点很多(超过100个),这可能是一团糟。

这是描述问题的简短视频:https : //gfycat.com/GleamingMellowHypacrosaurus

我知道设置所有节点坐标的方法,但是这是不可能的,因为节点太多,以后可能会增加。

这是我的代码:

function getNeighbors(node) {
    return links.reduce(function(neighbors, link) {
        if (link.target.id === node.id) {
            neighbors.push(link.source.id)
        } else if (link.source.id === node.id) {
            neighbors.push(link.target.id)
        }
        return neighbors
    }, [node.id])
}

function isNeighborLink(node, link) {
    return link.target.id === node.id || link.source.id === node.id
}


function getNodeColor(node, neighbors) {
    // If is neighbor
    if (Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1) {
        return 'rgba(251, 130, 30, 1)'
        // return node.level === 1 ? '#9C4A9C' : 'rgba(251, 130, 30, 1)'
    } else {
        // Check the node level
        if (node.level === 0) {
            return '#E72148'
        } else if (node.level === 1) {
            return '#9C4A9C'
        } else {
            return '#D8ABD8'
        }
    }
    //return node.level === 0 ? '#91007B' : '#D8ABD8'
}

function getLinkColor(node, link) {
    return isNeighborLink(node, link) ? 'rgba(251, 130, 30, .85)' : 'rgba(251, 130, 30, 0.25)'
}

function getTextColor(node, neighbors) {
    return Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1 ? '#333' : '#bbb'
}

function getLabelColor(node, link) {
    return isNeighborLink(node, link) ? 'rgba(51, 51, 51, .9)' : 'rgba(51, 51, 51, 0)'    // #333
}

var width = window.innerWidth
var height = window.innerHeight

var svg = d3.select('svg')
// svg.attr('width', width).attr('height', height)
svg.attr("width", '100%')
    .attr("height", '500px')
    .attr('viewBox', '250 0 800 600')
    //.attr('viewBox','0 0 '+Math.min(width,height)+' '+Math.min(width,height))
    .attr('preserveAspectRatio', 'xMidYMid')
    .append("g")
    .attr("transform", "translate(" + Math.min(width, height) / 2 + "," + Math.min(width, height) / 2 + ")");

//add zoom capabilities
var zoom_handler = d3.zoom()
    .scaleExtent([1 / 2, 8])
    .on("zoom", zoom_actions);

zoom_handler(svg);

function zoom_actions() {
    g.attr("transform", d3.event.transform)
}

function button_zoom_in() {
    zoom_handler.scaleBy(svg, 2);
}

function button_zoom_out() {
    zoom_handler.scaleBy(svg, 0.5);
}

// simulation setup with all forces
var linkForce = d3
    .forceLink()
    .id(function(link) {
        return link.id
    })
    // Alternative: using the distance from the data "strength"
    //.distance(50).strength(function (link) { return link.strength })
    // If don't want to use this, use default here:
    .distance(50).strength(.7)

var simulation = d3
    .forceSimulation()
    .force('link', linkForce)
    .force('charge', d3.forceManyBody().strength(-1500))
    .force('radial', d3.forceRadial(function(d) {
        return d.level * 50
    }, width / 2, height / 2))
    .force('center', d3.forceCenter(width / 2, height / 2))

var dragDrop = d3.drag().on('start', function(node) {
    node.fx = node.x
    node.fy = node.y
}).on('drag', function(node) {
    simulation.alphaTarget(0.7).restart()
    node.fx = d3.event.x
    node.fy = d3.event.y
}).on('end', function(node) {
    if (!d3.event.active) {
        simulation.alphaTarget(0)
    }
    node.fx = null
    node.fy = null
})

function selectNode(selectedNode) {
    var neighbors = getNeighbors(selectedNode)

    // we modify the styles to highlight selected nodes
    nodeElements.attr('fill', function(node) {
        return getNodeColor(node, neighbors)
    })
    textElements.attr('fill', function(node) {
        return getTextColor(node, neighbors)
    })
    linkElements.attr('stroke', function(link) {
        return getLinkColor(selectedNode, link)
    })
    labelElements.attr('fill', function(link) {
        return getLabelColor(selectedNode, link)
    }).attr("style", "-webkit-text-stroke: 1px rgba(255, 255, 255, 0.75); text-shadow: -1px -1px 0 rgba(255, 255, 255, 0.75), 1px -1px 0 rgba(255, 255, 255, 0.75), -1px 1px 0 rgba(255, 255, 255, 0.75), 1px 1px 0 rgba(255, 255, 255, 0.75)")

}

// Format the numbers to dots e.g. 100000 => 100.000
function commafy( num ) {
    var str = num.toString().split('.');
    if (str[0].length >= 5) {
        str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1.');
    }
    if (str[1] && str[1].length >= 5) {
        str[1] = str[1].replace(/(\d{3})/g, '$1 ');
    }
    return str.join('.');
}


// Enables zooming
var g = svg.append("g")
    .attr("class", "everything");
// Enables zooming end

// Create circling orbit
var circles = g.selectAll(null) // use g.selectAll instead of svg.selectAll to enable zoom
    .data([200, 350]) // sets the circle radius
    .enter()
    .append("circle")
    .attr("cx", width / 2)
    .attr("cy", height / 2)
    .attr("r", d => d)
    .style("fill", "none")
    .style("stroke", "#ddd");

var linkElements = g.append("g") // use g.append instead of svg.append to enable zoom
    .attr("class", "links")
    .selectAll("line")
    .data(links)
    .enter().append("path")
    .attr("id", function(d, i) {
        return "linkId_" + i;
    })
    .attr("stroke-width", function(link) {
        var linkValueNormalize = link.value / 100;
        var linkValueNormalize = Math.ceil(linkValueNormalize);
        if (linkValueNormalize >= 201) {
            return 26;
        } else if (linkValueNormalize >= 101 && linkValueNormalize <= 200) {
            return 20;
        } else if (linkValueNormalize >= 71 && linkValueNormalize <= 100) {
            return 16;
        } else if (linkValueNormalize >= 41 && linkValueNormalize <= 70) {
            return 12;
        } else if (linkValueNormalize >= 21 && linkValueNormalize <= 40) {
            return 8;
        } else if (linkValueNormalize >= 11 && linkValueNormalize <= 20) {
            return 12;
        } else if (linkValueNormalize >= 7 && linkValueNormalize <= 10) {
            return 8;
        } else if (linkValueNormalize >= 3 && linkValueNormalize <= 6) {
            return 4;
        } else {
            return 2;
        }
        // return linkValueNormalize;
    })
    .attr("stroke", "rgba(251, 130, 30, 0.5)")

var labelElements = g.append("g")
    .attr("class", "label")
    .selectAll("text")
    .data(links)
    .enter().append("text")
    .attr("font-size", 10)
    .attr("font-family", "sans-serif")
    .attr("fill", "rgba(51, 51, 51, 0)")  // #333
    .attr("x", "70")
    .attr("y", "-20")
    .attr("text-anchor", "start")
    .append("textPath")
    .attr("xlink:href", function(d, i) {
        return "#linkId_" + i;
    })
    .text(function(link) {
          var linkValueNormalize = link.value; 
          var linkValueNormalize = commafy(linkValueNormalize);

          return "Rp "+ linkValueNormalize +" M";

    })

var nodeElements = g.append("g") // use g.append instead of svg.append to enable zoom
    .attr("class", "nodes")
    .selectAll("circle")
    .data(nodes)
    .enter().append('a')    // Append a first, then circle
    .attr("xlink:href", function(node){return node.url;})
    .attr("target", "_blank")
    .append("circle")
    .attr("r", function(dat, index, n) {
        var linkItem = links.find(function(link) {
            return link.target == dat.id;
        });

        var radius = 26;
        var linkValueNormalize = (linkItem && linkItem.value) / 100; // in milyar
        var linkValueNormalize = Math.ceil(linkValueNormalize);

        if (linkValueNormalize >= 201) {
            radius = 24;
        } else if (linkValueNormalize >= 101 && linkValueNormalize <= 200) {
            radius = 22;
        } else if (linkValueNormalize >= 71 && linkValueNormalize <= 100) {
            radius = 18;
        } else if (linkValueNormalize >= 41 && linkValueNormalize <= 70) {
            radius = 14;
        } else if (linkValueNormalize >= 21 && linkValueNormalize <= 40) {
            radius = 10;
        } else if (linkValueNormalize >= 11 && linkValueNormalize <= 20) {
            radius = 14;
        } else if (linkValueNormalize >= 7 && linkValueNormalize <= 10) {
            radius = 10;
        } else if (linkValueNormalize <= 6) {
            radius = 6;
        }

        if (dat.level === 0) {
            radius = 26;
        }

        return radius;
    })
    .attr("fill", getNodeColor)
    .attr("stroke", "#fff")
    .attr('stroke-width', 2)
    .call(dragDrop)
    .on('mouseover', selectNode)

var textElements = g.append("g") // use g.append instead of svg.append to enable zoom
    .attr("class", "texts")
    .selectAll("text")
    .data(nodes)
    .enter().append("text")
    .text(function(node) {
        return node.label
    })
    .attr("font-size", 10)
    .attr("font-family", "sans-serif")
    .attr("text-anchor", "middle")
    .attr("fill", "#333")
    .attr("style", "font-weight:bold; -webkit-text-stroke: 1px #fff; text-shadow: 3px 3px 0 #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff")
    .attr("dx", 0)
    .attr("dy", 20)



simulation.nodes(nodes).on('tick', () => {
    nodeElements
        .attr('cx', function(node) {
            return node.x
        })
        .attr('cy', function(node) {
            return node.y
        })
    textElements
        .attr('x', function(node) {
            return node.x
        })
        .attr('y', function(node) {
            return node.y
        })
    linkElements.attr("d", function(link) {
        return "M" + link.source.x + "," + link.source.y + " L" + link.target.x + "," + link.target.y;
    });
    labelElements
        .attr('x', function(link) {
            return link.target.x
        })
        .attr('y', function(link) {
            return link.target.y
        })
})

simulation.force("link").links(links)
杰拉尔多·富塔多

这是预期的行为,因为您要重新启动仿真:

simulation.alphaTarget(0.7).restart()

除此之外,该行应位于“开始”侦听器中,而不应位于“拖动”侦听器中:每秒重启几次模拟没有意义,您知道吗?

回到问题:您想要的结果(“我希望我的力向图在拖动一个节点时保持镇定”)并不清楚。什么是“保持冷静”但是,如果我理解正确,则可以简单地修复所有其他节点。

由于您没有提供运行代码,因此这里是Mike Bostock使用此示例的演示我在这里所做的只是:

node.each(function(d){
    d.fx = d.x;
    d.fy = d.y;
})

这是修改后的bl.ocks:https ://bl.ocks.org/anonymous/93c0c9af8c729b62b1b194841298bc49/ede207278c873aa311133133782e9fb70e7504ed622

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在D3js强制有向图中为节点实现突出显示和过渡效果?

d3.js 强制有向图:如何使节点大小取决于链接的值?

如何更改d3JS强制有向图中所有突出显示的节点的颜色?

D3js强制有向图改变连接强度

将节点动态添加到d3.js强制有向图

如何仅将新节点添加到d3强制有向图?

d3js强制大量节点

d3js强制定向图-单击节点以弹出从JSON读取的信息框

D3js的节点和链接拖动行为

d3.js节点在强制布局中快速拖动时“跳回”

D3v4强制有向图-localStorage断开链接和节点的连接

d3js(v4)画布强制布局,节点上带有文本

D3js在enter()上强制重复节点

d3js强制节点xy的起始位置

d3js使用固定节点创建强制布局

D3js强制布局更新节点和链接

d3js强制定向-在悬停到节点上时,高亮显示/着色链接的节点和链接?

如何在 d3.js 中调整力有向图的大小?

强制有向图节点粘在中心

如何在d3js v5中创建具有可变半径的蜂群图?

如何使用d3js拖动完整的svg?

D3.js强制有向图,通过使边缘彼此排斥来减少边缘交叉

Javascript / D3.js /带标签的强制有向图

D3.js强制有向图,每组颜色不同吗?

我正在绘制 3d 图,我希望颜色不那么“不同”

d3 强制有向图节点在过滤后停留在固定位置

D3强制有向图ajax更新

D3力向图:拖动时节点不停留在鼠标位置

鼠标悬停时强制有向图更改所有连接节点的颜色