여러분이 사용하고 계신 브라우저는 HTML5를 지원하지 않기 때문에 몇몇 요소가 제대로 보이도록 JScript를 사용하고 있습니다. 하지만 여러분의 브라우저 설정에서 스크립트 기능이 꺼져있으므로, 현재 페이지를 제대로 확인하시려면 스크립트 기능을 켜주셔야 합니다. HTML 그래픽 - Scripting

HTML 그래픽 – Scripting

1주전 작성

SVG Scripting

SVG(Scalable Vector Graphics)는 단순히 정적인 그래픽이 아니라 JavaScript를 사용하여 동적으로 조작할 수 있는 강력한 웹 기술이다. JavaScript로 SVG 요소를 생성, 수정, 애니메이션화하여 인터랙티브한 웹 경험을 만들 수 있다.

JavaScript로 SVG 요소 조작하기

SVG는 HTML과 마찬가지로 DOM(Document Object Model)을 가지고 있어 JavaScript로 쉽게 접근하고 조작할 수 있다. SVG 요소의 속성을 변경하거나 새로운 요소를 추가하는 것이 가능하다.

기본적인 SVG 요소 접근

JavaScript에서 SVG 요소에 접근하는 방법은 HTML 요소에 접근하는 방법과 거의 동일하다.

const svgElement = document.getElementById('mySvg');
const circleElement = document.getElementById('myCircle');
circleElement.setAttribute('r', '30');
circleElement.setAttribute('fill', 'blue');

위 코드는 ID가 ‘myCircle’인 SVG 원 요소를 찾아 반지름과 색상을 변경한다.

SVG 요소 동적 생성

JavaScript를 사용하여 새로운 SVG 요소를 동적으로 생성하고 추가할 수 있다.

const svgNS = "http://www.w3.org/2000/svg";
const svg = document.getElementById("mySvg");
const circle = document.createElementNS(svgNS, "circle");
circle.setAttribute("cx", "100");
circle.setAttribute("cy", "100");
circle.setAttribute("r", "50");
circle.setAttribute("fill", "red");
svg.appendChild(circle);

여기서 주목할 점은 일반 HTML 요소와 달리 SVG 요소를 생성할 때는 createElementNS 메서드를 사용하고 SVG 네임스페이스를 지정해야 한다는 것이다.

💡 중요 포인트:
• SVG 요소 생성 시에는 항상 createElementNS 메서드와 SVG 네임스페이스를 사용한다.
• SVG 요소에 스타일을 적용할 때는 setAttribute 메서드를 사용하거나 style 객체를 직접 조작한다.

SVG 요소에 이벤트 추가하기

SVG 요소에도 클릭, 마우스오버 등의 이벤트를 추가하여 사용자 상호작용을 구현할 수 있다.

기본 이벤트 핸들링

SVG 요소에 이벤트 리스너를 추가하는 예제다.


<svg id="mySvg" width="300" height="200">
<circle id="interactiveCircle" cx="100" cy="100" r="50" fill="green" /></svg>
<script>
const circle = document.getElementById('interactiveCircle');
circle.addEventListener('click', function() {
this.setAttribute('fill', 'red');
});
circle.addEventListener('mouseover', function() {
this.setAttribute('r', '60');
});
circle.addEventListener('mouseout', function() {
this.setAttribute('r', '50');
this.setAttribute('fill', 'green');
});
</script>

이 예제에서는 원을 클릭하면 색상이 변경되고, 마우스를 올리면 크기가 커졌다가 마우스를 떼면 원래대로 돌아온다.

드래그 앤 드롭 구현하기

SVG 요소에 드래그 앤 드롭 기능을 구현하는 예제다.


<svg id="svgContainer" width="400" height="300" style="border: 1px solid #ccc">
<circle id="draggableCircle" cx="100" cy="100" r="30" fill="purple" /></svg>
<script>
const circle = document.getElementById('draggableCircle');
let isDragging = false;
let offset = { x: 0, y: 0 };
circle.addEventListener('mousedown', function(event) {
isDragging = true;
const pt = svgContainer.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
const svgPt = pt.matrixTransform(svgContainer.getScreenCTM().inverse());
offset.x = svgPt.x - parseFloat(circle.getAttribute('cx'));
offset.y = svgPt.y - parseFloat(circle.getAttribute('cy'));
});
document.addEventListener('mousemove', function(event) {
if (!isDragging) return;
const pt = svgContainer.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
const svgPt = pt.matrixTransform(svgContainer.getScreenCTM().inverse());
circle.setAttribute('cx', svgPt.x - offset.x);
circle.setAttribute('cy', svgPt.y - offset.y);
});
document.addEventListener('mouseup', function() {
isDragging = false;
});
</script>

