SVG이미지의 특징은 xml로 되어있어, 각 요소들에 대한 접근이 가능하다는 것이다.
일반적인 비트맵이미지로는 수행할 수 없는 엘리먼트
단위의 제어등이 가능하다. (특정 Path의 색상만을 바꾼다던지, 크기를 변경한다던지, 보이지 않게 한다던지 등..)
여러가지 프레임워크들이 이 SVG이미지를 지원하며, 여러 방법으로 제어할 수 있게 되어있다.
이 글은 JS에서 제공하는 SVG처리방법, 그리고 React에서 그것을 사용하는 방법에 대한 기록이다. 특히, svg의 엘리먼트에 접근하는 것에 중점을 둔다.
SVG파일 하나만으로 여러 작업을 수행할 수 있도록, SVG파일 내의 엘리먼트등을 자유롭게 조작할 수 있게 하는 것.
연습을 위한 목표 사양
ReactSVG
모듈을 통하여 SVG
가 DOM
에 로드가 완료되었을때의 시점에 접근을 할 수 있다.ReactSVG
컴포넌트의 afterInjection
어트리뷰트의 svg
가 DOM
에 로드가 완료된 SVG
의 SVGSVGElement
타입이며, 이 것을 이용하여 Element
에 접근할 수 있다.SVGSVGElement
의 classList
에 추가되는 문자열은 CSS
에서 접근할 수 있는 class
가 된다.How to import a svg file in javascript
위 링크에서 말하는 내용은 다음과 같다.
img
태그로 가져올 수 있다.
매우 간편하지만, 이 경우 SVG파일의 엘리먼트 조작이 안된다. 즉, SVG이미지를 사용하는 의미가 퇴색된다.
object
태그로 가져올 수 있다.
type
을 image/svg+xml
로 하여 가져올 수 있다.
이 방법만이 위 사이트에서 제시하는 유일한 엘리먼트가 조작 가능한 SVG import 타입이다.
iframe
태그로 가져올 수 있다.
하지만 이 방법은 SVG의 엘리먼트를 가져올수도, 성능도 나빠 위 사이트에서는 전혀 추천하지 않는 방법이다.
하기 코드는 위 링크에 기재된 예시 코드이다.
다음으로 구분되어있다.
<!DOCTYPE html>
<html lang="en">
<body>
<object type="image/svg+xml"
data=
"https://media.geeksforgeeks.org/wp-content/cdn-uploads/20210205161739/Screenshot-2021-02-05-161721.png"
class="logo">
GFG Logo
</object>
</body>
</html>
이를 예시로서, 다음과 같이 구현하였다.
import kr_map from './kr.svg'
import React from 'react'
export default class map extends React.Component {
render(){
return <object
type = "image/svg+xml"
className = "main_map"
data = {kr_map}
></object>
}
}
해당 코드는 동작한다.
CSS가 조금 다르게 동작하지만, 동작 자체는 확인하였다.
img와 class는 다음과 같은 CSS환경에서 다르게 동작한다.
.main_map {
width: 100%;
height: auto;
}
img
태그에서 의도한 그대로 동작하는 모습object
태그에서 조금 이상하게 동작하는 모습다음과 같은 방식으로 가져올 수 있다. 상기 링크를 참조하여 엘리먼트를 조작할 수 있게 코드를 바꾸어본다.
object 안의 무언가를 엘리먼트로 빼올 수 있을지 판단하기 위해 다음과 같은 코드를 작성하였다.
import kr_map from './kr.svg'
import React from 'react'
export default class map extends React.Component {
constructor(props){
super(props);
this.state = {
map_img : <object type = "image/svg+xml" className = "main_map" data = {kr_map}></object>
}
console.log(this.state.map_img)
}
render(){
return this.state.map_img;
}
}
[HMR] Waiting for update signal from WDS...
오류가 발생한다.
오류 해결 방법
오류를 해결하니 다음과 같은 결과를 준다.
단순한 React Element이며, SVG의 엘리먼트는 딱히 뺄 수 있을 것 같아보이지 않는다.
당연한 바이지만, React에서는 render()를 호출하기 전에는 DOM에 추가하지 않기 때문에, 다음과 같은 코드로는 목적을 달성할 수 없다.
constructor(props){
super(props);
this.state = {
map_img : <object type = "image/svg+xml" className = "main_map" data = {kr_map}></object>
}
//render()를 호출한 이후에 DOM에 추가되는데,
//constructor에서 호출하면 아무것도 불러올 수 없다.
console.log(document.getElementById("KOR2494"));
}
상기 기재된 방식은 SVG
이미지가 object
로 추가된 이후에 접근할 수 있는 방식인데, React
특성상 render()
가 호출이 되어야만 해당 컴포넌트
가 DOM
에 추가된다.
그리고 사실 render()
를 호출한 직후에 컴포넌트
가 DOM
에 추가되는 것도 아니다. (여러 컴포넌트
들의 render()
를 모아서 한번에 추가함)
따라서 상기 방법으로 해결될 것으로 보이지는 않는다.
직접 수행해보려고 하였으나, 상기 작업으로 수행하는 것은 굉장히 번거로운것을 떠나 실행 가능성조차 희박하므로, 최적화된 라이브러리를 사용해서 원하는 작업을 수행하려고 한다.
이 모듈은 방금 상기 시도에서 좌절되었던 SVG가 DOM에 추가되기 전 / 추가된 후에 작업을 정해놓고, 수행할 수 있게 한다.
상기 링크의 설명서를 참고하여, 다음과 같이 작성하였다.
import kr_map from './kr.svg'
import React from 'react'
import { ReactSVG } from 'react-svg'
export default class map extends React.Component {
render(){
return <ReactSVG
afterInjection = {(error, svg) => {
if (error) {
console.error(error);
return;
}
console.log(svg);
}
}
src = {kr_map}
></ReactSVG>
}
}
console.log(svg)를 통하여, svg는 완벽하게 svg를 읽은 결과가 된다!
svg의 타입은 SVGSVGElement 라고 한다.
이 svg의 자식 element를 갖고 오려면
NodeListOf< ChildNode >
)HTMLCollection
)다음 링크에서는 어떻게 SVG엘리먼트에 CSS스타일을 적용시킬 수 있는지 기재되어있다.
다음 링크를 통하여 이벤트를 어떻게 넣는지 알 수 있다.
관련하여, 다음과 같이 코드를 작성하였다.
import kr_map from './kr.svg'
import React from 'react'
import { ReactSVG } from 'react-svg'
export default class map extends React.Component {
render(){
return <ReactSVG
afterInjection = {(error, svg) => {
if (error) {
console.error(error);
return;
}
svg.classList.add('region');
}
}
src = {kr_map}
></ReactSVG>
}
}
이 작업으로 인하여, svg
의 class
는 region
이 되었다.
이 classList
에 부여한 class
이름은 CSS
에서 적용된다.
svg파일은 다음과 같이 수정하였다.
<svg baseprofile="tiny" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" version="1.2" xmlns="http://www.w3.org/2000/svg">
<path d="M372.9 ... 이후 생략"/>
주요 변경점
CSS파일은 다음과 같이 수정하였다.
.region {
position:relative;
fill:blueviolet;
transition-duration: .2s;
z-index: 0;
}
.region :hover{
fill:crimson;
transition-duration: .2s;
z-index: 1;
}
주요 변경점
상기의 의도한 CSS설정과 완전히 다르게 동작하는 SVG에 대한 항목이다.
다음 링크를 통해서, SVG파일의 ViewBox와 ViewPort에 대해 알 수 있다.
주요 요점은 다음과 같다.
ViewBox
는 SVG가 보일 영역을 지정할 수 있게 한다.ViewPort
는 ViewBox
와 비슷하지만, Pan
과 Zoom
이 가능하다는 특징이 있다.Pan과 Zoom의 기능은 뒤로 하고, 현재는 SVG의 영역을 지정해주는것에만 집중한다.
ViewBox
를 시도하는 경우, 다음과 같이 수정한다.<svg viewBox = "0 0 1000 1300" 이하생략 >
<path>...</path>
</svg>
결과
지원함.
윈도우 사이즈에 영향을 받음
ViewPort
를 시도하는 경우, 다음과 같이 수정한다.
<svg width = "1000" height = "1300" 이하생략 >
<path>...</path>
</svg>
결과
ViewBox
와 ViewPort
를 혼용하는 것으로 Zoom등이 가능하게 된다고 기재되어있다.import kr_map from './kr.svg'
import React from 'react'
import { ReactSVG } from 'react-svg'
export default class map extends React.Component {
constructor(props){
super(props);
}
render(){
return <ReactSVG
afterInjection = {(error, svg) => {
if (error) {
console.error(error);
return;
}
svg.setAttribute('width','1000');
svg.setAttribute('height','1300');
svg.setAttribute('viewBox','0 0 1000 1300');
svg.setAttribute('preserveAspectRatio',"xMinYMin meet");
svg.classList.add('region');
}
}
src = {kr_map}
></ReactSVG>
}
}
SVG xml코드를 통째로 가져오는 방법도 존재한다고 한다.
그 외에도 여러가지 모듈이 있는데,
이 중에서는 SVG 엘리먼트를 각기 다른 컴포넌트로 취급할 수 있는 패키지도 있다고 한다.