paper.js로 SVG 이미지
떠다니게 만들기


— Vector graphic, SVG, javascript


Soohyun Jeong, May 21, 2020




# 프로젝트 개요


p5.js를 배우고 난 뒤 언젠가 SVG(vector graphic)를 활용한 작업을 진행해 보고 싶었다. p5.js는 가장 유명하고 방대한 라이브러리이지만 픽셀 기반이고, 코드 안에서 그림을 그려야 한다는 단점이 있다. 이번 작업에서는 어도비 일러스트레이터로 그린 그림을 웹에 SVG로 옮겨 움직이게 하고자 했다. 진행하게 된 홈페이지의 주요 기능은 그림 아이콘이 화면 속에 떠다니고 마우스 오버 (hover)에 반응하며 클릭 시 특정 주소로 링크가 연결돼야 한다는 것이다.


# SVG? Vector vs Pixel


디자인 작업을 하면서 작업 파일 관리와 더불어 가장 신경 쓰는 지점은 ‘원본’의 보관이다. 그리고 그 원본이란 언제든 수정이 가능하며 고화질의 결과물을 얼마든지 다시 만들어낼 수 있다는 것을 의미한다. 결코 지나간 작업을 수정하거나 다시 들여다보지 않을지라도 원본 관리는 중요하다. 그래서 나 포함 아마 대부분의 디자이너에겐 벡터로 존재하는 그래픽에 대한 집착이 있을 것이다. 벡터는 수학 방정식을 기반으로 한 이미지이다. 일러스트에서 그림을 SVG로 저장하면 해당 그림이 숫자로 저장되는 것을 확인할 수 있다. 빨간색을 RGB(255, 0, 0) 혹은 #ff0000으로 치환할 수 있듯이, 그림 역시 숫자로 치환할 수 있고 그렇게 데이터로 만들어진 그림이 SVG라고 생각하면 편하다. 벡터 기반 확장자로는 SVG뿐만 아니라 PDF, EPS, AI, DXF 등이 있다.



# SVG 파일 만들기


︎ 일러스트레이터로 아이콘 그리고 내보내기

어도비 일러스트레이터는 대표적인 벡터 기반 프로그램이다. 일러스트레이터 내에서는 아무리 확대해도 이미지가 깨지지 않는 것을 확인할 수 있다.

일러스트레이터에서 원하는 모양으로 아이콘을 그린 후 정리한다. 내가 했던 정리 방식은 불필요한 점을 최대한 없애고 글자처럼 한 그룹으로 묶여야 하는 개체를 컴파운드 패스(단축키 ⌘8)로 만들어 주는 정도이다.

어도비 일러스트레이터 cc에는 [자산 내보내기 / asset export] 기능이 추가되었다. 이 기능을 이용하면 그림을 쉽게 SVG로 내보낼 수 있다.

  1. 윈도우에서 자산 내보내기 창을 킨다.
  2. 내보낼 아이콘을 선택한 후 자산 내보내기 창 안의 새 레이어 버튼을 클릭한다.
  3. 포맷을 SVG로 설정 후 내보내기를 누른다.



너무 쉬워서 설명을 하는 게 조금 민망할 정도다. 한번 만들어놓은 자산은 모양이 변형되어도 계속해서 업데이트되므로 수정할 때마다 다시 레이어를 만들 필요가 없다.

이렇게 만들어진 파일 용량을 줄이고 싶다면 여러 효과적인 방법이 많지만, 굉장히 어렵고 복잡하다(…). 머리 아픈 것은 건너뛰고, 설치 필요 없이 웹에서 간단하게 해결할 수 있는 사이트가 있다. 슬라이드 바를 움직여 최대한 모양을 해치지 않는 선에서 용량을 줄이고 다운받으면 된다.


(용량이 기존 대비 58%로 줄었다. 가운데 코드가 줄이기 전, 오른쪽이 줄이고 난 후의 모습이다.)

SVG 최적화 사이트



︎ SVG 파일 구조 정리하기