이 예제는 원을 드래그하여 SVG 캔버스 내 어디든 이동할 수 있게 한다. 마우스 좌표를 SVG 좌표계로 변환하는 방법에 주목할 필요가 있다.

SVG DOM 특징

SVG DOM은 HTML DOM과 유사하지만 몇 가지 중요한 차이점이 있다. SVG DOM을 효과적으로 조작하려면 이러한 특징을 이해하는 것이 중요하다.

특징 HTML DOM SVG DOM
요소 생성 createElement() createElementNS()
속성 접근 element.attribute = value element.setAttribute(name, value)
좌표계 픽셀 기반 사용자 정의 좌표계
스타일 적용 CSS 또는 style 속성 속성 값 또는 CSS
이벤트 처리 표준 DOM 이벤트 표준 DOM 이벤트 + SVG 특화 이벤트

SVG 좌표 변환

SVG 좌표계와 화면 좌표계 간의 변환은 대화형 SVG 작업에서 중요한 개념이다.

function getMousePositionInSVG(evt, svg) {
const pt = svg.createSVGPoint();
pt.x = evt.clientX;
pt.y = evt.clientY;
const svgPoint = pt.matrixTransform(svg.getScreenCTM().inverse());
return {
x: svgPoint.x,
y: svgPoint.y
};
}

이 함수는 마우스 이벤트에서 화면 좌표를 가져와 SVG 내부 좌표계로 변환한다.

실용적인 SVG 스크립팅 예제

SVG와 JavaScript를 함께 사용하는 몇 가지 실용적인 예제를 살펴보자.

대화형 데이터 시각화

간단한 막대 그래프를 생성하고 상호 작용을 추가하는 예제다.


<svg id="barChart" width="400" height="300">
<line x1="50" y1="250" x2="50" y2="50" stroke="black" />
<line x1="50" y1="250" x2="350" y2="250" stroke="black" /></svg>
<script>
const svg = document.getElementById('barChart');
const data = [45, 70, 30, 85, 60];
const barWidth = 40;
const spacing = 20;
const maxValue = 100;
const baseY = 250;
data.forEach((value, index) => {
const barHeight = (value / maxValue) * 200;
const x = 70 + (barWidth + spacing) * index;
const y = baseY - barHeight;
const bar = document.createElementNS("http://www.w3.org/2000/svg", "rect");
bar.setAttribute("x", x);
bar.setAttribute("y", y);
bar.setAttribute("width", barWidth);
bar.setAttribute("height", barHeight);
bar.setAttribute("fill", "steelblue");
bar.addEventListener('mouseover', function() {
this.setAttribute('fill', 'orange');
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", x + barWidth/2);
text.setAttribute("y", y - 10);
text.setAttribute("text-anchor", "middle");
text.setAttribute("id", "barValue");
text.textContent = value;
svg.appendChild(text);
});
bar.addEventListener('mouseout', function() {
this.setAttribute('fill', 'steelblue');
const text = document.getElementById('barValue');
if(text) svg.removeChild(text);
});
svg.appendChild(bar);
});
</script>

이 예제는 데이터 배열을 기반으로 대화형 막대 그래프를 생성한다. 각 막대에 마우스를 올리면 색상이 변경되고 데이터 값이 표시된다.

SVG 기반 도형 그리기 도구

간단한 SVG 그리기 도구를 구현하는 예제다.




