How can i add text to elastic collision in canvas?

问题: I have this working code of an elastic collision in canvas. How can I edit this code to change the bubbles with letters? For example I would like four letters 'A', 'B', 'C'...

问题:

I have this working code of an elastic collision in canvas. How can I edit this code to change the bubbles with letters? For example I would like four letters 'A', 'B', 'C', 'D' bouncing each other. Do you have better solution than canvas? thank you

Here the working codepen https://codepen.io/andreamante/pen/MxxxEB

var Ball = function(hue, r, o, v) {
  var k = EXPLAIN_MODE?4:1, 
      l = 100;//luminosita

  this.hue = hue || rand(360, 0, 1);
  this.c = 'hsl('+ this.hue +',100%,' + l + '%)';

  this.r = r || 50;

  this.o = o || null;

  this.init = function() {
    if(!this.o) {
      this.o = {
        'x': rand(w - this.r, this.r, 1), 
        'y': rand(h - this.r, this.r, 1)
      };
    }

    if(!this.v) {
      this.v = {
        'x': randSign()*rand(sqrt(k)*4, k), 
        'y': randSign()*rand(sqrt(k)*4, k)
      };
    }
  };

回答1:

One possible solution: initialize each "Ball" with a letter, and then use the ctxt.strokeText to draw the letter instead of a circle. Here's a modification of your codepen, with comments on the modified lines:

Object.getOwnPropertyNames(Math).map(function(p) {
  window[p] = Math[p];
});

if (!hypot) {
  var hypot = function(x, y) {
    return sqrt(pow(x, 2) + pow(y, 2));
  }
}

var rand = function(max, min, is_int) {
  var max = ((max - 1) || 0) + 1,
    min = min || 0,
    gen = min + (max - min) * random();

  return (is_int) ? round(gen) : gen;
};

var randSign = function(k) {
  return (random() < (k || .5)) ? -1 : 1;
};

var sigma = function(n) {
  return n / abs(n);
};

var mu = function(values, weights) {
  var n = min(values.length, weights.length),
    num = 0,
    den = 0;

  for (var i = 0; i < n; i++) {
    num += weights[i] * values[i];
    den += weights[i];
  }

  return num / den;
}

var N_BALLS = 6,
  EXPLAIN_MODE = false,
  balls = [],
  // declare the set of letters to use
  letters = ["A", "B", "C", "D", "E", "F"],
  c = document.querySelector('canvas'),
  w, h,
  ctx = c.getContext('2d'),
  r_id = null,
  running = true;

var Segment = function(p1, p2) {
  this.p1 = p1 || null;
  this.p2 = p2 || null;
  this.alpha = null;

  this.init = function() {
    if (!this.p1) {
      this.p1 = {
        'x': rand(w, 0, 1),
        'y': rand(h, 0, 1)
      };
    }

    if (!this.p2) {
      this.p2 = {
        'x': rand(w, 0, 1),
        'y': rand(h, 0, 1)
      };
    }

    this.alpha = atan2(this.p2.y - this.p1.y,
      this.p2.x - this.p1.x);
  };

  this.init();
};

// initialize Ball() with a letter
var Ball = function(hue, letter, r, o, v) {
  var k = EXPLAIN_MODE ? 4 : 1,
    l = 100; //luminosita

  this.hue = hue || rand(360, 0, 1);
  this.c = 'hsl(' + this.hue + ',100%,' + l + '%)';

  this.r = r || 50;

  this.o = o || null;

  // assign the letter argument to a local variable in Ball()
  this.letter = letter;

  this.init = function() {
    if (!this.o) {
      this.o = {
        'x': rand(w - this.r, this.r, 1),
        'y': rand(h - this.r, this.r, 1)
      };
    }

    if (!this.v) {
      this.v = {
        'x': randSign() * rand(sqrt(k) * 4, k),
        'y': randSign() * rand(sqrt(k) * 4, k)
      };
    }
  };

  this.handleWallHits = function(dir, lim, f) {
    var cond = (f === 'up') ?
      (this.o[dir] > lim) :
      (this.o[dir] < lim);

    if (cond) {
      this.o[dir] = lim;
      this.v[dir] *= -1;
    }
  };

  this.keepInBounds = function() {
    this.handleWallHits('x', this.r, 'low');
    this.handleWallHits('x', w - this.r, 'up');
    this.handleWallHits('y', this.r, 'low');
    this.handleWallHits('y', h - this.r, 'up');
  };

  this.move = function() {
    this.o.x += this.v.x;
    this.o.y += this.v.y;

    this.keepInBounds();
  };

  this.distanceTo = function(p) {
    return hypot(this.o.x - p.x, this.o.y - p.y);
  };

  this.collidesWith = function(b) {
    return this.distanceTo(b.o) < (this.r + b.r);
  };

  this.handleBallHit = function(b, ctxt) {
    var theta1, theta2,

      /* the normal segment */
      ns = new Segment(this.o, b.o),

      /* contact point */
      cp = {
        'x': mu([this.o.x, b.o.x], [b.r, this.r]),
        'y': mu([this.o.y, b.o.y], [b.r, this.r])
      };

    this.cs = {
      'x': sigma(cp.x - this.o.x),
      'y': sigma(cp.y - this.o.y)
    };
    b.cs = {
      'x': sigma(cp.x - b.o.x),
      'y': sigma(cp.y - b.o.y)
    };

    this.o = {
      'x': cp.x -
        this.cs.x * this.r * abs(cos(ns.alpha)),
      'y': cp.y -
        this.cs.y * this.r * abs(sin(ns.alpha))
    };
    b.o = {
      'x': cp.x - b.cs.x * b.r * abs(cos(ns.alpha)),
      'y': cp.y - b.cs.y * b.r * abs(sin(ns.alpha))
    };

    if (EXPLAIN_MODE) {
      ctxt.clearRect(0, 0, w, h);
      this.draw(ctxt);
      b.draw(ctxt);

      this.connect(b, ctxt);
    }

    this.v.alpha = atan2(this.v.y, this.v.x);
    b.v.alpha = atan2(b.v.y, b.v.x);

    this.v.val = hypot(this.v.y, this.v.x);
    b.v.val = hypot(b.v.y, b.v.x);

    theta1 = ns.alpha - this.v.alpha;
    theta2 = ns.alpha - b.v.alpha;

    this.v.alpha -= PI - 2 * theta1;
    b.v.alpha -= PI - 2 * theta2;

    this.v.x = this.v.val * cos(this.v.alpha);
    this.v.y = this.v.val * sin(this.v.alpha);

    b.v.x = b.v.val * cos(b.v.alpha);
    b.v.y = b.v.val * sin(b.v.alpha);

    if (EXPLAIN_MODE) {
      ctxt.setLineDash([0]);
      this.drawV(ctxt, 'gold');
      b.drawV(ctxt, 'blue');

      running = false;
      cancelAnimationFrame(r_id);
    }
  };

  this.connect = function(b, ctxt) {
    ctxt.strokeStyle = '#fff';
    ctxt.setLineDash([5]);

    ctxt.beginPath();
    ctxt.moveTo(this.o.x, this.o.y);
    ctxt.lineTo(b.o.x, b.o.y);
    ctxt.closePath();
    ctxt.stroke();
  };

  this.drawV = function(ctxt, lc) {
    var m = 32;

    ctxt.strokeStyle = lc || this.c;

    ctxt.beginPath();
    ctxt.moveTo(this.o.x, this.o.y);
    ctxt.lineTo(this.o.x + m * this.v.x,
      this.o.y + m * this.v.y);
    ctxt.closePath();
    ctxt.stroke();
  };

  this.draw = function(ctxt) {
    ctxt.strokeStyle = this.c;
    // draw the letter instead of a circle
    ctxt.font = "80px Georgia";
    ctxt.strokeText(this.letter, this.o.x, this.o.y);

    if (EXPLAIN_MODE) {
      this.drawV(ctxt);
    }
  };

  this.init();
};

var init = function() {
  var s = getComputedStyle(c),
    hue;

  w = c.width = ~~s.width.split('px')[0];
  h = c.height = ~~s.height.split('px')[0];

  if (r_id) {
    cancelAnimationFrame(r_id);
    r_id = null;
  }

  balls = [];

  ctx.lineWidth = 3;

  if (EXPLAIN_MODE) {
    N_BALLS = 2;
    running = true;
  }

  for (var i = 0; i < N_BALLS; i++) {
    hue = EXPLAIN_MODE ? (i * 169 + 1) : null;
    balls.push(new Ball(hue, letters[i]));
  }

  handleCollisions();

  draw();
};

var handleCollisions = function() {
  var collis = false;

  do {
    for (var i = 0; i < N_BALLS; i++) {
      for (var j = 0; j < i; j++) {
        if (balls[i].collidesWith(balls[j])) {
          balls[i].handleBallHit(balls[j], ctx);
        }
      }
    }
  } while (collis);
};

var draw = function() {
  ctx.clearRect(0, 0, w, h);

  for (var i = 0; i < N_BALLS; i++) {
    ctx.setLineDash([0]);
    balls[i].draw(ctx);
    balls[i].move();
    handleCollisions();
  }

  if (!EXPLAIN_MODE || running) {
    r_id = requestAnimationFrame(draw);
  }
};

setTimeout(function() {
  init();

  addEventListener('resize', init, false);
  c.addEventListener('dblclick', init, false);
  addEventListener('keydown', function(e) {
    if (e.keyCode == 13) {
      //EXPLAIN_MODE = !EXPLAIN_MODE;
      //init();
    }
  }, false);
}, 15);
html,
body,
canvas {
  height: 100%
}

html {
  overflow: hidden;
}

body {
  margin: 0
}

canvas {
  width: 100%;
  background: #000;
}
<canvas></canvas>

  • 发表于 2019-03-26 23:08
  • 阅读 ( 161 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除