포트폴리오를 기획하면서 우주로 테마를 잡게 되었다. 따라서 우주의 무중력 상태를 표현하고자 기획하게 되어 matter js를 이용하여 기능을 구현하게 되었다.
지금까지 canvas 공부하며 구현하였던 fps,requestAnimationFrame(frame) 재귀함수,gravity 등등의 직접 구현하기 어려운 물리 알고리즘을 쉽게 사용가능하게끔 만든 Javascript용 2D 물리 엔진이다.
matter js에는 engine,render,runner,body,bodies,composite 등의 api가 있다.
물리 시뮬레이션을 담당하는 api로, Body 들의 list들을 관리 한다.
Body 들의 위치, 속도, 충돌 등을 계산 할수 있으며 gravity, enableSleeping, positionIterations 등등의 기능을 수행할수 있다.
말그대로 Body 들의 시각적인 부분을 담당 한다. Canvas의 width, height를 받아오거나 color, pixelRatio, showVelocity, showBlaBla등의 기능을 수행할수 있다.
Engine, Render의 업데이트 loop 관리 한다. beforeTick, tick, afterTick, fps, requestAnimationFrame(frame) 등의 기능을 수행할수 있다.
단일 물리적 객체 ( 시각적 x ) 이다. 한 객체의 position, velocity, force, mass 등의 기능을 수행 할수 있다.
물리값만 가진 Body를 시각적으로 쉽게 표현위한 모듈 이다. 원, 사각형, 다각형, SVG Paths 등을 쉽게 제작 할수 있다.
world에 body를 추가해주는 역할을 한다. Body들을 하나로 묶는 그룹의 역할도 가능 하다고 한다.
Canvas 내에서의 포지션값을 위하여 사용하는 api다. 말그대로 마우스 이벤트시 값을 받아오기 때문에 캔버스의 Body들과 상호작용은 할 수 없는 특징이 있다.
mouse api와 다르게 각 Body들과 상호작용을 할 수있게 해준다.(들기 등)
Mouse를 이 MouseConstraint와 연결하여 사용한다.
설치
npm i matter-js
const canvasRef = useRef(null);
<canvas style={{ cursor: "pointer" }} ref={canvasRef}></canvas>
useEffect(() => {
const canvas = canvasRef.current;
initScene();
function initScene() {
engine = Engine.create();
render = Render.create({
canvas: canvas,
engine: engine,
options: {
width: cw,
height: ch,
wireframes: false,
},
});
runner = Runner.create();
Render.run(render);
Runner.run(runner, engine);
}
},[])
function initGround() {
const segments = 32;
const deg = (Math.PI * 2) / segments;
const width = 50;
const radius = cw / 2 + width / 2;
const height = radius * Math.tan(deg / 2) * 2;
for (let i = 0; i < segments; i++) {
const theta = deg * i;
const x = radius * Math.cos(theta) + cw / 2;
const y = radius * Math.sin(theta) + ch / 2;
addRect(x, y, width, height, { isStatic: true, angle: theta });
}
}
function initImageBoxes() {
const scale = 0.7;
const t1 = { w: 250 * scale, h: 250 * scale };
addRect(cw / 2, ch / 2, t1.w, t1.h, {
label: "JS",
chamfer: { radius: 20 },
render: { sprite: { texture: IconJS, xScale: scale, yScale: scale } },
});
addRect(cw / 2, ch / 2 + t1.h, t1.w, t1.h, {
label: "StyledComponent",
chamfer: { radius: 20 },
render: {
sprite: { texture: IconStyled, xScale: scale, yScale: scale },
},
});
addRect(cw / 2 - t1.w, ch / 2 + t1.h, t1.w, t1.h, {
label: "REACT",
chamfer: { radius: 75 },
render: {
sprite: { texture: IconREACT, xScale: scale, yScale: scale },
},
});
addRect(cw / 2, ch / 2 - t1.h, t1.w, t1.h, {
label: "TypeScript",
chamfer: { radius: 20 },
render: {
sprite: { texture: IconTypeScript, xScale: scale, yScale: scale },
},
});
addRect(cw / 2, ch / 2 - t1.h * 2, t1.w, t1.h, {
label: "ReduxToolkit",
chamfer: { radius: 20 },
render: {
sprite: { texture: IconRedux, xScale: scale, yScale: scale },
},
});
}
}
const garvityPower = 0.5;
Events.on(runner, "tick", () => {
gravityDeg += 1;
engine.world.gravity.x =
garvityPower * Math.cos((Math.PI / 180) * gravityDeg);
engine.world.gravity.y =
garvityPower * Math.sin((Math.PI / 180) * gravityDeg);
});
참고 문헌
21개 프로젝트로 완성하는 인터랙티브 웹 개발 with Three.js & Canvas