<div>
<button id="rectBtn">사각형</button>
<button id="circleBtn">원</button>
<button id="clearBtn">지우기</button>
</div>
<svg id="drawingBoard" width="400" height="300" style="border: 1px solid #ccc; margin-top: 10px"></svg>
<script>
const svg = document.getElementById('drawingBoard');
const svgNS = "http://www.w3.org/2000/svg";
let currentTool = null;
let isDrawing = false;
let startPoint = { x: 0, y: 0 };
let currentShape = null;
document.getElementById('rectBtn').addEventListener('click', () => currentTool = 'rect');
document.getElementById('circleBtn').addEventListener('click', () => currentTool = 'circle');
document.getElementById('clearBtn').addEventListener('click', () => {
while (svg.lastChild) {
svg.removeChild(svg.lastChild);
}
});
svg.addEventListener('mousedown', startDrawing);
svg.addEventListener('mousemove', draw);
svg.addEventListener('mouseup', endDrawing);
function getMousePosition(evt) {
const pt = svg.createSVGPoint();
pt.x = evt.clientX;
pt.y = evt.clientY;
return pt.matrixTransform(svg.getScreenCTM().inverse());
}
function startDrawing(evt) {
if (!currentTool) return;
isDrawing = true;
const pt = getMousePosition(evt);
startPoint = { x: pt.x, y: pt.y };
if (currentTool === 'rect') {
currentShape = document.createElementNS(svgNS, 'rect');
currentShape.setAttribute('x', startPoint.x);
currentShape.setAttribute('y', startPoint.y);
currentShape.setAttribute('width', 0);
currentShape.setAttribute('height', 0);
currentShape.setAttribute('fill', 'transparent');
currentShape.setAttribute('stroke', 'black');
} else if (currentTool === 'circle') {
currentShape = document.createElementNS(svgNS, 'circle');
currentShape.setAttribute('cx', startPoint.x);
currentShape.setAttribute('cy', startPoint.y);
currentShape.setAttribute('r', 0);
currentShape.setAttribute('fill', 'transparent');
currentShape.setAttribute('stroke', 'black');
}
svg.appendChild(currentShape);
}
function draw(evt) {
if (!isDrawing || !currentShape) return;
const pt = getMousePosition(evt);
if (currentTool === 'rect') {
const width = pt.x - startPoint.x;
const height = pt.y - startPoint.y;
currentShape.setAttribute('width', Math.abs(width));
currentShape.setAttribute('height', Math.abs(height));
currentShape.setAttribute('x', width < 0 ? pt.x : startPoint.x); currentShape.setAttribute('y', height < 0 ? pt.y : startPoint.y); } else if (currentTool === 'circle') { const dx = pt.x - startPoint.x; const dy = pt.y - startPoint.y; const radius = Math.sqrt(dx * dx + dy * dy); currentShape.setAttribute('r', radius); } } function endDrawing() { isDrawing = false; currentShape = null; } </script>[/code]

이 도구는 사용자가 버튼을 클릭하여 그리기 도구를 선택하고 SVG 캔버스에서 드래그하여 사각형이나 원을 그릴 수 있게 한다.

SVG 애니메이션과 SMIL 대신 JavaScript 사용

SMIL 애니메이션 대신 JavaScript를 사용하여 SVG 애니메이션을 구현할 수 있다. 이는 더 나은 브라우저 호환성과 애니메이션에 대한 더 많은 제어를 제공한다.

JavaScript로 간단한 애니메이션 구현


[code lang="markup"]<svg id="animationSvg" width="400" height="200">
<circle id="movingCircle" cx="50" cy="100" r="20" fill="red" /></svg>
<script>
const circle = document.getElementById('movingCircle');
let position = 50;
let direction = 1;
function animate() {
position += direction * 2;
if (position >= 350) direction = -1;
if (position <= 50) direction = 1; circle.setAttribute('cx', position); requestAnimationFrame(animate); } animate(); </script>[/code]

이 예제는 원이 SVG 캔버스의 왼쪽에서 오른쪽으로, 다시 왼쪽으로 움직이는 간단한 애니메이션을 보여준다.

💡 애니메이션 최적화 팁:
• 복잡한 애니메이션에는 항상 requestAnimationFrame을 사용한다.
• 성능을 위해 변형이 필요한 속성만 업데이트한다.
• 가능하면 SVG 변환(transform) 속성을 활용한다.

SVG와 React, Vue 등의 프레임워크 통합

