<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>partical</title>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
    canvas {
      background-color: black;
      border: 1px solid;
    }
  </style>
</head>
<body>
  <canvas></canvas>
  <script>
    var GAME_OVER = false;
    var PAUSE = false;
    var total = 0;
    var canvas = document.querySelector('canvas');
    var ctx = canvas.getContext('2d');
    canvas.width = 250;//window.innerWidth;
    canvas.height = 250;//window.innerHeight;
    var midX = canvas.width / 2;
    var midY = canvas.height / 2;
    function Vector(x, y){
      this.x = x || 0;
      this.y = y || 0;
    }
    Vector.prototype = {
      add: function(vector){
        this.x += vector.x;
        this.y += vector.y;
      },
      getMagnitude: function(){
        return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
      },
      getAngle: function(){
        return Math.atan2(this.y, this.x);
      }
    };
    Vector.fromAngle = function(angle, magnitude){
      return new Vector(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
    };


    function Particle(point, velocity, acceleration){
      this.position = point || new Vector(0,0);
      this.velocity = velocity || new Vector(0,0);
      this.acceleration = acceleration || new Vector(0,0);
    }
    Particle.prototype = {
      move: function(){
        this.velocity.add(this.acceleration);
        this.position.add(this.velocity);
      },
      bounceX: function(){
        this.velocity.x = -(this.velocity.x);
      },
      bounceY: function(){
        this.velocity.y = -(this.velocity.y);
      }
    };

    function Obstacle(point, status, width, height){
      this.position = point || new Vector(0,0);
      this.status = status || 'active';
      this.width = width || 1;
      this.height = height || 1;
      this.drawColor = "#999";
    }

    function generateObstacles(){
      var arr = [];
      total = 0;
      for (var i = canvas.width * 0.2; i < canvas.width * 0.8; i+= 4) {
        for (var j = canvas.height * 0.2; j < canvas.height * 0.3; j+= 4){
          ob = new Obstacle(new Vector(i, j), 'active', 3, 3);
          total += 1;
          arr.push(ob);
        }
      }
      return arr;
    }
    

    function plotObstacles(){
    }

    function drawObstacles(){
      for (var i = 0; i < obstacles.length ; i++) {
        if(obstacles[i].status == 'active'){
          drawRect(obstacles[i]);
        }
      }
    }

    function Emitter(point, velocity, spread){
      this.position = point;
      this.velocity = velocity;
      this.spread = spread || Math.PI / 32;
      this.drawColor = "#999";
    }
    Emitter.prototype = {
      emitParticle: function(){
        var angle = this.velocity.getAngle();// + this.spread - (Math.random() * this.spread * 2);
        var magnitude = this.velocity.getMagnitude();
        var velocity = Vector.fromAngle(angle, magnitude);
        var position = new Vector(this.position.x, this.position.y);
        return new Particle(position, velocity);
      }
    };
    function Board(point, width, height, velocity){
      this.position = point || new Vector(0,0);
      this.width = width || 1;
      this.height = height || 1;
      this.acceleration = new Vector(0,0);
      this.velocity = velocity || new Vector(0,0);

      this.max_velocity = 1.6;
    }
    Board.prototype = {
      move: function(){
        this.velocity.add(this.acceleration);

        if(this.velocity > this.max_velocity){
          this.velocity = this.max_velocity;
        }
        this.position.add(this.velocity);
      },
      is_stopped: function(){
        return this.velocity.x == 0 && this.velocity.y == 0;
      }
    };
    
    function plotParticle(boundX, boundY){
      if(particle.position.y > boundY){
        GAME_OVER = true;
        return;
      }
      if(particle.position.y < 0){
        particle.bounceY();
      }
      if(particle.position.x < 0 || particle.position.x > boundX){
        particle.bounceX();
      }

      if((boundY - particle.position.y) < 10){

        if(particle.position.x > board.position.x && particle.position.x < board.position.x + board.width && particle.position.y > board.position.y){

          if(board.velocity.x != 0){
              particle.velocity.x = board.velocity.x;
            }
          particle.bounceY();
        }
      }

      for (var i = 0; i < obstacles.length ; i++) {
//        让 obstacle 近似圆,根据两物体中心点距离是否小于两物体半径和判断是否碰撞,很粗
        if(obstacles[i].status == 'active' && Math.sqrt(Math.pow((particle.position.x-(obstacles[i].position.x + obstacles[i].width / 2)),2) + Math.pow((particle.position.y-(obstacles[i].position.y + obstacles[i].height / 2)),2)) <= (1.5 * 1.414 + 2)){
          obstacles[i].status = 'inactive';
          total -= 1;
          if(total <= 0) {
            GAME_OVER = true;
          }


          //从上下碰撞
          if(particle.position.x >= obstacles[i].position.x && particle.position.x <= (obstacles[i].position.x + obstacles[i].width)){
            particle.bounceY();
          }
          //从左右碰撞
          else if (particle.position.y >= obstacles[i].position.y && particle.position.y <= (obstacles[i].position.y + obstacles[i].height)) {
            particle.bounceX();
          }
        }
      }

      particle.move();
    }
    
    function plotBoard(boundX, boundY){
      if(board.position.x < 0){
        board.position.x  = 0;
        board.acceleration  = new Vector(0,0);
        board.velocity = new Vector(0,0);
      }
      if(board.position.x > boundX - board.width){
        board.position.x  = boundX - board.width;
        board.acceleration  = new Vector(0,0);
        board.velocity = new Vector(0,0);
      }
      if(board.position.y < 0){
        board.position.y = 0;
        board.acceleration  = new Vector(0,0);
        board.velocity = new Vector(0,0);
      }
      if(board.position.y > boundY - board.height){
        board.position.y = boundY - board.height;
        board.acceleration  = new Vector(0,0);
        board.velocity = new Vector(0,0);
      }
      board.move();
    } 

    var particleSize = 2;
    function drawParticle(){
      ctx.fillStyle = 'rgb(255,255,255)';
      ctx.beginPath();
      ctx.arc(particle.position.x, particle.position.y, particleSize, 0, Math.PI * 2);
      ctx.closePath();
      ctx.fill();
    }
    var objectSize = 2;
    function drawCircle(object){
      ctx.fillStyle = object.drawColor;
      ctx.beginPath();
      ctx.arc(object.position.x, object.position.y, object.size || objectSize, 0, Math.PI * 2);
      ctx.closePath();
      ctx.fill();
    }
    function drawBoard(){
      ctx.fillStyle = 'rgb(0,255,0)';
      ctx.fillRect(board.position.x, board.position.y, board.width, board.height);
    }

    function drawRect(object){
      ctx.fillStyle = object.drawColor;
      ctx.fillRect(object.position.x, object.position.y, object.width, object.height);
    }

    function drawPause(){
      if(PAUSE) {
        ctx.fillStyle = "#999";
        ctx.fillText('pause', 100, 100);
      }

    }
    
    function boardListenEvent(){
      document.addEventListener('keydown', function(e){
        switch(e.keyCode){
          case 65:
            // console.log('keydown left');
            if(board.velocity.x > 0){
              board.velocity.x = 0;
            }
            board.acceleration = new Vector(-0.2, board.acceleration.y);
            break;
          case 68:
            // console.log('keydown right');
            if(board.velocity.x < 0){
              board.velocity.x = 0;
            }
            board.acceleration = new Vector(0.2, board.acceleration.y);
            break;
          case 32:
            // console.log('keydown run/pause toggle');
            PAUSE = !PAUSE;
            if(!PAUSE) {
              window.requestAnimationFrame(loop);
            }
            break;
          default:
             console.log('keydown skip code: ', e.keyCode);
        }
      },false);
      document.addEventListener('keyup', function(e){
        switch(e.keyCode){
          case 65:
            // console.log('keyup left');
            board.acceleration = new Vector(0, 0);
            board.velocity = new Vector(0, 0);
            break;
          case 68:
            // console.log('keyup right');
            board.acceleration = new Vector(0, 0);
            board.velocity = new Vector(0, 0);
            break;
          default:
            // console.log('keyup skip code: ', e.keyCode);
        }
        // console.log('now velocity:', board.velocity);
      }, false);
    }
    var board,emitter,particle,obstacles;
    function begin(){
      board = new Board(new Vector(0,canvas.height), 40, 5);
      emitter = new Emitter(new Vector(midX - 50, midY), Vector.fromAngle(-1, 2), Math.PI / 4);
      particle = emitter.emitParticle();
      obstacles = generateObstacles();
    }
    function loop(){
      clear();
      update();
      draw();
      queue();
    }
    function clear(){
      ctx.clearRect(0,0, canvas.width, canvas.height);
    }
    function update(){
      plotBoard(canvas.width, canvas.height);
      plotParticle(canvas.width, canvas.height);
      plotObstacles();
    }
    function draw(){
      drawParticle();
      drawBoard();
      drawObstacles();
      drawPause();
    }
    function queue(){
      if(!over() && !PAUSE){
        window.requestAnimationFrame(loop);
      }
      else if(over()) {
        var confirm = window.confirm('game over, play again?');
        if(confirm) {
          GAME_OVER = false;
          begin();
          loop();
        }
      }
    }

    function over(){
      return GAME_OVER == true;
    }

    begin();
    boardListenEvent();
    loop();
  </script>
</body>
</html>