이제 SVG 파일을 열어보자. 난해한 문자가 난무하지만 파일 구조 자체는 간단하기 때문에 겁먹을 필요 없다.
code 화면 <svg xmlns="http://www.w3.org/2000/svg">

필수로 들어가야하는 주소. 아이콘이 여러개라도 한 번만 작성해주면 된다. 추후 스크립트에서 아이콘을 불러올 수 있도록 SVG에 id값을 ‘icons’로 할당했다.

<viewBox="0 0 174.37 156.88">

인라인으로 넣을 때 viewBox 크기에 따라 아이콘이 잘리거나 크기가 예상하지 못하게 달라지므로 꼭 숫자를 정확히 적어야한다. 그러나 나는 paper.js를 사용할 예정이어서 viewBox가 필요하지 않았다.

<g data-name="레이어 2">

그룹으로 묶여있다는 표시. 나는 총 여섯개의 아이콘이 필요해서 보기 편하도록 각각의 아이콘을 이름 없이 <g>로 묶어주었다.

<path fill="#fff" d="~~패스 경로~~">

색상은 라이브러리 내에서 조정해야 하므로, 내가 정말 필요한 부분은 <d> 패스 경로 속성이 전부다. 아래는 최종 정리된 사진(길이상 일부만 올린다)


︎ 웹상에서 SVG 구현하기

웹에서 SVG를 사용할 때 기초는 이 사이트 에 가장 잘 정리되어있다.

위 페이지에서는 object로 구현하는 방식을 추천하지만 나는 외부 링크를 연결해야하기 때문에 inline으로 구현했다(참고 사이트).



# SVG 라이브러리


SVG를 다루는 라이브러리는 매우 많다.


여러 라이브러리를 다운받아보고, 최종적으로는 paper.js를 선택했다. 내가 최우선으로 고려한 사항은 p5.js처럼 vector를 만들어 쓸 수 있고 import와 애니메이션이 용이한 것이다. paper.js는 tween의 기능이 기본으로 내장되어있어 애니메이션이 용이하고 onClick, onMouseOver, onMouseDrag 등 이벤트도 다양하다.







# paper.js 의 특징


︎ 튜토리얼이 친절하고 예제가 다양하다.

튜토리얼이 (영어로 되어있다는 점만 빼면) 상세하고 친절해서 초심자가 접근하기에 좋다. 예제 또한 난이도가 다양하다.
(+) p5.js를 Daniel Shiffman에게 배운 사람들이 특히 접근하기 좋을 듯하다. 그의 코드를 참고한 부분이 많이 눈에 띄었다.
(+) two.js도 좋은 라이브러리이지만, 예제가 어려우므로 자바스크립트 숙련 사용자가 사용하기 적합하다.


︎ html <canvas>를 사용한다

이 부분은 단점이 될 수도 있을 것 같다. 캔버스는 어찌 됐건 픽셀화된다는 뜻이다. 그러나 paper.js는 vector 그래픽에 기반하고 있다. 쉽게 말하자면 화면을 500% 확대하면 깨지지만, 그 상태에서 창을 새로고침하면 500%의 크기를 유지한 채 선명한 이미지를 볼 수 있다. 그림을 비트맵이 아닌 벡터 즉 수학 방정식 기반으로 그리기 때문에 새로고침 했을 때 비트맵 이미지를 확대하는 게 아니라 수학적으로 500% 크게 계산된 원을 그리기 때문이다. 따라서 새로고침 없이 윈도우 창을 확대해도 깨지지 않아야 한다면 two.js 라이브러리를 쓰는 편이 좋다. 그러나 이와 같은 상황이 많지는 않을 것이라 예상한다. 일반적인 상황(화면 크기 100%)에서는 그림이 깨질 염려는 전혀 하지 않아도 된다. 홈페이지 튜토리얼에서 기본적으로 Hi-DPI(레티나) 화면의 고해상도로 캔버스를 렌더링한다고 설명한다.

(왼) 윈도우 500% 확대 (오) 500% 확대를 유지하고 새로고침

