168 lines
7.7 KiB
Markdown
168 lines
7.7 KiB
Markdown
### SVG와 JavaScript 이벤트 및 인터랙티브 그래프 제작
|
|
|
|
SVG는 JavaScript와 결합하여 클릭, 마우스 이동 등 다양한 이벤트를 처리할 수 있어 인터랙티브한 그래픽을 구현하는 데 이상적입니다. 여기에 `getBoundingClientRect()`를 활용한 좌표 변환, 그래프/차트 제작 기법, 그리고 드래그&드롭 기능을 위한 포인터 이벤트까지 설명하겠습니다.
|
|
|
|
---
|
|
|
|
### SVG와 JavaScript 이벤트
|
|
|
|
SVG 요소는 HTML 요소처럼 DOM 이벤트(`click`, `mouseover`, `mousemove` 등)를 지원합니다. 이를 통해 사용자 상호작용을 감지하고 동적으로 반응할 수 있습니다.
|
|
|
|
#### 주요 이벤트
|
|
| 이벤트 | 설명 | 사용 예시 |
|
|
|-----------------|--------------------------------------------------------------------------------------|-------------------------------|
|
|
| `click` | 요소를 클릭할 때 발생. | 버튼, 토글 기능 |
|
|
| `mouseover` | 마우스가 요소 위로 올라갈 때 발생. | 호버 효과, 툴팁 표시 |
|
|
| `mouseout` | 마우스가 요소를 벗어날 때 발생. | 호버 효과 해제 |
|
|
| `mousemove` | 마우스가 요소 위에서 움직일 때 발생. | 드래그, 실시간 좌표 추적 |
|
|
| `mousedown` | 마우스 버튼을 누를 때 발생. | 드래그 시작 |
|
|
| `mouseup` | 마우스 버튼을 뗄 때 발생. | 드래그 종료 |
|
|
|
|
#### 예시: 클릭과 호버 이벤트
|
|
```xml
|
|
<svg width="200" height="200">
|
|
<circle id="myCircle" cx="100" cy="100" r="30" fill="blue" />
|
|
</svg>
|
|
<script>
|
|
const circle = document.getElementById("myCircle");
|
|
circle.addEventListener("click", () => circle.setAttribute("fill", "red"));
|
|
circle.addEventListener("mouseover", () => circle.setAttribute("r", "40"));
|
|
circle.addEventListener("mouseout", () => circle.setAttribute("r", "30"));
|
|
</script>
|
|
```
|
|
- 클릭 시 색상이 빨강으로, 호버 시 반지름이 커짐.
|
|
|
|
---
|
|
|
|
### `getBoundingClientRect()`를 활용한 마우스 좌표 변환
|
|
|
|
SVG는 자체 좌표계를 사용하므로, 브라우저의 클라이언트 좌표(`clientX`, `clientY`)를 SVG 좌표로 변환해야 합니다. `getBoundingClientRect()`와 `getScreenCTM()`를 활용하면 이를 쉽게 처리할 수 있습니다.
|
|
|
|
#### 변환 과정
|
|
1. `getBoundingClientRect()`: SVG 요소의 뷰포트 내 경계 상자 좌표를 반환.
|
|
2. `getScreenCTM()`: SVG 좌표계와 화면 좌표계 간 변환 행렬을 반환.
|
|
3. `point` 객체: SVG 좌표로 변환된 값을 계산.
|
|
|
|
#### 예시: 마우스 위치에 원 추가
|
|
```xml
|
|
<svg id="svgCanvas" width="400" height="400">
|
|
</svg>
|
|
<script>
|
|
const svg = document.getElementById("svgCanvas");
|
|
svg.addEventListener("click", (e) => {
|
|
const rect = svg.getBoundingClientRect();
|
|
const matrix = svg.getScreenCTM().inverse();
|
|
const point = svg.createSVGPoint();
|
|
point.x = e.clientX - rect.left;
|
|
point.y = e.clientY - rect.top;
|
|
const svgPoint = point.matrixTransform(matrix);
|
|
|
|
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
circle.setAttribute("cx", svgPoint.x);
|
|
circle.setAttribute("cy", svgPoint.y);
|
|
circle.setAttribute("r", "10");
|
|
circle.setAttribute("fill", "green");
|
|
svg.appendChild(circle);
|
|
});
|
|
</script>
|
|
```
|
|
- 클릭한 위치에 녹색 원을 추가. `viewBox`가 설정된 경우에도 정확한 SVG 좌표로 변환됨.
|
|
|
|
---
|
|
|
|
### SVG를 활용한 인터랙티브 그래프 및 차트 제작
|
|
|
|
SVG는 데이터 시각화(막대그래프, 선그래프 등)에 적합하며, 이벤트와 동적 업데이트를 통해 인터랙티브하게 만들 수 있습니다.
|
|
|
|
#### 예시: 인터랙티브 막대그래프
|
|
```xml
|
|
<svg id="chart" width="400" height="200" viewBox="0 0 400 200">
|
|
<g id="bars"></g>
|
|
</svg>
|
|
<script>
|
|
const data = [50, 100, 80, 120];
|
|
const svg = document.getElementById("chart");
|
|
const barsGroup = document.getElementById("bars");
|
|
|
|
data.forEach((value, index) => {
|
|
const bar = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
bar.setAttribute("x", index * 100);
|
|
bar.setAttribute("y", 200 - value);
|
|
bar.setAttribute("width", "80");
|
|
bar.setAttribute("height", value);
|
|
bar.setAttribute("fill", "steelblue");
|
|
|
|
bar.addEventListener("mouseover", () => bar.setAttribute("fill", "orange"));
|
|
bar.addEventListener("mouseout", () => bar.setAttribute("fill", "steelblue"));
|
|
bar.addEventListener("click", () => alert(`Value: ${value}`));
|
|
|
|
barsGroup.appendChild(bar);
|
|
});
|
|
</script>
|
|
```
|
|
- 막대그래프에서 호버 시 색상 변경, 클릭 시 값 표시.
|
|
|
|
#### 기법
|
|
- **데이터 바인딩**: 배열 데이터를 SVG 요소로 변환.
|
|
- **축 추가**: `<line>`과 `<text>`로 x/y축 렌더링.
|
|
- **툴팁**: `mouseover`로 동적 `<text>` 요소 표시.
|
|
- **업데이트**: 데이터 변경 시 `removeChild`와 `appendChild`로 DOM 갱신.
|
|
|
|
---
|
|
|
|
### 드래그&드롭 (Pointer Events)
|
|
|
|
SVG에서 드래그&드롭은 `pointerdown`, `pointermove`, `pointerup` 이벤트를 사용해 구현합니다. 이들은 `mousedown` 등보다 멀티터치와 스타일러스 입력을 지원해 더 현대적입니다.
|
|
|
|
#### 예시: 드래그 가능한 원
|
|
```xml
|
|
<svg id="svgCanvas" width="400" height="400">
|
|
<circle id="dragCircle" cx="100" cy="100" r="30" fill="purple" />
|
|
</svg>
|
|
<script>
|
|
const svg = document.getElementById("svgCanvas");
|
|
const circle = document.getElementById("dragCircle");
|
|
let isDragging = false;
|
|
|
|
circle.addEventListener("pointerdown", (e) => {
|
|
isDragging = true;
|
|
circle.setPointerCapture(e.pointerId); // 포인터 캡처 설정
|
|
});
|
|
|
|
svg.addEventListener("pointermove", (e) => {
|
|
if (!isDragging) return;
|
|
const rect = svg.getBoundingClientRect();
|
|
const matrix = svg.getScreenCTM().inverse();
|
|
const point = svg.createSVGPoint();
|
|
point.x = e.clientX - rect.left;
|
|
point.y = e.clientY - rect.top;
|
|
const svgPoint = point.matrixTransform(matrix);
|
|
|
|
circle.setAttribute("cx", svgPoint.x);
|
|
circle.setAttribute("cy", svgPoint.y);
|
|
});
|
|
|
|
svg.addEventListener("pointerup", (e) => {
|
|
isDragging = false;
|
|
circle.releasePointerCapture(e.pointerId); // 포인터 캡처 해제
|
|
});
|
|
</script>
|
|
```
|
|
- 원을 클릭해 드래그하면 마우스 위치로 이동.
|
|
|
|
#### Pointer Events 주요 속성
|
|
- `pointerdown`: 드래그 시작.
|
|
- `pointermove`: 드래그 중 위치 업데이트.
|
|
- `pointerup`: 드래그 종료.
|
|
- `setPointerCapture`: 해당 요소가 포인터 이벤트를 독점.
|
|
- `releasePointerCapture`: 캡처 해제.
|
|
|
|
#### 추가 팁
|
|
- **제약 조건**: `Math.min`/`Math.max`로 이동 범위 제한.
|
|
- **스냅 효과**: 좌표를 일정 단위로 반올림.
|
|
|
|
---
|
|
|
|
### 결론
|
|
|
|
SVG와 JavaScript 이벤트는 클릭, 호버, 드래그 등 다양한 상호작용을 가능하게 하며, `getBoundingClientRect()`와 좌표 변환으로 정확한 위치 계산을 지원합니다. 이를 활용해 막대그래프 같은 데이터 시각화를 만들고, 포인터 이벤트를 통해 드래그&드롭 기능을 추가할 수 있습니다. 실무에서는 D3.js 같은 라이브러리로 복잡한 차트를 쉽게 구현하거나, 순수 JavaScript로 가볍고 커스터마이징된 인터랙션을 만들 수 있습니다. SVG의 유연성과 이벤트 처리 능력은 웹에서 동적이고 사용자 친화적인 경험을 제공하는 데 큰 강점입니다. |