본문 바로가기
CSS

[Canvas] 버튼 Particle 이펙트

by F.E.D 2019. 1. 30.

[Canvas] 버튼 Particle 이펙트

HTML 요소의 자유를 결합하여 웹 페이지의 시각적 기능을 향상시키는 방법에 대해 살펴 보겠습니다.  최신브라우저에만 적용이 가능합니다.


초기 상태

See the Pen DOM to Canvas #1 by Zach Saucier (@Zeaklous) on CodePen.

캔버스 버전

html2canvas라는 매우 편리한 라이브러리가 도움이 됩니다.
라이브러리를 로드 한 다음 html2canvas를 호출하면 엘리먼트의 캔버스 버전과 함께 Promise가 반환됩니다.

See the Pen DOM to Canvas #2 by Zach Saucier (@Zeaklous) on CodePen.

여기에는 HTML 버전과 캔버스 버전의 버튼이 있습니다. 
캔버스 버전은 "screenshot"으로 사용하거나 특정 위치의 픽셀 색상과 같은 정보의 소스로 사용할 수 있습니다.

캔버스에서 데이터 가져 오기

이렇게하려면 특정 위치에서 픽셀 정보를 가져 오는 새로운 함수를 만들어 보겠습니다. 
원래의 HTML 요소를 대신 보여주기 때문에 색상 데이터를 가져올 캔버스를 표시 할 필요도 없습니다.

1
2
3
4
5
6
7
8
9
10
function getColorAtPoint(e) {
  // Get the coordinate of the click
  let x = e.offsetX;
  let y = e.offsetY;
  
  // Get the color data of the canvas version of our element at that location
  let rgbaColorArr = ctx.getImageData(x, y, 11).data;
 
  // Do something with rgbaColorArr
}
cs

See the Pen DOM to Canvas #3 by Zach Saucier (@Zeaklous) on CodePen.

이제 위에 가져온 캔버스 정보를 가지고 입자를 생성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var particleCanvas, particleCtx;
function createParticleCanvas() {
  // Create our canvas
  particleCanvas = document.createElement("canvas");
  particleCtx = particleCanvas.getContext("2d");
  
  // Size our canvas
  particleCanvas.width = window.innerWidth;
  particleCanvas.height = window.innerHeight;
  
  // Position out canvas
  particleCanvas.style.position = "absolute";
  particleCanvas.style.top = "0";
  particleCanvas.style.left = "0";
  
  // Make sure it's on top of other elements
  particleCanvas.style.zIndex = "1001";
  
  // Make sure other elements under it are clickable
  particleCanvas.style.pointerEvents = "none";
  
  // Add our canvas to the page
  document.body.appendChild(particleCanvas);
}
cs

우리는 또한 버튼의 위와 왼쪽뿐 아니라 오른쪽에서 입자를 생성하기 위해 전역 좌표의 위치인 로컬 좌표에서 색상 데이터를 계속 가져와야합니다. 

좌표 데이터 가져 오기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
btn.addEventListener("click", e => {
  // Get our color data like before
  let localX = e.offsetX;
  let localY = e.offsetY;
  let rgbaColorArr = ctx.getImageData(localX, localY, 11).data;
  
  // Get the button's positioning in terms of the window
  let bcr = btn.getBoundingClientRect();
  let globalX = bcr.left + localX;
  let globalY = bcr.top + localY;
  
  // Create a particle using the color we obtained at the window location
  // that we calculated
  createParticleAtPoint(globalX, globalY, rgbaColorArr);
});
cs

