브라우저의 이해 #2 히스토리 그리고 history API
2019-03-14
D3.js 를 사용하여 데이터 시각화하기 #4 Custom Bar Charts
2018-10-31
Explanation
오늘은 예제로 사용하던 막대 그래프를 기반으로 간단하게 스타일을 조금 수정하고 툴팁 박스를 추가로 만들어보려 합니다.
[이전글 참고]
D3.js 를 사용하여 데이터 시각화하기 #1 Line Charts
D3.js 를 사용하여 데이터 시각화하기 #2 Bar Charts
D3.js 를 사용하여 데이터 시각화하기 #3 Pie Charts
아직 초보이기 때문에 글에는 수많은 추측과 오류를 담고 있습니다.
예제 코드 출처
https://beta.observablehq.com/@mbostock/d3-pie-chart
아래의 코드는 위 예제 코드를 아주 조금 수정한 내용입니다.
1 2 3 4 5 6 7 8 9 10 |
<!doctype html> <html lang="ko"> <head> <title>D3 bar chart example</title> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body> <script src="custom-bar-chart.js"></script> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
//custom-bar-chart.js const width = 400; const height = 400; const margin = {top: 40, left: 40, bottom: 40, right: 40}; const data = [ {name: 'A', value: 10, color: '#5487b1'}, {name: 'B', value: 29, color: '#63a1af'}, {name: 'C', value: 32, color: '#7ab8aa'}, {name: 'D', value: 25, color: '#93caa8'}, {name: 'E', value: 23, color: '#add7a8'}, {name: 'F', value: 15, color: '#c6e3a7'} ]; const x = d3.scaleBand() .domain(data.map(d => d.name)) .range([margin.left, width - margin.right]) .padding(0.4); const y = d3.scaleLinear() .domain([0, d3.max(data, d => d.value)]).nice() .range([height - margin.bottom, margin.top]); const xAxis = g => g .attr('transform', `translate(0, ${height - margin.bottom})`) .call(d3.axisBottom(x) .tickSizeOuter(0)) .call(g => g.select('.domain').remove()) // x축의 기준선을 지웁니다. .call(g => g.selectAll('line').remove()); // x축의 구분선을 지웁니다. // line chart와 동일 const yAxis = g => g .attr('transform', `translate(${margin.left}, 0)`) .call(d3.axisLeft(y)) .call(g => g.select('.domain').remove()) // y축의 기준선을 지웁니다. .call(g => g.selectAll('line') .attr('x2', width) .style('stroke', '#f5f5f5')) // y축의 구분선의 너비 값을 그래프의 너비값으로 설정하고 구분선의 색을 #f5f5f5 로 합니다. // svg의 가로 선은 x 속성의 값의 출발점으로 x2의 값을 끝점으로 선을 그립니다. const svg = d3.select('#svg-area').append('svg').style('width', width).style('height', height); // id 값이 svg-area인 엘리먼트를 찾고 그래프를 그릴 svg엘리먼트를 추가합니다. svg.append('g').call(xAxis); svg.append('g').call(yAxis); svg.append('g') .selectAll('rect').data(data).enter().append('rect') .attr('x', d => x(d.name)) .attr('y', d => y(d.value)) .attr('height', d => y(0) - y(d.value)) .attr("rx", 15) // svg rect 엘리먼트의 rx 속성으로 둥군 모서리를 설정할 수 있습니다. .attr('width', x.bandwidth()) .attr('fill', d => d.color) .attr('data-x', d => d.name) // 해당 데이터의 이름값을 데이터셋 x 값으로 추가합니다. .attr('data-y', d => d.value) // 해당 데이터의 벨류값을 데이터셋 y 값으로 추가합니다. .attr('data-color', d=> d.color); // 해당 데이터의 컬러값을 데이터셋 color 값으로 추가합니다. svg.node(); const rectEl = document.getElementsByTagName('rect'); // 현재 도큐멘트의 rect 엘리먼트들을 담습니다. const tooltop = document.getElementById('tooltip'); // 미리 html에 만들어 놓은 id값 tooltip의 엘리먼트를 담습니다. for(const el of rectEl) { // 도큐멘트의 rect 엘리먼트들에게 마우스 오버 이벤트를 추가합니다. el.addEventListener('mouseover', (event) => { // 해당 rect 엘리먼트 const target = event.target; // 툴팁이 위치할 x축의 값입니다. // svg 엘리먼트는 offset 값을 가지고 있지 않아서, 해당 엘리먼트의 데이터셋으로 추가한 x 값과 막대의 넓이 그리고 툴팁의 너비값을 이용해 정의합니다. const positionLeft = Number(target.getAttribute('x')) + Number(x.bandwidth()/2) - tooltop.clientWidth/2; // 툴팁이 위치한 y축의 값입니다. // 역시 offset 값이 없기 때문에 막대의 높이값과 툴팁의 높이값을 이용해 정의합니다. // 마지막의 5는 막대와 툴팁의 여백값입니다. const positionTop = height - margin.top - target.getAttribute('height') - tooltop.clientHeight - 5; // 아까 추가한 rect 데이터셋의 컬러값을 가져옵니다. const color = target.dataset.color; // 아까 추가한 rect 데이터셋의 벨류값을 가져옵니다. const value = target.dataset.y; // 벨류값을 text로 넣습니다. tooltop.innerText = value; // 툴팁의 배경색을 막대 색과 같게 합니다. tooltop.style.background = color; // 툴팁의 위치를 지정합니다. tooltop.style.top = positionTop + 'px'; tooltop.style.left = positionLeft + 'px'; // 투명하게 했던 툴팁을 보이게 합니다. tooltop.style.opacity = 1; }); } |
ES6 문법을 사용해서 최신 브라우저에서 테스트해야 할 것 같습니다. 저는 최신 버전의 크롬에서 테스트 하였습니다.
SVG의 엘리먼트들은 일반적인 DOM의 엘리먼트과 거의 동일하게 자바스크립트로 제어할 수 있어서, SVG에 대해 공부좀 하고 D3.js API들도 좀 많이 알게되면 정말 다양하게 편하게 만들 수 있을거 같아요.