# 实践-弹弹球

# 预览效果

刷新网页重新开始动画

# 实现一个二维向量类

export function Vector2(x, y) {
  this.x = x;
  this.y = y;
}

Vector2.prototype = {
  // 复制
  copy: function () {
    return new Vector2(this.x, this.y);
  },
  // 取模
  length: function () {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  },
  // 平方和
  sqrLength: function () {
    return this.x * this.x + this.y * this.y;
  },
  // 方向矢量(单位矢量)
  normalize: function () {
    var inv = 1 / this.length();
    return new Vector2(this.x * inv, this.y * inv);
  },
  // 取反
  negate: function () {
    return new Vector2(-this.x, -this.y);
  },
  // 加法
  add: function (v) {
    return new Vector2(this.x + v.x, this.y + v.y);
  },
  // 减法
  subtract: function (v) {
    return new Vector2(this.x - v.x, this.y - v.y);
  },
  // 数乘
  multiply: function (f) {
    return new Vector2(this.x * f, this.y * f);
  },
  // 除法
  divide: function (f) {
    var invf = 1 / f;
    return new Vector2(this.x * invf, this.y * invf);
  },
  // 点乘
  dot: function (v) {
    return this.x * v.x + this.y * v.y;
  },
  // 投影
  projection: function (v) {}
};

# 实现一个球类

export function Ball(color, mass, pos, vel, acc) {
  this.color = color;
  this.mass = mass;
  this.r = Math.cbrt(mass);
  this.pos = pos;
  this.vel = vel;
  this.acc = acc;
}

// 小球绘制方法 传入canvas上下文
Ball.prototype.draw = function (ctx) {
  ctx.beginPath();
  ctx.fillStyle = this.color;
  ctx.arc(this.pos.x, this.pos.y, this.r, 0, Math.PI * 2);
  ctx.closePath();
  ctx.fill();
};

// 小球运动方法 与其他球以及墙壁交互 并且传入canvas上下文
Ball.prototype.move = function (ball, container) {
  // 位置
  this.pos = this.pos.add(this.vel);
  // 速度
  this.vel = this.vel.add(this.acc);
  // 边界检测
  // 撞击左右墙壁 速度反向并且衰减1%
  if (
    this.pos.x < container.left + this.r ||
    this.pos.x > container.right - this.r
  ) {
      this.vel.x *= -0.99;
  }
  // 撞击上下墙壁 速度反向并且衰减1%
  if (
    this.pos.y < container.top + this.r ||
    this.pos.y > container.bottom - this.r
  ) {
    this.vel.y *= -0.99;
  }
  // 小球之间的撞击
  if (isBallHit(this, ball)) {
    // console.log('碰撞了');
    // 假设为完全弹性碰撞
    let v10 = this.vel.copy();
    let v20 = ball.vel.copy();

    this.vel = v10
      .multiply(this.mass - ball.mass)
      .add(v20.multiply(2 * ball.mass))
      .divide(this.mass + ball.mass);

    ball.vel = v20
      .multiply(ball.mass - this.mass)
      .add(v10.multiply(2 * this.mass))
      .divide(this.mass + ball.mass);
  }
};

# 实现一个墙壁类

export function Container(left, right, top, bottom) {
  this.left = left;
  this.right = right;
  this.top = top;
  this.bottom = bottom;
  this.width = this.right - this.left;
  this.height = this.bottom - this.top;
}

Container.prototype.draw = function (ctx) {
  ctx.strokeRect(this.left, this.top, this.width, this.height);
};