gpt4 book ai didi

javascript - D3v4力向图-localStorage断开链接和节点

转载 作者:行者123 更新时间:2023-12-05 06:51:21 25 4
gpt4 key购买 nike

我正在尝试将 localStorage 与我的力导向图一起使用。我在每个 simulation.on("end", function()) 中都保存了 graph 变量,这可以正常工作。但是,当我重新加载页面时,使用 localStorage.getItem 并拖动节点,然后链接不再与节点连接。

你能帮我解决这个奇怪的问题吗?

请查看下面的截图和代码: localStorage-update-screenshot

'use strict';

var test = [];
var datatable;
var index = [];
var graph;

getLocal();
// load and save data
function getLocal() {
if (localStorage.getItem("graph") === null) {
graph = {
"nodes": [{'id': 1, 'lable': 1, 'group': 'search'}, {'id': 2, 'lable': 2, 'group': '1'}, {'id': 3, 'lable': 3, 'group': '2'}],
"links": [{'source': 1, 'target': 2, 'value': "1-2"},{'source': 2, 'target': 1, 'value': "2-1"},{'source': 1, 'target': 3, 'value': "1-3"},{'source': 2, 'target': 3, 'value': "2-3"}]
}} else {
graph = JSON.parse(localStorage.getItem("graph"));
};
};

// Graph variables
var firstLinks = true;

var w = 1680; //window.innerWidth;
var h = 850; //window.innerHeight;

var svg = d3.select("#svgData"),
scheme = ['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'],
width = +svg.attr(w),
height = +svg.attr(h),
color = d3.scaleOrdinal(d3.schemeCategory10);
//color = d3.scaleOrdinal(scheme);

// elements for data join
var link = svg.append("g").attr("class", "link").selectAll(".link"),
value = svg.append("g").selectAll(".link"),
node = svg.append("g").attr("class", "node").selectAll(".node"),
lable = svg.append("g").selectAll(".node"),
image = svg.append("g").selectAll(".node");

// simulation initialization
var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-30).distanceMax(300))
//.force('collision', d3.forceCollide().radius(30))
//.force("center", d3.forceCenter(w / 2, h / 2))
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(function (d) { if (d.length) {return d.length; } else { return 300;}}))
.force("x", d3.forceX().x(function(d) { if (d.group == "search") {return w / 2; } else { return " ";}}))
.force("y", d3.forceY().y(function(d) { if (d.group == "search") {return h / 2; } else { return h / 3;}}));
//.force("center", d3.forceCenter(w / 2, h / 2));

var marker = d3.select("#svgData").append('defs')
.append('marker')
.attr("id", "Triangle")
.attr('viewBox', '-0 -5 10 10')
.attr("refX", 25)
.attr("refY", 0)
.attr("markerUnits", 'userSpaceOnUse')
.attr("orient", 'auto')
.attr("markerWidth", 13)
.attr("markerHeight", 13)
.attr('xoverflow', 'visible')
.append('path')
.attr("d", 'M 0,-5 L 10 ,0 L 0,5');

// Add search result to graph data
function QuickSearch(value) {
var new_node = {};
new_node = {'id': value, 'lable': value, 'group': 'search'};
graph.nodes.findIndex(x => x.id == new_node.id) == -1 ? graph.nodes.push(new_node) : console.log("object already exists")
update();
};

update();

function update() {
// DATA JOIN
link = link.data(graph.links, d => d.id);

// EXIT
// Remove old links
link.exit().remove();

// ENTER
// Create new links as needed.
link = link.enter().append("path")
.attr("id", function(_, i) {
return "path" + i
})
.attr("marker-end", "url(#Triangle)")
.merge(link);

// DATA JOIN
value = value.data(graph.links, d => d.id);

// EXIT
value.exit().remove();

// ENTER
value = value.enter().append("text")
.attr("dy", -4)
.append("textPath")
.attr("xlink:href", function(_, i) {
return "#path" + i
})
.attr("startOffset", "50%")
.text(function(d) {
return d.value;
})
.merge(value);

// DATA JOIN
node = node.data(graph.nodes, d => d.id);

// EXIT
node.exit().remove();

// ENTER

node = node.enter().append("circle")
.attr('stroke-width', 3)
.attr('stroke', function(d) { if (d.group == "addr") {return '#1f77b4'; } else {return color(d.group)}})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.merge(node);

// DATA JOIN
lable = lable.data(graph.nodes, d => d.id);

// EXIT
lable.exit().remove();

// ENTER

lable = lable.enter().append("text")
.text(function (d) {
if(d.id.length > 10)
return d.id.substring(0,10)+'...';
else
return d.id;
})
.merge(lable);


// Set nodes, links, and alpha target for simulation
simulation.nodes(graph.nodes).on("tick", ticked);
simulation.force("link").links(graph.links).distance(function (d) { if (d.length) {return d.length; } else { return 200;}});
simulation.alphaTarget(1).restart();
simulation.on("end", function() {
node.each(function(d) {
d.fx = d.x;
d.fy = d.y;
});
localStorage.setItem("graph", JSON.stringify(graph));
console.log("saving");
});


function ticked() {
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });

