D3.js forceSimulation 노드에 여러 svg를 포함하는 방법
d3.js에서 노드에 여러 svg 요소를 포함하고 텍스트와 같은 요소를 동시에 드래그하는 방법을 설명한다.
예제 프로그램
D3.js forceSimulation의 forceRadial 데모이다. 요소를 원형으로 배치할 수 있다.
예제 코드
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 v7 force simulation group element</title>
</head>
<body>
<svg width="800" height="600"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
// 1. 그리려는 데이터 준비
var width = document.querySelector("svg").clientWidth;
var height = document.querySelector("svg").clientHeight;
var nodesData = [];
for (var i = 0; i < 50; i++) {
nodesData.push({
"x": width * Math.random(),
"y": height * Math.random(),
"r": 40 * Math.random() + 5
});
}
// 2. svg 요소 추가
var nodeGroup = d3.select("svg")
.selectAll("g")
.data(nodesData)
.enter()
.append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
nodeGroup.append("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("r", function (d) { return d.r })
.attr("fill", "Gold")
.attr("stroke", "black")
.append("title")
.text("This is title.");
nodeGroup.append("text")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; })
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.style("fill", "steelblue")
.text("Ball")
.append("title")
.text("This is title.");
// 3. forceSimulation 설정
var simulation = d3.forceSimulation()
.force("collide",
d3.forceCollide()
.radius(function (d) { return d.r + 1 }))
.force("charge", d3.forceManyBody())
.force("x", d3.forceX().strength(0.05).x(width / 2))
.force("y", d3.forceY().strength(0.05).y(height / 2));
simulation
.nodes(nodesData)
.on("tick", ticked);
// 4. forceSimulation 그림 업데이트 함수
function ticked() {
nodeGroup.select("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
nodeGroup.select("text")
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; });
}
// 5. 드래그 이벤트 함수
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = d3.event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
</body>
</html>
설명
이 예제 프로그램은 forceSimulation
을 사용한다. forceSimulation
에 대한 자세한 내용은 여기를 참조한다.
프로그램의 일부만 설명한다.
var nodeGroup = d3.select("svg")
.selectAll("g")
.data(nodesData)
.enter()
.append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
먼저 그룹 요소를 나타내는 <g>
요소를 만든다. <g>
요소에 노드에 대한 데이터 배열을 할당하고 드래그 이벤트를 등록한다.
그런 다음에 <g>
태그의 자식 요소로 <circle>
과 <text>
를 설정한다. <g>
요소에 노드의 데이터 배열이 할당되어 있으므로 참조하여 사용한다. 그리고, <circle>
과 <text>
의 자식 요소로 title
도 설정할 수 있다. 예제 프로그램 노드에 커서를 놓으면 제목 문자열이 표시된다. (단, 태블릿이나 스마트폰에서는 표시되지 않는다.)
nodeGroup.append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r })
.attr("fill", "Gold")
.attr("stroke", "black")
.append("title")
.text("This is title.");
nodeGroup.append("text")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.style("fill", "steelblue")
.text("Ball")
.append("title")
.text("This is title.");
forceSimulation
을 사용하지 않고, Drag으로만 동작의 경우에는 이벤트 함수를 아래와 같이 변경한다.
function dragged(event, d) {
d3.select(this).select("circle")
.attr("cx", d.x = event.x)
.attr("cy", d.y = event.y);
d3.select(this).select("text")
.attr("x", d.x = event.x)
.attr("y", d.y = event.y);
}
최종 수정 : 2024-01-18