파티클 프로토 타입 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* An "exploding" particle effect that uses circles */
var ExplodingParticle = function() {
  // Set how long we want our particle to animate for
  this.animationDuration = 1000// in ms
 
  // Set the speed for our particle
  this.speed = {
    x: -5 + Math.random() * 10,
    y: -5 + Math.random() * 10
  };
  
  // Size our particle
  this.radius = 5 + Math.random() * 5;
  
  // Set a max time to live for our particle
  this.life = 30 + Math.random() * 10;
  this.remainingLife = this.life;
  
  // This function will be called by our animation logic later on
  this.draw = ctx => {
    let p = this;
 
    if(this.remainingLife > 0 && this.radius > 0) {
      // Draw a circle at the current location
      ctx.beginPath();
      ctx.arc(p.startX, p.startY, p.radius, 0, Math.PI * 2);
      ctx.fillStyle = "rgba(" + this.rgbArray[0+ ',' + this.rgbArray[1+ ',' + this.rgbArray[2+ ", 1)";
      ctx.fill();
      
      // Update the particle's location and life
      p.remainingLife--;
      p.radius -= 0.25;
      p.startX += p.speed.x;
      p.startY += p.speed.y;
    }
  }
}
cs

입자 팩토리 만들기

또한 좌표와 색상 정보를 기반으로 입자를 생성하는 함수가 필요합니다. 생성 된 입자의 배열에 추가해야합니다.

1
2
3
4
5
6
7
8
9
10
var particles = [];
function createParticleAtPoint(x, y, colorData) {
  let particle = new ExplodingParticle();
  particle.rgbArray = colorData;
  particle.startX = x;
  particle.startY = y;
  particle.startTime = Date.now();
  
  particles.push(particle);
}
cs

애니메이션 논리 추가

또한 생성된 모든 입자에 애니메이션을 적용하는 방법이 필요합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function update() {
  // Clear out the old particles
  if(typeof particleCtx !== "undefined") {
    particleCtx.clearRect(00window.innerWidth, window.innerHeight);
  }
 
  // Draw all of our particles in their new location
  for(let i = 0; i < particles.length; i++) {
    particles[i].draw(particleCtx);
    
    // Simple way to clean up if the last particle is done animating
    if(i === particles.length - 1) {
      let percent = (Date.now() - particles[i].startTime) / particles[i].animationDuration[i];
      
      if(percent > 1) {
        particles = [];
      }
    }
  }
  
  // Animate performantly
  window.requestAnimationFrame(update);
}
 
window.requestAnimationFrame(update);
cs

HTML 요소를 기반으로 입자를 만들 수 있습니다!

See the Pen DOM to Canvas #4 by Zach Saucier (@Zeaklous) on CodePen.

한 픽셀 대신 클릭하여 전체 버튼을 "폭발"시키려면 클릭 기능 만 수정하면됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let reductionFactor = 17;
btn.addEventListener("click", e => {
  // Get the color data for our button
  let width = btn.offsetWidth;
  let height = btn.offsetHeight
  let colorData = ctx.getImageData(00, width, height).data;
  
  // Keep track of how many times we've iterated (in order to reduce
  // the total number of particles create)
  let count = 0;
  
  // Go through every location of our button and create a particle
  for(let localX = 0; localX < width; localX++) {
    for(let localY = 0; localY < height; localY++) {
      if(count % reductionFactor === 0) {
        let index = (localY * width + localX) * 4;
        let rgbaColorArr = colorData.slice(index, index + 4);
 
        let bcr = btn.getBoundingClientRect();
        let globalX = bcr.left + localX;
        let globalY = bcr.top + localY;
 
        createParticleAtPoint(globalX, globalY, rgbaColorArr);
      }
      count++;
    }
  }
});
cs

See the Pen DOM to Canvas #5 by Zach Saucier (@Zeaklous) on CodePen.

웹은 이제 더 창조적인 자유를 행사하기 위해 캔버스와 같은 추가 기능을 사용할 수 있다는 것을 알게 되었습니다.


가장자리 감지 기능을 사용하여 요소가 컨테이너 경계 외부에 있는지 (예 :보기에서 숨겨져 있는지) 알려주고 요소가 경계 외부로 나가면 입자를 생성하여 훨씬 더 창의적으로 처리 할 수 ​​있습니다.


출처 : https://css-tricks.com/adding-particle-effects-to-dom-elements-with-canvas/



'CSS' 카테고리의 다른 글

Bootstrap5 새소식(큰 변화 4가지)  (6) 2019.12.22
[CSS] Rethinking CSS  (0) 2019.01.31
[Reflow] Reflow 최소화  (0) 2019.01.29
Will-Change  (0) 2019.01.22
잘 알려지지 않은 CSS 팁  (1) 2019.01.01

댓글