개인노트-인강

개인 노트 정리) Canvas - 1-08. 캔버스 resize 다루기

roroism 2023. 6. 1. 19:55

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

 

# 캔버스 resize 다루기

- 캔버스 사이즈를 첫 로딩할때 웹 브라우저 크기만큼 적용된 뒤로는 바뀌지 않기 때문에 웹 브라우저 크기를 조절해도 캔버스 크기는 달라지지 않고 고정되어 있습니다.

- 그래서 window가 리사이즈 될 때마다 다시 캔버스 width 변수 값과 캔버스 height 변수 값을 할당해주면 해결할 수 있습니다.

 

## 코드 수정 하기

1. 캔버스 사이즈를 정의하는 코드 부분을 init 함수로 묶어줍니다.

// ...

let canvasWidth;
let canvasHeight;

function init() {
  canvasWidth = innerWidth;
  canvasHeight = innerHeight;

  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값을 각각 곱해줍니다.
}

// ...

 

2. 모든 파티클의 위치값들을 정의하는 부분도 init 함수 안으로 넣어줍니다.

// ...

let canvasWidth;
let canvasHeight;
let particles;

function init() {
  canvasWidth = innerWidth;
  canvasHeight = innerHeight;

  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값을 각각 곱해줍니다.

  particles = [];

  for (let i = 0; i < TOTAL; i++) {
    const x = randomNumBetween(0, canvasWidth);
    const y = randomNumBetween(0, canvasHeight);
    const radius = randomNumBetween(50, 100);
    const vy = randomNumBetween(1, 5);
    const particle = new Particle(x, y, radius, vy);
    particles.push(particle);
  }
}

// ...

 

3. window에 event를 추가해줍니다.

// ...

window.addEventListener("load", () => {
  init();
  animate();
});

window.addEventListener("resize", () => {
  init();
});

 

## 화면 사이즈에 따라 파티클 갯수 변경하기

파티클의 갯수를 정의하는 코드를 init안으로 옮기고 canvasWidth 값을 이용하여 파티클의 갯수를 정해줍니다.

이제 화면 사이즈가 바뀌어도 파티클의 밀도값은 항상 같습니다.

let canvasWidth;
let canvasHeight;
let particles;

function init() {
  canvasWidth = innerWidth;
  canvasHeight = innerHeight;

  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값을 각각 곱해줍니다.

  particles = [];
  const TOTAL = canvasWidth / 60;
  console.log("TOTAL : ", TOTAL);
  for (let i = 0; i < TOTAL; i++) {
    const x = randomNumBetween(0, canvasWidth);
    const y = randomNumBetween(0, canvasHeight);
    const radius = randomNumBetween(50, 100);
    const vy = randomNumBetween(1, 5);
    const particle = new Particle(x, y, radius, vy);
    particles.push(particle);
  }
}

 

## 배운 코드 전체

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

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

let canvasWidth;
let canvasHeight;
let particles;

function init() {
  canvasWidth = innerWidth;
  canvasHeight = innerHeight;

  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값을 각각 곱해줍니다.

  particles = [];
  const TOTAL = canvasWidth / 60;
  console.log("TOTAL : ", TOTAL);
  for (let i = 0; i < TOTAL; i++) {
    const x = randomNumBetween(0, canvasWidth);
    const y = randomNumBetween(0, canvasHeight);
    const radius = randomNumBetween(50, 100);
    const vy = randomNumBetween(1, 5);
    const particle = new Particle(x, y, radius, vy);
    particles.push(particle);
  }
}

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

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

const feGaussianBlur = document.querySelector("feGaussianBlur");
const feColorMatrix = document.querySelector("feColorMatrix");

// 1. datGUI 컨트롤 패널안에서 변화를 주기 위한 값들을 먼저 정의를 해주어야합니다.
// 함수의 인스턴스 값에 담아서 만들어주도록 합니다.
const controls = new (function () {
  // dat GUI 메소드에서 사용되어야할 값들을 여기서 this로 가져오기 때문에 변수명 앞에 this를 붙입니다.
  this.blurValue = 40;
  this.alphaChannel = 100;
  this.alphaOffset = -23;
  this.acc = 1.03;
})();

// 2. datGUI 가져오기
let gui = new dat.GUI();

const f1 = gui.addFolder("gooey Effect");
f1.open();
// 첫번째 인자 : controls
// 두번째 인자 : controls에서 사용한 변수 이름의 string 형태
// 세번째 인자 : 최소값 범위
// 네번째 인자 : 최대값 범위
// onChange : 값이 바뀌었을 때 어떻게 동작시킬지 정의
// value : 바뀐 값을 return 해줍니다.
f1.add(controls, "blurValue", 0, 100).onChange((value) => {
  feGaussianBlur.setAttribute("stdDeviation", value);
});
f1.add(controls, "alphaChannel", 1, 200).onChange((value) => {
  feColorMatrix.setAttribute(
    "values",
    `1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 ${value} ${controls.alphaOffset}`
  );
});
f1.add(controls, "alphaOffset", -40, 40).onChange((value) => {
  feColorMatrix.setAttribute(
    "values",
    `1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 ${controls.alphaChannel} ${value}`
  );
});
const f2 = gui.addFolder("Particle Property");
// 5번째 인자는 소수점 두번째 자리까지 출력하기 때문에 0.01로 세팅
f2.add(controls, "acc", 1, 1.5, 0.01).onChange((value) => {
  // 현재 생성되어 있는 파티클들 모두 값을 세팅해주어야 하기 때문에 particle들을 foreach로 돌아야 합니다.
  particles.forEach((particle) => (particle.acc = value));
});

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

const randomNumBetween = (min, max) => {
  return Math.random() * (max - min + 1) + min;
};

console.log(particles);

// 1초에 60frame으로 동작
let interval = 1000 / 60;
let now, delta;
let then = Date.now();

function animate() {
  window.requestAnimationFrame(animate);
  now = Date.now();
  delta = now - then;

  if (delta < interval) return;

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

  particles.forEach((particle) => {
    particle.update();
    particle.draw();

    if (particle.y - particle.radius > canvasHeight) {
      particle.y = -particle.radius;
      particle.x = randomNumBetween(0, canvasWidth);
      particle.radius = randomNumBetween(50, 100);
      particle.vy = randomNumBetween(1, 5);
    }
  });

  then = now - (delta % interval);
}

window.addEventListener("load", () => {
  init();
  animate();
});

window.addEventListener("resize", () => {
  init();
});