개인노트-인강

개인 노트 정리) Canvas - 1-03. 파티클 그리기

roroism 2023. 5. 21. 19:32

인터랙티브 웹 개발 Canvas 인강 정리

 

# 파티클 그리기

class 함수를 통해서 파티클에 x와 y 좌표, 사이즈 값 등을 어떻게 관리하고, 그리는 방식에 대해 알아보겠습니다.

 

## 원 그리기

- 원을 그릴때는 arc() 메소드를 사용합니다.

- arc() 란 중심을 기준으로 반지름만큼 떨어져 원하는 각도만큼 호를 그리는 메서드입니다.

 

### 원 그리기 코딩 순서

1. 먼저 원 그리기에 앞서 시작할 때, beginPath()로 '패스를 그리기 시작할께' 라고 먼저 알려주어야 합니다.

2. 이후 arc 메소드를 사용합니다.

3. closePath()로 마칩니다.

 

### arc 메소드의 매개변수

- 시작하는 x위치, 시작하는 y위치, 반지름의 길이, 시작하는 각도, 끝나는 각도, 시계방향 또는 반시계 방향(기본값 시계방향인 false, 생략가능)

- 각도는 degree가 아니라 radian 입니다. (PI를 180으로 나눈것이 1도가 됩니다.)

 

const canvas = document.querySelector("canvas");

const ctx = canvas.getContext("2d");
const dpr = window.devicePixelRatio; // 1. devicePixelRatio 값을 구한 뒤,

const canvasWidth = 300;
const canvasHeight = 300;

canvas.style.width = canvasWidth + "px";
canvas.style.height = canvasHeight + "px";

// 2. dpr 값을 canvas의 width와 height에 곱해줍니다.
canvas.width = canvasWidth * dpr;
canvas.height = canvasHeight * dpr;

ctx.scale(dpr, dpr); // 3. 그리려는 객체에도 가로와 세로에 dpr값을 각각 곱해줍니다.

// ctx.fillRect(10, 10, 50, 50);

ctx.beginPath();
ctx.arc(100, 100, 50, 0, (Math.PI / 180) * 360);
ctx.fill(); // 안에 색상을 채워줍니다.
ctx.closePath(); // 100, 100 위치에 반지름이 50인 원이 그려지게 됩니다.

- 참고 : 나중에 선(line)을 그릴때도 마찬가지지만 그리기 시작하는 곳에 beginPath를 하고, 끝날 때 closePath를 해주어 라인을 그릴 수 있습니다.

 

### fill() 대신 stroke() 사용해보기

아래처럼 fill()대신에 stroke() 메소드를 사용하게 되면, 원모양의 선이 그려지게 됩니다.

// ...
ctx.beginPath();
ctx.arc(100, 100, 50, 0, (Math.PI / 180) * 360);
// ctx.fill(); // 안에 색상을 채워줍니다.
ctx.stroke();
ctx.closePath(); // 100, 100 위치에 반지름이 50인 원이 그려지게 됩니다.

 

### 끝나는 각도 바꿔보기

360도이면 원모양이 나옵니다. 90도로 바꾸게 된다면..

// ...
ctx.beginPath();
ctx.arc(100, 100, 50, 0, (Math.PI / 180) * 90);
// ctx.fill(); // 안에 색상을 채워줍니다.
ctx.stroke();
ctx.closePath(); // 100, 100 위치에 반지름이 50인 원이 그려지게 됩니다.

시계방향으로 90도만큼 호가 그려지게됩니다.

 

### fillStyle()로 fill() 스타일 바꾸기

fill() 메소드를 사용하기전에 fillStyle()로 스타일을 바꾸어 채우기 색상을 바꾸어 줄 수도 있습니다.

// ...
ctx.beginPath();
ctx.arc(100, 100, 50, 0, (Math.PI / 180) * 180);
ctx.fillStyle = "red";
ctx.fill(); // 안에 색상을 채워줍니다.
// ctx.stroke(); // 선을 그립니다.
ctx.closePath(); // 100, 100 위치에 반지름이 50인 원이 그려지게 됩니다.

 

# Class로 파티클 관리하기

- 그런데 이렇게 단순히 캔버스에 그리기만 한다면 우리가 생각하는 애니메이션을 만들 수 없습니다.

- frame들을 한개씩 연속으로 재생하여 마치 움직이는 것처럼 보이게 만드는 애니메이션의 원리가 canvas 애니메이션의 핵심입니다.

- 파티클이 생성되는 위치를 바꿔가며 그린다면 마치 움직이는 파티클처럼 보일 것입니다.

- 우리가 만들려는 파티클을 하나만 관리하지 않고 여러개를 동시에 관리하기 위해서 가장 좋은 방법 중 하나가 javascript의 class를 만들어서 이 class의 인스턴스들로 파티클을 만드는 방법입니다.

 

class Particle {
  constructor(x, y, radius) {
    this.x = x;
    this.y = y;
    this.radius = radius;
  }
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, (Math.PI / 180) * 360);
    ctx.fillStyle = "red";
    ctx.fill();
    ctx.closePath();
  }
}

const x = 100;
const y = 100;
const radius = 50;
const particle = new Particle(x, y, radius);

particle.draw();

 

## 애니메이션 효과를 주기

애니메이션 효과를 주기 위해서 x와 y좌표를 한 프레임 한 프레임마다 옆으로 옮겨 보겠습니다.

먼저, 애니메이트 함수를 정의합니다.

// ...
function animate() {
  window.requestAnimationFrame(animate);
  particle.draw(); // draw 함수를 animate() 안으로 옮겨 옵니다.
}
// animate 함수가 실행이 되고, 다시 animate 함수를 요청해서 또 실행이 되면서 매 frame마다 계속
// 무한으로 실행이 되는 함수가 만들어집니다.
animate();

이제 위 코드 안에 파티클 값 변화를 통해서 애니메이션 효과를 줄 수 있습니다.

하지만, 지금은 파티클을 그리고, 지우고, 새로 그려서 애니메이션 효과를 주는 것이 아니라, 현재 계속 그린 위치에 덮어 씌우는 형태로 그려지고 있습니다.

 

### clearRect()

- 그럼, 이전 프레임을 지우고 새 프레임에서 새로 그리게 하기 위해서는 제일 상단에 clearRect 메소드를 사용하면 됩니다.

- fillRect() 를 사용했을 때와 마찬가지로 시작하는 x위치, 시작하는 y위치, 가로길이, 세로길이 를 넣어주면 됩니다.

- clearRect() 가로길이, 세로길이에 canvas의 가로길이와 canvas의 세로길이를 넣어주게되면 겉으로는 표가 안나지만, 매프레임마다 지우고 파티클을 새로 그리게 됩니다.

 

## Class 최종 코드

class Particle {
  constructor(x, y, radius) {
    this.x = x;
    this.y = y;
    this.radius = radius;
  }
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, (Math.PI / 180) * 360);
    ctx.fillStyle = "red";
    ctx.fill();
    ctx.closePath();
  }
}

const x = 100;
const y = 100;
const radius = 50;
const particle = new Particle(x, y, radius);

function animate() {
  window.requestAnimationFrame(animate);

  ctx.clearRect(0, 0, canvasWidth, canvasHeight);

  particle.draw();
}
// animate 함수가 실행이 되고, 다시 animate 함수를 요청해서 또 실행이 되면서 매 frame마다 계속
// 무한으로 실행이 되는 함수가 만들어집니다.

animate();