Data Driven Document의 약자로 데이터를 시각화하기 위해서 사용하는 라이브러입니다.
D3.js는 SVG(Scalable Vector Graphic)와 DOM API를 통해 해상도 깨짐이 없는 시각화가 가능합니다.
Selection은 _groups 프로퍼티를 갖고 있으며, _groups 프로퍼티에는 배열이 바인딩되어 있습니다.
이 배열 안에 선택된 DOM Element들이 존재합니다.
Selection {
_groups: [DOM Element,,,],
_parents: [DOM Element]
}
d3.select(css-selector)
인수로 CSS 선택자를 전달하면 문서 내 첫 번재로 매칭된 하나의 DOM Element를 _groups 배열의 요소로 갖는 Selection 객체를 반환합니다.
만약 매칭된 DOM Element가 없다면 빈 Selection을 반환합니다.
d3.selectAll(css-selector)
: 인수로 CSS 선택자를 전달하면 매칭되는 모든 DOM Element들을 _groups 배열의 요소로 갖는 Selection 객체를 반환합니다.
만약 매칭된 DOM Element가 없다면 빈 Selection을 반환합니다.
반환된 Selection 객체로 select
나 selectAll
메서드 체이닝이 가능하며 중첩된 DOM Element를 선택할 수도 있습니다.
d3.select('body')
.select('.canvas')
.select('svg')
.selectAll('rect');
Selection 객체를 통해 선택된 DOM Element을 동적으로 다룰 수 있습니다.
d3.selection.text(text)
: 선택된 DOM Element의 text content를 추가하거나 수정할 수 있습니다.
반환값은 기존 선택된 DOM Element를 선택한 Selection 객체를 반환합니다.
d3.selection.append(tag-name)
: 새롭게 DOM Element를 생성하고, 선택된 DOM Element 끝에 추가합니다.
이때 새롭게 추가된 Element를 선택한 Selection 객체를 반환합니다.
d3.selection.remove()
: 선택된 DOM Element들을 제거합니다.
반환값은 제거된 DOM Element를 선택한 Selection 객체를 반환합니다.
d3.selection.attr(attr-name, attr-value)
: 선택된 DOM Element에 HTML Attribute를 설정합니다.
반환값은 기존 선택된 DOM Element를 선택한 Selection 객체를 반환합니다.
d3.selection.property(property-name, property-value)
: attr 메서드로 접근되지 않는 것들은 property 메서드로 접근해여 설정합니다. 반환값은 기존 선택된 DOM Element를 선택한 Selection 객체를 반환합니다.
d3.selection.style(property, property-value)
: 선택된 DOM Element에 인라인 스타일을 설정합니다.
반환값은 기존 선택된 DOM Element를 선택한 Selection 객체를 반환합니다.
<!DOCTYPE html>
<html lang='ko'>
<head>
,,,
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div class="canvas"></div>
<script>
const svg = d3.select('canvas')
.append('svg') // svg 태그 생성하고 추가
.attr('width', 1000) // svg 태그 width 어트리뷰트 설정
.attr('height', 800); // svg 태그 height 어트리뷰트 설정
svg.append('text') // text 태그 생성하고 추가
.attr('x', 500) // text 태그 x 어트리뷰트 설정
.attr('y', 400) // text 태그 y 어트리뷰트 설정
.text('hello, world'); // text 태그의 text content 설정
svg.select('text')
.remove(); // text 태그 제거
</script>
</body>
</html>
D3에서는 Selection 객체에 대해서 .data()
메서드를 통해 선택된 요소에 데이터를 바인딩할 수 있습니다.
데이터가 바인딩된 Selection 객체는 Manipulation 메서드의 인수로 콜백함수를 전달하여 바인딩된 배열의 요소에 접근할 수 있습니다.
요소에 바인딩될 데이터의 타입은 배열 타입만을 허용되며, 배열의 요소로는 자바스크립트에서 사용되는 모든 값을 허용합니다.
즉, 선택된 요소에 바인딩된 배열의 요소를 순차적으로 바인딩해줍니다.
d3.selection.data([...value])
만약 Selection 객체 내 선택된 요소가 없다면 바인딩되지 않으며, 선택된 요소가 하나라면 배열의 첫 번째 요소만이 바인딩됩니다.
<!DOCTYPE html>
<html lang='ko'>
<head>
,,,
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div class="canvas"></div>
<script>
const svg = d3.select('.canvas')
.append('svg')
.attr('width', 1000)
.attr('height', 800);
const dataArr = ['a', 'b', 'c', 'd'];
// 1. 선택된 요소가 없는 경우
svg.select('.text1')
.data(dataArr)
.text((d, idx) => {
console.log(d);
console.log(idx);
return d;
});
// 2. 선택된 요소가 하나인 경우
svg.select('.text2')
.append('p')
.attr('class', 'text2')
.data(dataArr)
.text((d, idx) => {
console.log(d); // a
console.log(idx); // 0
return d;
});
</script>
</body>
</html>
위 코드에서 1번의 경우 svg 태그 내 text1을 class 값으로 갖는 태그가 존재하지 않아 빈 Selection 객체를 반환합니다. 빈 Selection 객체에 데이터를 바인딩하더라도 선택된 요소가 존재하지 않기 때문에 데이터가 바인딩되지 않습니다.
2번의 경우에는 append 메서드를 통해 p 태그를 선택하고, class 값으로 text2를 설정했습니다. 그러므로 Selection 객체에는 하나의 요소가 존재하며 데이터를 바인딩하면 배열의 첫 번째 요소만이 선택된 하나의 요소에 바인딩됩니다.
즉, 선택된 요소의 순서대로 바인딩한 배열의 요소가 바인딩됩니다.
d3.selection.data([...value]).enter()
이는 앞서 작성된 코드의 1번 같은 경우 빈 Selection 객체에 데이터를 바인딩하는 경우 바인딩된 데이터의 요소만큼 빈 객체를 생성하고 데이터를 바인딩시켜 줍니다.
2번의 경우에도 선택된 요소가 하나이므로 배열의 첫 번째 요소만이 바인딩되지만, enter 메서드를 체이닝하면 바인딩되지 않은 남은 요소의 개수만큼 가상 객체를 생성하고 순차적으로 데이터를 바인딩시켜 줍니다.
<!DOCTYPE html>
<html lang='ko'>
<head>
,,,
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div class="canvas"></div>
<script>
const svg = d3.select('.canvas')
.append('svg')
.attr('width', 1000)
.attr('height', 800);
const dataArr = ['a', 'b', 'c', 'd'];
// 1. 선택된 요소가 없는 경우
svg.select('.text1')
.data(dataArr)
.enter()
.text((d, idx) => {
console.log(d); // a b c d
console.log(idx); // 0 1 2 3
return d;
});
// 2. 선택된 요소가 하나인 경우
svg.select('.text2')
.append('text')
.attr('class', 'text2')
.data(dataArr)
.enter()
.text((d, idx) => {
console.log(d); // a b c d
console.log(idx); // 0 1 2 3
return d;
});
</script>
</body>
</html>
d3.selection.exit()
.remove()
메서드를 통해 DOM Element를 제거할 수 있습니다.<!DOCTYPE html>
<html lang='ko'>
<head>
,,,
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div class="canvas"></div>
<script>
const svg = d3.select('.canvas')
.append('svg')
.attr('width', 1000)
.attr('height', 800);
const dataArr = ['a', 'b'];
svg.append('text');
svg.append('text');
svg.append('text');
svg.selectAll('text')
.data(dataArr)
.exit()
.remove();
</script>
</body>
</html>
위 코드에서 svg 태그의 자식으로 text 태그 3개를 생성했습니다. 이후 text 태그를 선택하고 데이터를 바인딩하면 마지막 text 태그에는 데이터가 바인딩되지 않아 exit 메서드를 호출하면 바인딩되지 않은 마지막 text 태그가 선택되고 이를 remove 메서드를 호출하여 제거할 수 있습니다.
d3.selection.transition()
: 선택된 DOM Element에 transition 효과의 시작을 알리는 메서드입니다.
d3.selection.duration(number)
: transition 효과의 지속시간을 설정하는 메서드입니다.
d3.selection.ease(fn)
: ease 메서드는 transition 효과의 motion을 설정하는 메서드입니다.
d3.selection.delay(number)
: delay 메서드는 transition 효과의 지연시간을 설정하는 메서드입니다.
<!DOCTYPE html>
<html lang='ko'>
<head>
,,,
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div class="canvas"></div>
<script>
const svg = d3.select('.canvas')
.append('svg')
.attr('width', 1000)
.attr('height', 800);
svg.transition() // transition 효과 설정
.duration(3000) // transition 지속 시간 3000ms
.style('background-color', 'red'); // background-color값 red로
</script>
</body>
</html>
참고로 transition 메서드를 호출한 이후에는 .style
메서드를 체이닝하여 스타일을 설정해주어야 합니다.
d3.selection.on(eventType, function)
data
메서드로 데이터를 바인딩한 경우 두 번째 인수로 바인딩한 데이터가 전달됩니다.D3.js의 Sacales 함수의 경우 실제 데이터를 시각적으로 표현할 때 실제 데이터를 비율에 맞게 값을 변환시켜주는 함수입니다.
이는 실제 데이터를 x 좌표와 y 좌표의 값으로 변환시키기 위해서 사용합니다.
d3.scaleLinear().domain([min, max]).range([min, max])
const yScale = d3.scaleLinear()
.domain([0, 60000000])
.range([0, 800]);
yScale(0); // 0
yScale(30000000); // 400
yScale(60000000); // 800
위 코드는 대한민국 총 인구수를 비율에 맞게 값을 변환하려고 합니다. 이때 range 메서드에는 좌표 평면상 표현될 값의 범위를 작성하고, domain에는 실제 값의 범위를 작성합니다.
반환된 함수의 인수로 domain에 전달한 범위의 실제 값을 전달하면 ranage 메서드의 인수로 전달한 범위에 맞게 변환한 값을 반환합니다.
d3.scaleBand().domain([...value]).range([min, max])
const xScale = d3.scaleBand()
.domain(['1970', '1980', '1990', '2000', '2010'])
.range([0, 1000]);
xScale('1970'); // 0
xScale('1980'); // 250
xScale('1990'); // 500
xScale('2000'); // 750
xScale('2000'); // 1000
위 코드에서처럼 domain에는 사용되는 실제 값 모두 작성하고, range에는 좌표 평면상 표현될 값의 범위를 작성합니다.
이후 반환되는 함수의 인수로 domain에 작성된 값을 전달하면 일정한 간격으로 띄어진 값을 반환합니다.
d3.scale.padding(number)
D3.js에서는 차트의 축을 생성하기 위한 함수를 제공합니다. 이때 인수로 scale 함수를 인수로 전달해줍니다.
축을 생성하기 위해 scale 함수를 전달하면 range 범위를 통해 적절하게 생성합니다.
이후 d3.selection.call(axis)
메서드의 인수로 Axis 함수의 반환값을 전달하여 축을 실제로 그릴 수 있습니다.
d3.axisTop(scale)
: 상단 부분에 수평축을 생성합니다. 이때 인수로 Scale 함수를 전달하면 그에 맞는 축을 생성하게 됩니다.
d3.axisRight(scale)
: 우측 부분에 수직축을 생성합니다.
d3.axisBottom(scale)
: 하단 부분에 수평축을 생성합니다.
d3.axisLeft(scale)
: 좌측 부분에 수직축을 생성합니다.
const xScale = d3.scaleBand()
.domain(['a', 'b', 'c', 'd'])
.range([0, 1000]);
const yScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 800]);
const xAxis = svg.append('g')
.call(xScale);
const yAxis = svg.append('g')
.attr('transform', `translate(0, ${800})`)
.call(yScale);
d3.axis.ticks(number)
: 생성될 축에 표시될 단위의 개수를 설정할 수 있습니다.
d3.axis.tickFormat(d => { ,,, })
: 생성될 축에 표시될 단위의 포맷을 설정할 수 있습니다. 콜백 함수를 전달하면 콜백 함수의 인수로 기존 단위값이 전달되고 반환값으로 작성된 값이 단위로 표시됩니다.
생성기는 데이터를 받아 SVG 코드를 반환합니다. 즉, 생성기란 path 요소를 통해 원하는 path 도형을 생성하기 위한 d 어트리뷰트의 값을 생성해주는 함수입니다.
d3.line().x(function).y(function)
const lineGenerator = d3.line()
.x(d => xScale(d.실제x값))
.y(d => yScale(d.실제y값));
const lineEl = svg.append('path')
.attr('d', line(data))
.attr('fill', 'none')
.attr('stroke', 'blue')
.attr('stroke-width', 3);
d3.arc().innerRadius().outerRadius().startAngle().endAngle()
const arcGenerator = d3.arc()
.innerRadius(50) // 내부 원의 반지름 50
.outerRadius(100) // 외부 원의 반지름 100
.startAngle(Math.PI * 0.5) // 영역의 시작 위치는 90도(Math.PI * 0.5)
.endAngle(Math.PI * 1); // 영역의 끝 위치는 180도(Math.PI * 1)
d3.select('svg')
.append('g')
.append('path')
.attr('fill', '#0066ff')
.attr('d', arcGenerator());
위 코드는 아래와 같은 결과를 출력하게 됩니다. 내부 빈 영역의 반지름은 50, 전체 원의 반지름은 100, 영역의 시작 지점은 0, 영역의 끝 지점은 180도를 나타내는 Math.PI로 설정되었습니다.
레이아웃은 데이터를 입력받아 그래프를 그릴 수 있는 데이터로 변환하여 반환해줍니다.
d3.pie().value(d => d.value)
const sampleData = [
{ year: 'a', value: 78 },
{ year: 'b', value: 28 },
{ year: 'c', value: 36 },
{ year: 'd', value: 48 }
];
const pieLayout = d3.pie().value(d => d.value);
pieLayout(sampleData);
/*
[
{
"data": {
"year": "a",
"value": 78
},
"index": 0,
"value": 78,
"startAngle": 0,
"endAngle": 2.579412915578988,
"padAngle": 0
},
{
"data": {
"year": "b",
"value": 28
},
"index": 3,
"value": 28,
"startAngle": 5.357242209279437,
"endAngle": 6.283185307179586,
"padAngle": 0
},
{
"data": {
"year": "c",
"value": 36
},
"index": 2,
"value": 36,
"startAngle": 4.166743940550673,
"endAngle": 5.357242209279437,
"padAngle": 0
},
{
"data": {
"year": "d",
"value": 48
},
"index": 1,
"value": 48,
"startAngle": 2.579412915578988,
"endAngle": 4.166743940550673,
"padAngle": 0
}
]
*/
위 그림과 같이 실제 값을 PI 그래프를 그리기 위한 데이터로 변환하여 반환시켜줍니다.
const data = [10, 30, 20, 100, 50, 80];
const arcGen = d3.arc()
.outerRadius(100)
.innerRadius(50);
const pieLayout = d3.pie()
.value(d => d);
const pieGoup = d3.select('svg')
.append('g');
pieGroup.selectAll()
.data(pieLayout(data))
.enter()
.append('path')
.attr('d', arcGen)
.attr('fill', '#0066ff')
.attr('stroke', '#fff')
.attr('stroke-width', 3);