lable
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y - 30; });

link
.attr("d", function(d) {
return "M" + d.source.x + "," + d.source.y
+ "L" + d.target.x + "," + d.target.y;
})
.attr("stroke-dasharray", function() {
return this.getTotalLength() - 25;
});

value
.attr("x", function(d) { return (d.source.x + d.target.x)/2; })
.attr("y", function(d) { return ((d.source.y + d.target.y)/2) - 10; });

}

// Zoom
var zoom = d3.zoom()
.scaleExtent([0, 10])
.on("zoom", zoomed);

d3.select("#svgData").call(zoom);

function zoomed() {
const currentTransform = d3.event.transform;
svg.selectAll("g").attr("transform", currentTransform);
}

function slided(d) {
zoom.scaleTo(svg, d3.select(this).property("value"));
}

// drag nodes
function dragstarted(d){
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(d){
d.fx = d3.event.x;
d.fy = d3.event.y;
//fix_nodes(d);
}

function dragended(d){
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = d.x;
d.fy = d.y;
}

// Preventing other nodes from moving while dragging one node
function fix_nodes(this_node) {
node.each(function(d) {
if (this_node != d) {
d.fx = d.x;
d.fy = d.y;
}
});
}
};

var deleteLeafs = function() {
var source = [];
var target = [];
link.each(function (d) {
source.push(d.source.index);
target.push(d.target.index);
});
node.each(function (d, i) {
if (i in source && i in target ) {
index.push(d);
}
})

};

最佳答案

D3-force 将链接中的源和目标属性从给定节点的某个标识符替换为节点的对象引用本身。因此,所有链接都由源对象和目标对象引用组成,节点对象引用自身。

使用 JSON.parse 和 JSON.stringify 存储/重新创建数据不会导致链接和节点共享对象引用的对象:节点和链接包含对不同对象的对象引用(即使它们看起来相同) :

下面查看链接源是否与节点相同,它是在力初始化之后,但在解析和字符串化之后不是。

var graph = {
nodes: [
{id: 1},
{id: 2}
],
links : [
{source: 1, target: 2}
]
}

var force = d3.forceSimulation()
.nodes(graph.nodes)
.force("link", d3.forceLink().id(d=>d.id).links(graph.links));

// force only:
console.log(graph.nodes[0] == graph.links[0].source);

// parsed:
graph = JSON.parse(JSON.stringify(graph))
console.log(graph.nodes[0] == graph.links[0].source);

// For comparison:
console.log("graph.nodes[0]:")
console.log(graph.nodes[0])
console.log("graph.links[0].source:")
console.log(graph.links[0].source)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

如果您只是创建/删除节点,您可以每次都从起始链接数组重新加载链接,只存储节点。然后,通过重新初始化节点和链接,您应该得到一个新的力布局,其中节点位置(以及链接)位于它们的最后位置。

如果您要添加/删除链接,那么您可以使用另一种解析对象的方法,例如 Flatted ,这似乎适用于强制布局:

var graph = {
nodes: [
{id: 1},
{id: 2}
],
links : [
{source: 1, target: 2}
]
}

var force = d3.forceSimulation()
.nodes(graph.nodes)
.force("link", d3.forceLink().id(d=>d.id).links(graph.links));

// force only:
console.log(graph.nodes[0] == graph.links[0].source);

// parsed:
graph = Flatted.parse(Flatted.stringify(graph))
console.log(graph.nodes[0] == graph.links[0].source);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatted@3.1.1/min.js"></script>

关于javascript - D3v4力向图-localStorage断开链接和节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66184275/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com