Tree Visualization에 대해 자세히 알고싶다면 d3-hierarchy 참고
d3.stratify()
를 사용하여 hierarchy형태의 포멧으로 변환한다.{
"name": "Eve",
"children": [
{
"name": "Cain"
},
{
"name": "Seth",
"children": [
{
"name": "Enos"
},
{
"name": "Noam"
}
]
},
{
"name": "Abel"
},
// ...
import { select, json, tree, hierarchy, linkHorizontal } from 'd3';
const width = document.body.clientWidth;
const height = document.body.clientHeight;
const svg = select('div')
.append('svg')
.attr('width', width)
.attr('height', height);
const treeLayout = tree().size([height, width]); // (*)
json('world-country.json').then(data => {
const root = hierarchy(data); // (*)
const links = treeLayout(root).links(); // (*)
const linkPathGenerator = linkHorizontal() // (*)
.x(d => d.y)
.y(d => d.x);
svg
.selectAll('path')
.data(links)
.enter()
.append('path')
.attr('d', linkPathGenerator);
});
/* css */
path {
fill: none;
stroke: black;
}
linkVertical()
을 사용한다. const linkPathGenerator = linkVertical()
.x(d => d.x)
.y(d => d.y);
svg
.selectAll('text')
.data(root.descendants()) // (*)
.enter()
.append('text')
.attr('x', d => d.y)
.attr('y', d => d.x)
.text(d => d.data.data.id);
});
1️⃣ Center labels vertically
svg
.selectAll('text')
.data(root.descendants())
.enter()
.append('text')
.attr('x', d => d.y)
.attr('y', d => d.x)
.attr('dy', '0.32em') // (*)
.text(d => d.data.data.id);
2️⃣ CSS
white shadow
를 추가한다.path {
fill: none;
stroke: #57bdc3;
}
text {
text-shadow:
-1px -1px 3px white,
-1px 1px 3px white,
1px -1px 3px white,
1px 1px 3px white;
pointer-events: none; /* Prevent cursor for text */
}
.attr('font-size', d => 3.2 - d.depth + 'em')
: Tree Depth에 따라서 font-size
를 조정한다. (Depth가 깊어질 수록 font-size
가 작아짐).attr('text-anchor', d => (d.children ? 'middle' : 'start'))
: Country name은 start
로, 다른 텍스트는 middle
로 정렬한다.import { select, json, tree, hierarchy, linkHorizontal } from 'd3';
const width = document.body.clientWidth;
const height = document.body.clientHeight;
// Add margins
const margin = { top: 0, left: 80, right: 50, bottom: 0 };
const innerWidth = width - margin.right - margin.left;
const innerHeight = height - margin.top - margin.bottom;
const g = select('div')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g') // (*)
.attr('transform', `translate(${margin.left}, ${margin.top})`);
// tree() sets x and y value
const treeLayout = tree().size([innerHeight, innerWidth]);
json('world-country.json').then(data => {
// Create tree layout
const root = hierarchy(data);
const links = treeLayout(root).links();
const linkPathGenerator = linkHorizontal()
.x(d => d.y)
.y(d => d.x);
g.selectAll('path')
.data(links)
.enter()
.append('path')
.attr('d', linkPathGenerator);
// Add text label to node
g.selectAll('text')
.data(root.descendants())
.enter()
.append('text')
.attr('x', d => d.y)
.attr('y', d => d.x)
.attr('dy', '0.32em') // Center labels vertically
.attr('text-anchor', d => (d.children ? 'middle' : 'start')) // Align only country names in start
.attr('font-size', d => 3.2 - d.depth + 'em') // Makes country names smaller / others bigger
.text(d => d.data.data.id);
});
g
태그를 이용한 레이아웃을 2개로 나눈다. (스무스한 zooming)const svg = select('div')
.append('svg')
.attr('width', width)
.attr('height', height);
const zoomG = svg.append('g'); // (*)
const g = zoomG // (*)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
// Add Zooming
svg.call(
zoom().on('zoom', ({ transform }) => {
zoomG.attr('transform', transform); // (*)
})
);
import { select, json, tree, hierarchy, linkHorizontal, zoom } from 'd3';
const width = document.body.clientWidth;
const height = document.body.clientHeight;
// Add margins
const margin = { top: 0, left: 80, right: 50, bottom: 0 };
const innerWidth = width - margin.right - margin.left;
const innerHeight = height - margin.top - margin.bottom;
const svg = select('div')
.append('svg')
.attr('width', width)
.attr('height', height);
const zoomG = svg.append('g');
const g = zoomG
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
// tree() sets x and y value
const treeLayout = tree().size([innerHeight, innerWidth]);
// Add Zooming
svg.call(
zoom().on('zoom', ({ transform }) => {
zoomG.attr('transform', transform);
})
);
json('world-country.json').then(data => {
// Create tree layout
const root = hierarchy(data);
const links = treeLayout(root).links();
const linkPathGenerator = linkHorizontal()
.x(d => d.y)
.y(d => d.x);
g.selectAll('path')
.data(links)
.enter()
.append('path')
.attr('d', linkPathGenerator);
// Add text label to node
g.selectAll('text')
.data(root.descendants())
.enter()
.append('text')
.attr('x', d => d.y)
.attr('y', d => d.x)
.attr('dy', '0.32em') // Center labels vertically
.attr('text-anchor', d => (d.children ? 'middle' : 'start')) // Align only country names in start
.attr('font-size', d => 3.2 - d.depth + 'em') // Makes country names smaller / others bigger
.text(d => d.data.data.id);
});