모던 웹 프레임워크를 사용하면 SVG 스크립팅을 더 효율적으로 관리할 수 있다.

React에서의 SVG 활용

[code lang="js"]import React, { useState } from 'react';
const SVGComponent = () => {
const [radius, setRadius] = useState(20);
const handleMouseOver = () => {
setRadius(40);
};
const handleMouseOut = () => {
setRadius(20);
};
return (
<svg width="300" height="200">
<circle
cx="150"
cy="100"
r={radius}
fill="blue"
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
/>
</svg>
);
};
export default SVGComponent;

이 React 컴포넌트는 마우스를 올렸을 때 크기가 변하는 SVG 원을 보여준다. React의 상태 관리 기능을 사용하여 원의 반지름을 제어한다.

Vue.js에서의 SVG 활용


<template>
<svg width="300" height="200">
<circle
:cx="150"
:cy="100"
:r="radius"
fill="green"
@mouseover="radius = 40"
@mouseout="radius = 20"
/>
</svg>
</template>
<script>
export default {
data() {
return {
radius: 20
};
}
};
</script>

Vue.js에서도 반응형 데이터 바인딩을 통해 SVG 요소를 쉽게 조작할 수 있다.

SVG 스크립팅 최적화 기법

대규모 웹 애플리케이션에서 SVG를 사용할 때는 성능 최적화가 중요하다.

성능 최적화 팁

  • 불필요한 요소 업데이트 피하기: 변경된 요소만 업데이트한다.
  • DOM 조작 최소화: 대량의 SVG 요소를 추가할 때는 DocumentFragment를 사용한다.
  • 복잡한 계산 캐싱: 좌표 변환이나 복잡한 계산 결과를 캐싱한다.
  • requestAnimationFrame 사용: 애니메이션에는 setTimeout 대신 requestAnimationFrame을 사용한다.
  • 오프스크린 렌더링 최소화: 화면에 보이지 않는 요소는 업데이트를 연기한다.

대량의 요소를 효율적으로 추가하는 방법

function addManyElements(svg, count) {
const fragment = document.createDocumentFragment();
const svgNS = "http://www.w3.org/2000/svg";
for (let i = 0; i < count; i++) { const circle = document.createElementNS(svgNS, 'circle'); circle.setAttribute('cx', Math.random() * 400); circle.setAttribute('cy', Math.random() * 300); circle.setAttribute('r', Math.random() * 10 + 2); circle.setAttribute('fill', `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`); fragment.appendChild(circle); } svg.appendChild(fragment); }[/code]

이 함수는 DocumentFragment를 사용하여 많은 수의 원을 SVG에 효율적으로 추가한다. 이렇게 하면 DOM 업데이트를 한 번만 수행하여 성능이 크게 향상된다.

💡 성능 개선을 위한 주요 기법:
• SVG 요소 수를 적절하게 관리한다.
• 복잡한 경로 대신 간단한 기본 도형을 사용한다.
• 대규모 데이터 시각화에는 캔버스(Canvas) 사용을 고려한다.
• GPU 가속을 위해 transform 속성을 활용한다.

실제 프로젝트에서의 SVG 스크립팅 적용

SVG 스크립팅은 다음과 같은 실제 프로젝트에서 활용할 수 있다.

활용 분야 사례 장점
데이터 시각화 대화형 차트, 그래프, 대시보드 사용자 상호작용, 애니메이션 제어, 동적 데이터 업데이트
맵 및 다이어그램 인터랙티브 지도, 네트워크 다이어그램 확대/축소, 팬, 선택 기능 구현
그래픽 에디터 온라인 디자인 도구, 다이어그램 편집기 사용자가 생성한 콘텐츠, 실시간 미리보기
인터랙티브 인포그래픽 교육용 자료, 설명이 포함된 시각화 단계별 애니메이션, 사용자 진행 제어

SVG와 JavaScript를 함께 사용하면 웹 그래픽의 가능성이 크게 확장된다. 확장성, 접근성, 인터랙티브성을 모두 갖춘 풍부한 시각적 경험을 만들 수 있다. SVG 스크립팅을 마스터하면 웹 개발자와 디자이너 모두에게 귀중한 도구가 된다.

참고
Mingg`s Diary
밍구
공부 목적 블로그