안녕하세요. 볼드나인 프론트엔드팀 에이든입니다. 저희 이지스토리지에서는 물류 센터를 시각화하여 로케이션이 한눈에 들어 오고 다양한 인사이트를 얻을 수 있는 ‘센터 맵’ 기능을 제공하고 있습니다.(아직 실험 단계입니다.)
센터 맵은 2D와 3D로 제공되는데, 2D의 경우, 오늘 알아볼 PixiJS를 사용하고 있습니다. PixiJS는 웹 어플리케이션에서 렌더링 가속화(하드웨어 가속)을 사용하여 강력하고 빠른 2D 렌더링 엔진을 제공합니다. 따라서 2D 렌더링 엔진을 찾고 계시다면 좋은 선택 옵션이 될 수 있습니다.
이 글에서는 PixiJS의 기본 개념, 주요 기능, 그리고 간단한 프로젝트를 만들어보도록 하겠습니다. 렛츠 고!
PixiJS란?
Pixi.js는 오픈 소스 2D 렌더링 엔진으로, WebGL을 활용하여 그래픽을 그립니다. WebGL을 지원하지 않는 환경에서는 HTML5 캔버스로 대체됩니다. 이 엔진을 사용하면 개발자가 웹에서 고성능이면서 시각적으로 풍부한 경험을 적은 노력으로 만들 수 있습니다. Pixi.js는 게임 및 대화형 애플리케이션에 특히 적합하며, 부드러운 애니메이션과 복잡한 그래픽 기능을 지원합니다.
시작하기
전제 조건
그리고 pixi.js의 버전은 8.5.2를 사용하겠습니다.
프로젝트 만들기
vite는 모던 프로젝트 환경을 쉽게 셋업할 수 있도록 create-vite CLI를 제공합니다. 다양한 프레임워크와 함께 Typescript도 지원하니 원하는 템플릿을 선택하여 프로젝트를 만들어 주세요. 저는 Vanilla Javascript로 진행하겠습니다.
pnpm create vite my-vue-app --template vue
Bash
복사
pixi.js 설치
사용하시는 패키지 매니저로 pixi.js를 설치해 주세요.
pnpm add pixi.js
Bash
복사
Application Setup
Application은 렌더링, 인터렉션, 캔버스 안에 들어가는 PixiJS 그래픽 오브젝트 관리 등을 담당하는 PixiJS 루트 객체라고 생각하시면 됩니다. 코드는 Canvas API를 사용해 보신 분들이라면 크게 어렵지 않으실 거예요.
// main.js
import { Application } from 'pixi.js'
const app = new Application();
const render = async () => {
await app.init({ width: 800, height: 600, backgroundColor: 0x1099bb });
document.body.appendChild(app.canvas);
app.stage.addChild(Sprite.from('something.png'));
}
render();
JavaScript
복사
// index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<script type="module" src="/main.js"></script>
</body>
</html>
JavaScript
복사
Application 인스턴스를 만들고 init메서드를 옵션과 함께 호출하여 canvas를 만들어 줍니다. init 메서드는 프로미스를 반환하기 때문에 async 함수에서 사용합니다.
코드가 실행되면 800x600 사이즈의 캔버스가 만들어진 것을 볼 수 있습니다.
자, 이제 준비는 되었으니, PixiJS 어플리케이션을 구성하는 각 오브젝트들에 대해서 알아보죠.
Sprite
PixiJS는 이미지를 그릴 수 있는 몇 가지 방법이 있는데, 그중 가장 간단한 방법이 Sprite를 사용하는 것입니다.
// main.js
import { Application, Sprite, Assets } from 'pixi.js'
const app = new Application();
const render = async () => {
await app.init({ width: 800, height: 600, backgroundColor: 0x1099bb });
document.body.appendChild(app.canvas);
const texture = await Assets.load('https://pixijs.com/assets/bunny.png');
const bunny = new Sprite(texture);
app.stage.addChild(bunny);
}
render();
JavaScript
복사
const texture = await Assets.load('https://pixijs.com/assets/bunny.png');
JavaScript
복사
Asset은 글로벌 클래스로 싱글톤입니다. 따라서 따로 인스턴스를 생성할 필요 없이 사용 가능합니다.
load메서드는 하나 또는 여러 개의 리소스를 로드하는 메서드입니다. 웹 브라우저에서 렌더링 시 필요한 이미지를 로드하듯 Application에서 필요한 리소스를 비동기로 로드합니다. 여기서는 PixiJS에서 기본으로 제공하는 귀여운 토끼 캐릭터를 로드하겠습니다.
const bunny = new Sprite(texture);
JavaScript
복사
준비된 토끼 캐릭터 리소스를 Sprite에 담아 그릴 준비를 합니다.
app.stage.addChild(bunny);
JavaScript
복사
sprite 오브젝트를 stage에 넣어 주세요. canvas가 HTML element라고 하면 stage는 canvas에 그려질 루트 컨테이너입니다.
이제 결과를 확인해 보죠. 귀여운 토끼 한 마리가 나타났네요!
아 그런데 토끼가 좌상단에 딱 붙어 있네요. stage 가운데로 이동시켜 볼까요?
bunny.anchor.set(0.5);
bunny.x = app.screen.width / 2;
bunny.y = app.screen.height / 2;
JavaScript
복사
위 코드를 추가해 주세요. anchor는 sprite의 좌표에 대한 기준이에요. 우리는 토끼를 스크린 가운데 위치시킬 거예요. anchor의 기본값은 0이에요. 그럼 토끼의 머리 왼쪽이 스크린 가운데 위치하게 돼요. 따라서 anchor를 0.5로 설정해 줍니다.
anchor 0
anchor 1
물론 x, y를 따로 설정하는 것도 가능합니다.
bunny.anchor.set(0.5, 0.5); // x, y
JavaScript
복사
이렇게 토끼를 가운데로 잘 이동시켰네요.
Graphics
Graphics는 선, 원, 사각형, 삼각형, 커브, 호, 패스 등 원시적인 도형을 그리기 위한 주요한 오브젝트입니다.
사각형 그리기
const graphics = new Graphics(); // graphics 객체 만들기
graphics.rect(50, 50, 100, 100);
graphics.fill(0xde3249);
app.stage.addChild(graphics);
JavaScript
복사
grahics 객체를 만들고 rect 메서드를 사용해서 사각형을 그려줍니다. 첫 번째 파라미터부터 순서대로 사각형의 x, y, width, height값을 전달합니다. 그런 다음 fill메서드를 사용해서 사각형에 색을 칠해 줍니다.
원 그리기
graphics.circle(80, 80, 50); // x, y, 반지름 길이
graphics.fill(0x650a5a);
JavaScript
복사
circle 메서드를 사용하여 원을 그렸습니다. 여기서 한 가지 주의할 점은 rect 메서드는 사각형을 그릴 때 그리는 시작점이 왼쪽 상단입니다. 하지만 circle 메서드는 좌표를 중심점으로 하여 원을 그립니다.
삼각형 그리기
사실 사각형, 원을 그릴 때와는 다르게 삼각형을 그리는 메서드는 따로 제공되지 않습니다. 삼각형을 그리기 위해서는 좌표를 이동시켜 선을 그려 삼각형을 만들어야 합니다.
graphics.moveTo(50, 50);
graphics.lineTo(200, 200);
graphics.lineTo(50, 200);
graphics.lineTo(50, 50);
graphics.fill(0xaa4f08);
graphics.stroke({ width: 2, color: 0xff00ff });
JavaScript
복사
moveTo메서드는 점을 인자로 전달받은 좌표 값으로 이동 시킵니다. lineTo 메서드는 이동된 점을 시작점으로 하여 전달 받은 좌표 값까지 선을 그립니다. 위 코드와 값이 세 번 lineTo를 호출하여 삼각형을 그립니다.
fill메서드로 삼각형의 색을 채워 주고 storke메서드를 사용해서 삼각형의 border를 그려줍니다.
Interaction
PixiJS는 앞서 배운 Sprite, Graphics와 같은 오브젝트에 HTML element처럼 click, mouseover와 같은 인터렉션 이벤트를 제공합니다. 토끼와 함께 바로 예제로 들어가 봅시다!
Click
const texture = await Assets.load('https://pixijs.com/assets/bunny.png');
texture.baseTexture.scaleMode = SCALE_MODES.NEAREST;
const bunny = Sprite.from(texture);
bunny.anchor.set(0.5);
bunny.x = app.screen.width / 2;
bunny.y = app.screen.height / 2;
bunny.eventMode = 'static';
bunny.cursor = 'pointer';
bunny.on('pointerdown', onClick);
app.stage.addChild(bunny);
function onClick() {
bunny.scale.x *= 1.25;
bunny.scale.y *= 1.25;
}
JavaScript
복사
eventMode 는 모든 컨테이너에 존재하는 프로퍼티입니다. eventMode는 다음 중 하나를 값으로 설정합니다.
•
‘none’: 자신의 자식 객체 포함한 모든 이벤트 무시
•
‘passive’: 해당 객체의 이벤트는 발생하지 않고 무시하지만, 자식 객체가 interactive하다면, 자식들의 이벤트는 emit됩니다.
•
‘auto’: 해당 객체의 이벤트는 발생되지 않지만, 부모 객체가 interactive 하다면, 해당 객체가 히트되었는지는 알 수 있습니다.
•
‘static’: 이벤트도 발생되고 히트 여부도 감지됩니다.
•
‘dynamic’: 이벤트도 발생시키고 히트여부도 알 수 있습니다. static과 차이점은 이벤트가 발생하지 않을 때도 모킹 이벤트를 만들어 발생시킨다는 점 입니다. 예를 들어, mouseover 이벤트가 등록되었다고 가정했을 때 마우스가 객체에 mouseover된 상태에서 가만히 있어도 mouseover 이벤트가 발생합니다.
Drag & Drop
이번에는 아주 간단하게 토끼를 드래그 앤 드롭하는 기능을 만들어 볼게요.
let isDragging = false;
bunny.on('pointerdown', onDown);
bunny.on('pointerup', onUp);
bunny.on('pointermove', onMove);
function onDown() {
isDragging = true;
}
function onUp() {
isDragging = false;
}
function onMove(event) {
if (!isDragging) return;
bunny.x = event.global.x;
bunny.y = event.global.y;
}
JavaScript
복사
pointerdown, pointerup, pointermove 이벤트를 활용해서 드래그 앤 드롭 기능을 만들었습니다.
pointer* 이벤트는 마우스 이벤트와 터치 이벤트 모두 반응합니다. 마우스 이벤트만 처리하고 싶다면 mouse* 이벤트를, 터치 이벤트만 처리하고 싶다면 touch* 이벤트를 활용해 주세요.
Animation
일반적으로 애니메이션 구현은 자바스크립트에서 제공하는 requestAnimationFrame 메서드를 사용할 수 있지만 start, stop, add 와 같은 메서드가 제공되어 렌더링 루프를 추가, 삭제하기 더 쉽습니다. 그리고 속도 제한 기능 등 성능상 안 좋은 영향을 줄 수 있는 부분들을 자동으로 관리해 줍니다. 따라서 PixiJS로 어플리케이션을 만든다면, Ticker를 사용하여 애니메이션을 구현하는 것이 좋은 선택이 될 수 있습니다.
토끼 회전 시키기
// Ticker 사용
const rotate = (time) => {
bunny.rotation += 0.1 * time.deltaTime;
};
app.ticker.add(rotate); // Ticker 사용
JavaScript
복사
// requestAnimationFrame 사용
let deltaTime = 0;
let elapsedTime = 0;
function animate(time) {
if (!deltaTime) {
deltaTime = time;
} else {
elapsedTime = time - deltaTime;
deltaTime = time;
}
bunny.rotation += 0.006 * elapsedTime;
requestAnimationFrame(animate);
}
animate(performance.now());
TypeScript
복사
결론
PixiJS는 렌더링 가속화를 이용해서 복잡한 그래픽을 빠르게 렌더링합니다. 따라서 WebGL 이용하여 복잡한 그래픽을 그려야 하는 프로젝트를 진행하신다면, 아주 좋은 선택지가 될 것이라고 생각됩니다.
오늘 알아 본 Graphic, Sprite, Interaction이외 렌더링할 수 있는 거의 대부분의 요소들을 지원하여, 제공되는 기능들이 엄청 방대하게 많습니다. 따라서 문서를 한 번에 읽기보다는 만들어 보며, 필요한 부분을 찾아 보는 것을 추천 드립니다.
다음 글에서는 좀 더 심층적인 예제와 함께 여러 가지 깨알 팁들을 공유하도록 하겠습니다.
긴 글 읽어 주셔서 감사합니다.