Frontend/Canvas

[Canvas] svg 필터 입히기(feGaussianBlur, feColorMatrix) | Gooey Effect

gamzaggang7 2024. 5. 21. 01:28
728x90

2024.05.20 - [Canvas] - [Canvas] 파티클 그리기 | 애니메이션 추가(requestAnimationFrame)

 

[Canvas] 파티클 그리기 | 애니메이션 추가(requestAnimationFrame)

* 현재 js 코드 *const ctx = canvas.getContext('2d')const dpr = widdow.devicePixelRatioconst canvasWidth = 300const canvasHeight = 300canvas.style.width = canvasWidth + 'px'canvas.style.height = canvasHeight + 'px'canvas.width = canvasWidth * dprcanvas.

gamzaggang7.tistory.com

 

* 현재 js 코드 *

const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const dpr = window.devicePixelRatio

const canvasWidth = innerWidth
const canvasHeight = innerHeight

canvas.style.width = canvasWidth + 'px'
canvas.style.height = canvasHeight + 'px'

canvas.width = canvasWidth * dpr
canvas.height = canvasHeight * dpr
ctx.scale(dpr, dpr)

class Particle {
  constructor(x, y, r, vy) {
    this.x = x
    this.y = y
    this.r = r
    this.vy = vy
    this.acc = 1.05
  }
  update() {
    this.vy *= this.acc
    this.y += this.vy
  }
  draw() {
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.r, 0, Math.PI / 180 * 360)
    ctx.fill()
    ctx.closePath()
  }
}

const Total = 15;
const randomNumBetween = (min, max) => {
  return Math.random() * (max - min + 1) + min
}
let particles = []

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

console.log(particles);

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.r > canvasHeight) {
      particle.y = -particle.r
      particle.x = randomNumBetween(0, canvasWidth)
      particle.r = randomNumBetween(50, 100)
      particle.vy = randomNumBetween(1, 5)
    }
  })

  then = now - (delta & interval)
}

animate()
728x90

 

파티클에 gooey 효과를 주기 위해 canvas에 필터를 적용한다.

canvas {
  filter: blur(10px) contrast(10);
  background-color: rgb(255, 255, 255);
}

blur와 contrast 값을 더 높이면 더 끈적끈적한 효과가 나타난다.

이러한 contrast 효과는 배경색을 지정하지 않으면 일어나지 않는다. 그렇기 때문에 css의 필터 효과가 아닌 svg의 필터 속성을 이용해 커스텀 필터를 정의하는 방법을 알아보자. 위 css 코드는 주석처리한다.

 

html에서 재사용가능한 필터를 정의한다.

filter태그를 만들고 먼저 blur 효과를 만든다. 이때 사용되는 svg 필터는 feGaussian이라는 필터이다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="./main.css">
  <title>Particle</title>
</head>
<body>
  <canvas></canvas>
  <svg>
    <defs>
      <filter id="gooey">
        <feGaussianBlur />
      </filter>
    </defs>
  </svg>
</body>
<script type="module" src="./index.js"></script>
</html>

 

<feGaussianBlur stdDeviation="10" in="SourceGraphic" result="blur1" />

- stdDeviation은 블러의 강도를 결정한다. 위에서는 가로 세로 뱡향에 동일한 정도의 블러 효과를 적용한다. 

- in 속성은 블러를 적용할 입력 이미지를 지정한다. SourceGraphic은 필터가 적용될 원본 그래픽을 나타낸다. 즉 svg 요소 자체에 필터가 적용된다.

- result 속성은 필터의 출력 결과에 이름을 붙인다. 여기서는 블러처리된 이미지를 blur1이라고 이름을 붙였다.

 

이렇게 만든 필터를 css에 적용할 때는 id를 사용하면 된다.

canvas {
  filter: url("#gooey");
}

필터가 잘 적용되었다.

여기에 contrast값을 주면 gooey 효과가 완성된다. feGaussianBlur 아래에 다음 코드를 추가한다.

<feColorMatrix in="blur1" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 1 0" />

feColorMatrix는 입력된 이미지를 색상 변환 매트릭스를 통해 조절한다.

- in: 처리할 입력 이미지를 지정한다.

- mode: 색상 변환 방식의 모드를 지정한다. matrix 모드는 5*4 행렬을 사용하여 색상을 변환한다.

- values: 색상 변환 행렬의 값을 설정한다. 첫번째 행은 빨간색 채널 변환, 두번째 행은 초록색 채널 변환, 세번째 행은 파란색 채널 변환, 네번째 행은 알파 채널 변환을 의미한다.

현재는 입력 이미지의 각 색상 채널을 변경하지 않고 그대로 유지한다. 즉 입력 이미지에 아무런 변화가 생기지 않는다.

여기서 아래처럼 코드를 수정해보면

<feGaussianBlur stdDeviation="40" in="SourceGraphic" result="blur1" />
<feColorMatrix in="blur1" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 100 -20" />

gooey 효과가 나타난다. 

이 svg 필터를 테스트해볼 수 있는 사이트가 있다. -> https://yoksel.github.io/svg-filters/#/

 

SVG Filters

 

yoksel.github.io

 

728x90