본문 바로가기

HTML5/Pixel Destroy2

HTML5 Particle effects

HTML5 Particle Effects (HTML 5 파티클 효과 구현) 예


마우스로 이미지를 클릭하면 해당 영역의 픽셀들이 파편(파티클)이 되어 사방으로 날리는 효과를 구현해 본 것이며 파티클의 이동방향과 속도를 결정하기 위해 Vector2D 오브젝트를 정의하고 벡터연산에 사용했다

<!DOCTYPE HTML>
<html>
  <head>
    <style>
      body {
        margin: 0px;
        padding: 20px;
      }
      canvas { background:url(bg.png) }
    </style>
      
  <script type="text/javascript">
  var canvas;
  var offCanvas;
  var offContext;
  var mainLevel;
  var timer;
  var particles;
   
  window.onload = function() {
    init();
  }
   
  function init() {
       
     offCanvas = document.createElement('canvas');
     offCanvas.height = 500;
     offCanvas.width = 500;
     offContext = offCanvas.getContext('2d');
      
     var imageObj = new Image();
     imageObj.onload = function() {
        mainLevel = this;
        offContext.drawImage(this,0,0);
        timer = setInterval( gameLoop, 60);
     };
     imageObj.src = 'wall.png';
        
    canvas = document.getElementById('myCanvas');
     
    canvas.addEventListener('mousedown', function(evt){
        var rect = canvas.getBoundingClientRect();
  
        var cx = evt.clientX-rect.left;
        var cy = evt.clientY-rect.top;
        //파티클 생성, 배열에 저장
        if(!particles) particles = [];
 
        var idx = 0;
        for(var i=0;i<4;i++) {
            for(var j=0;j<4;j++) {
                var vp = Math.round(Math.random()*10)%5+5;  //파티클 속도
 
                var vr = Math.round(Math.random()*100)%20+10;//파티클 회전속도
                if(Math.round(Math.random()*10)%2==0) vr=-vr;
                 
                var targetV = new Vector2D(cx,cy);// 마우스 클릭 위치
                var particlePos = new Vector2D(cx-20+(i*10),cy-20+(j*10));//파티클 위치
                var directionV = particlePos.sub(targetV);	// 파티클 비산 방향 결정
                if(directionV.x==0 && directionV.y==0) directionV.y=1; // 방향이 없는 파티클 방지
                var particleNormV = directionV.normalize();	// 방향정보만 사용하기 위한 단위벡터
                var particleV = particleNormV.multiply(vp); // 방향정보에 속도를 곱해서 파티클의 방향과 속도를 완성
 
                //particles배열에 추가
                particles[idx++] = new Particle(cx-20+(i*10), cy-20+(j*10), particleV.x, particleV.y, vr);
            }
        }
        //백버퍼에 그려진 메인 이미지에 투명한 구멍을 낸다. 백버퍼는 메인 캔바스에 그릴 때 복사된다
        transOffRegion(cx,cy);
    });
  }
   
   
  function gameLoop(){
      drawScene();
      if(particles && particles.length>0) {
          for(var i=0;i<particles.length;i++) {
              if(particles[i]) particles[i].draw();
          }
      }
  }
 
  // 캔바스에 이미지를 그린다
  function drawScene() {
    if(!canvas) canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');
    context.clearRect(0,0,canvas.width,canvas.height);
    //백버퍼에 그려진 이미지를 복사해서 메인 캔바스에 그린다
    context.drawImage(offCanvas,0,0);
  }
   
  // 백버퍼 이미지에 구멍을 낸다. 백버퍼는 메인 캔바스에 복사하여 그릴때 사용한다
  function transOffRegion(cx,cy) {
      if(!offCanvas) {
          offCanvas = document.createElement('canvas');
      }
      var offCtx = offCanvas.getContext('2d');
 
      offCtx.globalCompositeOperation = 'destination-out';
      offCtx.beginPath();
        
      offCtx.moveTo(cx-15, cy-10);
      offCtx.lineTo(cx, cy-15);
      offCtx.lineTo(cx+15, cy-10);
      offCtx.lineTo(cx+20, cy);
      offCtx.lineTo(cx+15, cy+10);
      offCtx.lineTo(cx, cy+15);
      offCtx.lineTo(cx-15, cy+10);
      offCtx.lineTo(cx-20, cy);
        
      offCtx.closePath();
      offCtx.fill();
  }
 
  // 파티클을 표현한 생성자 함수
  function Particle(x, y, vx, vy, vr){
       
      this.x = x;
      this.y = y;
      this.vx = vx;
      this.vy = vy;
      this.rotDeg = 0;
      this.vr = vr;
      this.startTime = new Date();
      this.tmpCanvas;
      //메인 캔바스에서 지정된 위치의 이미지 데이터(파편)를 가져와서 임시 캔바스에 그린다
      if(!canvas) canvas = document.getElementById('myCanvas');
      var ctx = canvas.getContext('2d');
      var imgData = ctx.getImageData(this.x,this.y, 10,10);
      this.tmpCanvas = document.createElement('canvas');
      this.tmpCanvas.height = imgData.height;
      this.tmpCanvas.width = imgData.width;
       
      var tmpCtx = this.tmpCanvas.getContext('2d');
      tmpCtx.putImageData(imgData,0,0);
 
      //게임루프에서 매 프레임마다 호출되어 화면에 현재 파티클을 그린다
      this.draw = function(){
        if(!canvas) canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        if(this.y>canvas.height || this.x>canvas.width || this.x<0) {
            var idx = particles.indexOf(this);
            particles.splice(idx,1);
            delete particles[idx];
            return;
        } 
        if(new Date()-this.startTime>2000) {
            var idx = particles.indexOf(this);
            particles.splice(idx,1);
            delete particles[idx];
            return;
        }
        this.vx *= 0.9;
        this.vy += 0.5;
         
        ctx.save();
        ctx.translate(this.x+=this.vx, this.y+=this.vy);
        ctx.rotate((this.rotDeg+=vr)*Math.PI/180);
        ctx.drawImage(this.tmpCanvas,0,0);
        ctx.restore();
      }
  }
   
  // 파티클의 이동 방향과 속도를 표현하며 벡터 연산에 사용될 생성자 함수
  function Vector2D(x,y) {
       
      this.x = x;
      this.y = y;
       
      this.add = function(v2d) {
          return new Vector2D(this.x + v2d.x, this.y + v2d.y);
      }
       
      this.sub = function(v2d) {
          return new Vector2D(this.x - v2d.x, this.y - v2d.y);
      }
       
      this.multiply = function(len) {
          return new Vector2D(this.x * len, this.y * len);
      }
       
      this.normalize = function() {
          var len = Math.sqrt((this.x * this.x + this.y * this.y));
          return new Vector2D(this.x/len, this.y/len);
      }
  }
  </script>   
  </head>
  <body>
 <canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>