我已经使用svg在d3.js中建立了一个力向图,但最终该图变得很大,并且存在性能问题。我决定尝试在画布上执行此操作,因为我阅读它可以使东西变得更好,更快。但是现在我在缩放方面遇到了问题。我已经正确实现了缩放行为(我想),但是我只能在图形静止时进行缩放。在仿真发现平衡点之前,缩放行为不起作用。知道为什么吗?或有关该怎么办的任何提示?
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d, i) { return i; }))
.force("charge", d3.forceManyBody().strength( -5 ))
.force("center", d3.forceCenter(width / 2, height / 2));
force.nodes(data.nodes)
.on("tick", ticked)
force.force("link")
.links(data.links);
function ticked(){
context.clearRect(0, 0, width, height);
// Draw the links
data.links.forEach(function(d) {
// Draw a line from source to target.
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes
data.nodes.forEach(function(d, i) {
// Draws a complete arc for each node.
context.beginPath();
context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI, true);
context.fill();
});
};
// now the zooming part
canvas.call( d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed) )
function zoomed(d) {
context.save();
context.clearRect(0, 0, width, height);
context.translate(d3.event.transform.x, d3.event.transform.y);
context.scale(d3.event.transform.k, d3.event.transform.k);
// Draw the links ...
data.links.forEach(function(d) {
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes ...
data.nodes.forEach(function(d, i) {
context.beginPath();
context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI, true);
context.fill();
});
context.restore();
}
最佳答案
您的tick
函数不知道缩放所创建的任何变换。最好的方法是始终在刻度中应用变换(在任何缩放之前为identity transform)。这使您可以重用tick方法进行所有绘图。
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d, i) {
return d.id;
}))
.force("charge", d3.forceManyBody().strength(-5))
.force("center", d3.forceCenter(width / 2, height / 2));
force.nodes(data.nodes)
.on("tick", ticked);
force.force("link")
.links(data.links)
var trans = d3.zoomIdentity; //<-- identity transform
function ticked() {
context.save();
context.clearRect(0, 0, width, height);
context.translate(trans.x, trans.y); //<-- this always applies a transform
context.scale(trans.k, trans.k);
// Draw the links
data.links.forEach(function(d) {
// Draw a line from source to target.
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes
data.nodes.forEach(function(d, i) {
// Draws a complete arc for each node.
context.beginPath();
context.arc(d.x, d.y, 5, 0, 2 * Math.PI, true);
context.fill();
});
context.restore();
};
// now the zooming part
canvas.call(d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed))
function zoomed(d) {
trans = d3.event.transform; //<-- set to current transform
ticked(); //<-- use tick to redraw regardless of event
}
完整的运行代码:
<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<canvas width="500" height="500"></canvas>
<script>
var width = 500,
height = 500
canvas = document.querySelector("canvas"),
context = canvas.getContext("2d");
canvas = d3.select(canvas);
var data = {
"nodes": [{
"id": "Myriel",
"group": 1
}, {
"id": "Napoleon",
"group": 1
}, {
"id": "Mlle.Baptistine",
"group": 1
}, {
"id": "Mme.Magloire",
"group": 1
}, {
"id": "CountessdeLo",
"group": 1
}, {
"id": "Geborand",
"group": 1
}, {
"id": "Champtercier",
"group": 1
}, {
"id": "Cravatte",
"group": 1
}, {
"id": "Count",
"group": 1
}, {
"id": "OldMan",
"group": 1
}, {
"id": "Labarre",
"group": 2
}, {
"id": "Valjean",
"group": 2
}, {
"id": "Marguerite",
"group": 3
}, {
"id": "Mme.deR",
"group": 2
}, {
"id": "Isabeau",
"group": 2
}, {
"id": "Gervais",
"group": 2
}, {
"id": "Tholomyes",
"group": 3
}, {
"id": "Listolier",
"group": 3
}, {
"id": "Fameuil",
"group": 3
}, {
"id": "Blacheville",
"group": 3
}, {
"id": "Favourite",
"group": 3
}, {
"id": "Dahlia",
"group": 3
}, {
"id": "Zephine",
"group": 3
}, {
"id": "Fantine",
"group": 3
}, {
"id": "Mme.Thenardier",
"group": 4
}, {
"id": "Thenardier",
"group": 4
}, {
"id": "Cosette",
"group": 5
}, {
"id": "Javert",
"group": 4
}, {
"id": "Fauchelevent",
"group": 0
}],
"links": [{
"source": "Napoleon",
"target": "Myriel",
"value": 1
}, {
"source": "Mlle.Baptistine",
"target": "Myriel",
"value": 8
}, {
"source": "Mme.Magloire",
"target": "Myriel",
"value": 10
}, {
"source": "Mme.Magloire",
"target": "Mlle.Baptistine",
"value": 6
}, {
"source": "CountessdeLo",
"target": "Myriel",
"value": 1
}, {
"source": "Geborand",
"target": "Myriel",
"value": 1
}, {
"source": "Champtercier",
"target": "Myriel",
"value": 1
}, {
"source": "Cravatte",
"target": "Myriel",
"value": 1
}, {
"source": "Count",
"target": "Myriel",
"value": 2
}, {
"source": "OldMan",
"target": "Myriel",
"value": 1
}, {
"source": "Valjean",
"target": "Labarre",
"value": 1
}, {
"source": "Valjean",
"target": "Mme.Magloire",
"value": 3
}, {
"source": "Valjean",
"target": "Mlle.Baptistine",
"value": 3
}, {
"source": "Valjean",
"target": "Myriel",
"value": 5
}, {
"source": "Marguerite",
"target": "Valjean",
"value": 1
}, {
"source": "Mme.deR",
"target": "Valjean",
"value": 1
}, {
"source": "Isabeau",
"target": "Valjean",
"value": 1
}, {
"source": "Gervais",
"target": "Valjean",
"value": 1
}, {
"source": "Listolier",
"target": "Tholomyes",
"value": 4
}, {
"source": "Fameuil",
"target": "Tholomyes",
"value": 4
}, {
"source": "Fameuil",
"target": "Listolier",
"value": 4
}, {
"source": "Blacheville",
"target": "Tholomyes",
"value": 4
}, {
"source": "Blacheville",
"target": "Listolier",
"value": 4
}, {
"source": "Blacheville",
"target": "Fameuil",
"value": 4
}, {
"source": "Favourite",
"target": "Tholomyes",
"value": 3
}, {
"source": "Favourite",
"target": "Listolier",
"value": 3
}, {
"source": "Favourite",
"target": "Fameuil",
"value": 3
}, {
"source": "Favourite",
"target": "Blacheville",
"value": 4
}, {
"source": "Dahlia",
"target": "Tholomyes",
"value": 3
}, {
"source": "Dahlia",
"target": "Listolier",
"value": 3
}, {
"source": "Dahlia",
"target": "Fameuil",
"value": 3
}, {
"source": "Dahlia",
"target": "Blacheville",
"value": 3
}, {
"source": "Dahlia",
"target": "Favourite",
"value": 5
}, {
"source": "Zephine",
"target": "Tholomyes",
"value": 3
}, {
"source": "Zephine",
"target": "Listolier",
"value": 3
}, {
"source": "Zephine",
"target": "Fameuil",
"value": 3
}, {
"source": "Zephine",
"target": "Blacheville",
"value": 3
}, {
"source": "Zephine",
"target": "Favourite",
"value": 4
}, {
"source": "Zephine",
"target": "Dahlia",
"value": 4
}, {
"source": "Fantine",
"target": "Tholomyes",
"value": 3
}, {
"source": "Fantine",
"target": "Listolier",
"value": 3
}, {
"source": "Fantine",
"target": "Fameuil",
"value": 3
}, {
"source": "Fantine",
"target": "Blacheville",
"value": 3
}, {
"source": "Fantine",
"target": "Favourite",
"value": 4
}, {
"source": "Fantine",
"target": "Dahlia",
"value": 4
}, {
"source": "Fantine",
"target": "Zephine",
"value": 4
}, {
"source": "Fantine",
"target": "Marguerite",
"value": 2
}]
}
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d, i) {
return d.id;
}))
.force("charge", d3.forceManyBody().strength(-5))
.force("center", d3.forceCenter(width / 2, height / 2));
force.nodes(data.nodes)
.on("tick", ticked);
force.force("link")
.links(data.links)
var trans = d3.zoomIdentity;
function ticked() {
context.save();
context.clearRect(0, 0, width, height);
context.translate(trans.x, trans.y);
context.scale(trans.k, trans.k);
// Draw the links
data.links.forEach(function(d) {
// Draw a line from source to target.
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes
data.nodes.forEach(function(d, i) {
// Draws a complete arc for each node.
context.beginPath();
context.arc(d.x, d.y, 5, 0, 2 * Math.PI, true);
context.fill();
});
context.restore();
};
// now the zooming part
canvas.call(d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed))
function zoomed(d) {
trans = d3.event.transform;
ticked();
}
</script>
</body>
</html>
关于javascript - 在 Canvas 上放大d3js强制模拟,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39807094/