D3.js
기술
import React, { useRef, useEffect } from 'react';
import * as d3 from 'd3';
const Skills = ({ title }) => {
const widthRef = useRef();
const svgRef = useRef();
const skillData = [
{
title: 'HTML & CSS',
value: 65,
color: '#d9a1ab',
},
{
title: 'JS',
value: 70,
color: '#d9a1ab',
},
{
title: 'React.js',
value: 52,
color: '#ceb5df',
},
{
title: 'Vue.js',
value: 35,
color: '#ceb5df',
},
{
title: 'Node.js',
value: 15,
color: '#dcdfb5',
},
{
title: 'Java',
value: 50,
color: '#dcdfb5',
},
{
title: 'Oracle',
value: 38,
color: '#bddfb5',
},
{
title: 'Mysql',
value: 28,
color: '#bddfb5',
},
];
useEffect(() => {
const svg = d3.select(svgRef.current);
const chart = svg.append('g').attr('transform', `translate(40, 20)`);
const margin = 50;
// width: 렌더링될 때의 화면 너비에 맞춤
const width = widthRef.current.clientWidth - margin * 2;
const height = 350 - margin * 2;
// scaleLinear() 사용
const yScale = d3.scaleLinear().range([height, 0]).domain([0, 100]);
const xScale = d3
.scaleBand()
.range([0, width])
.domain(skillData.map(d => d.title))
.padding(0.4);
// x축, y축 만들기
chart.append('g').call(d3.axisLeft(yScale));
chart
.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale));
// 수평 그리드선 만들기
chart
.append('g')
.call(d3.axisLeft().scale(yScale).tickSize(-width, 0, 0).tickFormat(''));
// bar 만들기
const barGroups = chart.selectAll().data(skillData).enter().append('g');
barGroups
.append('rect')
.attr('x', d => xScale(d.title))
.attr('y', height)
.attr('width', xScale.bandwidth())
.attr('height', 0)
.attr('fill', d => d.color);
// 점수 셋팅: display none
barGroups
.append('text')
.attr('class', 'value')
.attr('id', d => d.title.substring(0, 2))
.attr('x', d => xScale(d.title) + xScale.bandwidth() / 2)
.attr('y', d => yScale(d.value) + 30)
.attr('text-anchor', 'middle')
.text(d => d.value)
.style('display', 'none');
// 애니메이션 효과
barGroups
.selectAll('rect')
.transition()
.duration(800)
.attr('y', d => yScale(d.value))
.attr('height', d => height - yScale(d.value));
// 마우스 이벤트
barGroups
.selectAll('rect')
.on('mouseenter', function (r, i) {
d3.select(this)
.transition()
.duration(300)
.attr('opacity', 0.7)
.attr('x', d => xScale(d.title) - 2)
.attr('width', xScale.bandwidth() + 4);
// 점수 표시
d3.selectAll(`#${i.title.substring(0, 2)}`).style('display', 'block');
// 가이드 눈금선 표시
const y = yScale(i.value);
chart
.append('line')
.attr('id', 'limit')
.attr('x1', 0)
.attr('y1', y)
.attr('x2', width)
.attr('y2', y);
})
.on('mouseleave', function () {
d3.select(this)
.transition()
.duration(300)
.attr('opacity', 1)
.attr('x', d => xScale(d.title))
.attr('width', xScale.bandwidth());
d3.selectAll('.value').style('display', 'none');
chart.selectAll('#limit').remove();
});
barGroups
.selectAll('text')
.on('mouseenter', function (r, i) {
d3.selectAll(`#${i.title.substring(0, 2)}`).style('display', 'block');
})
.on('mouseleave', function () {
d3.selectAll('.value').style('display', 'none');
});
}, []);
// code...
};
1---2name: features-23description: import React, { useRef, useEffect } from 'react';4---5
6# D3.js
7
8## 기술
9
10```js
11import React, { useRef, useEffect } from 'react';
12import * as d3 from 'd3';
13
14const Skills = ({ title }) => {
15 const widthRef = useRef();
16 const svgRef = useRef();
17
18 const skillData = [
19 {
20 title: 'HTML & CSS',
21 value: 65,
22 color: '#d9a1ab',
23 },
24 {
25 title: 'JS',
26 value: 70,
27 color: '#d9a1ab',
28 },
29 {
30 title: 'React.js',
31 value: 52,
32 color: '#ceb5df',
33 },
34 {
35 title: 'Vue.js',
36 value: 35,
37 color: '#ceb5df',
38 },
39 {
40 title: 'Node.js',
41 value: 15,
42 color: '#dcdfb5',
43 },
44 {
45 title: 'Java',
46 value: 50,
47 color: '#dcdfb5',
48 },
49 {
50 title: 'Oracle',
51 value: 38,
52 color: '#bddfb5',
53 },
54 {
55 title: 'Mysql',
56 value: 28,
57 color: '#bddfb5',
58 },
59 ];
60
61 useEffect(() => {
62 const svg = d3.select(svgRef.current);
63 const chart = svg.append('g').attr('transform', `translate(40, 20)`);
64
65 const margin = 50;
66 // width: 렌더링될 때의 화면 너비에 맞춤
67 const width = widthRef.current.clientWidth - margin * 2;
68 const height = 350 - margin * 2;
69
70 // scaleLinear() 사용
71 const yScale = d3.scaleLinear().range([height, 0]).domain([0, 100]);
72 const xScale = d3
73 .scaleBand()
74 .range([0, width])
75 .domain(skillData.map(d => d.title))
76 .padding(0.4);
77
78 // x축, y축 만들기
79 chart.append('g').call(d3.axisLeft(yScale));
80 chart
81 .append('g')
82 .attr('transform', `translate(0, ${height})`)
83 .call(d3.axisBottom(xScale));
84
85 // 수평 그리드선 만들기
86 chart
87 .append('g')
88 .call(d3.axisLeft().scale(yScale).tickSize(-width, 0, 0).tickFormat(''));
89
90 // bar 만들기
91 const barGroups = chart.selectAll().data(skillData).enter().append('g');
92 barGroups
93 .append('rect')
94 .attr('x', d => xScale(d.title))
95 .attr('y', height)
96 .attr('width', xScale.bandwidth())
97 .attr('height', 0)
98 .attr('fill', d => d.color);
99
100 // 점수 셋팅: display none
101 barGroups
102 .append('text')
103 .attr('class', 'value')
104 .attr('id', d => d.title.substring(0, 2))
105 .attr('x', d => xScale(d.title) + xScale.bandwidth() / 2)
106 .attr('y', d => yScale(d.value) + 30)
107 .attr('text-anchor', 'middle')
108 .text(d => d.value)
109 .style('display', 'none');
110
111 // 애니메이션 효과
112 barGroups
113 .selectAll('rect')
114 .transition()
115 .duration(800)
116 .attr('y', d => yScale(d.value))
117 .attr('height', d => height - yScale(d.value));
118
119 // 마우스 이벤트
120 barGroups
121 .selectAll('rect')
122 .on('mouseenter', function (r, i) {
123 d3.select(this)
124 .transition()
125 .duration(300)
126 .attr('opacity', 0.7)
127 .attr('x', d => xScale(d.title) - 2)
128 .attr('width', xScale.bandwidth() + 4);
129
130 // 점수 표시
131 d3.selectAll(`#${i.title.substring(0, 2)}`).style('display', 'block');
132
133 // 가이드 눈금선 표시
134 const y = yScale(i.value);
135 chart
136 .append('line')
137 .attr('id', 'limit')
138 .attr('x1', 0)
139 .attr('y1', y)
140 .attr('x2', width)
141 .attr('y2', y);
142 })
143 .on('mouseleave', function () {
144 d3.select(this)
145 .transition()
146 .duration(300)
147 .attr('opacity', 1)
148 .attr('x', d => xScale(d.title))
149 .attr('width', xScale.bandwidth());
150
151 d3.selectAll('.value').style('display', 'none');
152 chart.selectAll('#limit').remove();
153 });
154
155 barGroups
156 .selectAll('text')
157 .on('mouseenter', function (r, i) {
158 d3.selectAll(`#${i.title.substring(0, 2)}`).style('display', 'block');
159 })
160 .on('mouseleave', function () {
161 d3.selectAll('.value').style('display', 'none');
162 });
163 }, []);
164
165 // code...
166};
167```
168
169