이 블로그에 의하면 canvas에 그린 그림엔 이벤트 핸들러를 적용할 수 없다고 되어있지만 paper.js에서 이벤트 핸들러를 제공한다. (onFrame, onMouseDown, onMouseDrag, onMouseUp, onClick, onDoubleClick, onMouseMove, onMouseEnter, onMouseLeave)


︎ SVG import/export

SVG 가져오기/내보내기가 타 라이브러리에 비해 쉽고 오류가 없다. 위에서도 언급했지만 어도비 일러스트레이터에서 그린 그림을 가져와야 했는데, two.js에서 가져오기 했을 때는 그림이 복잡할 때 깨지는 경우가 종종 발생했다. paper.js는 About에서도 어도비 일러스트레이터의 스크립팅 환경인 Scriptographer를 사용한다고 명시되어있는 만큼, 호환이 잘 된다.


︎ 기능들 : bounds, getNearestPoint, blend, tween, onFrame

1. bounds

별거 아닐지 몰라도 내가 느꼈을 때 paper.js가 편한 가장 큰 이유 중 하나가 bounds 기능이다. 이름에서 알 수 있듯 일종의 바운딩 박스, 개체의 경계를 말한다. 콘솔에 console.log(item.bounds)를 찍어보면 아래와 같이 여러 종류의 수치가 나오는 것을 볼 수 있다.


따라서 나는 이번 프로젝트에서 가져온 SVG의 경계를 정확하게 알 수 있었고 특별한 계산 없이도 화면 안(window.innerwidth)에서 튕기는 움직임을 만들 수 있었다.

(+) two.js에도 getBoundingClientRect 라는 이름으로 같은 기능이 있지만 paper.js만큼 정확하지 않다. paper.js에서는 물체가 움직일 때마다 바운드의 수치들을 다시 잰다. 예를 들어 10*50 직사각형이 서 있을 때는 item.bound.width가 10이지만 누워있게 되면 item.bound.width가 50으로 재정의되는 식이다.

2. getNearestPoint

Path에서 특정 포인트와 가장 가까운 점을 찾는 기능이다. 양옆과 위쪽은 화면 사이즈만 알면 튕겨 나갈 수 있지만 아래쪽에는 계속 움직이는 물결을 배치했기 때문에 매번 달라지는 높이를 알아야 했다. 각 아이콘에서 움직이는 path의 가장 가까운 점을 찾고, 아이콘의 바운딩 박스가 움직이는 path의 가장 가까운 점보다 낮아지면 아이템이 튕겨 나가도록 했다.

3. blend

포토샵의 블렌드 기능이 있다. 여러 아이콘이 겹쳤을 때 답답하지 않은 인상을 주기에 좋았다. 단 익스플로러에서는 블렌드 기능이 일체 불가능하다.

4. tween

애니메이션 기능. (원래는 tween.js 라이브러리가 따로 있다. two.js를 사용한다면 tween.js 라이브러리를 추가로 설치해야 한다.) 아주 쉽게 색상을 바꾸거나 위치를 바꿀 수 있다. 아래는 tween을 이용해 색상을 바꾸는 코드 예제다. 제이쿼리의 animate()와 같은 형식이란 걸 알 수 있다.

item.tween({fillColor: red}, {duration: 2000, easing: 'easeInOutQuad'})

5. onFrame

프레임마다 계산하는 애니메이션 루프이다. 나는 뷰 전체를 계속 로드해야 했기 때문에 view.onFrame(); 함수를 사용했다. 이 함수 안에 콘솔을 찍으면 프레임마다 반복해서 콘솔이 찍힌다. p5.js의 draw() 함수와 같다.


# 완성


이번 홈페이지 프로젝트에서는 최대한 SVG에 집중하고자 빌드가 필요 없는 카고(cargo.site)로 홈페이지를 만들었다. SVG 이미지는 카고의 특정 페이지에서 CODE VIEW를 누르고 붙여넣기 하면 간단하게 inline으로 구현된다. paper.js를 이용한 스크립트는 파일 업로드 기능을 이용해서 올렸다. 아래는 깃랩에 구현한 예제와 실제 홈페이지에 적용한 링크이다.