我在画布上有这个弹性碰撞的工作代码。 如何编辑此代码以更改带有字母的气泡?例如,我想要四个字母“ A”,“ B”,“ C”,“ D”互相反弹。 您有比画布更好的解决方案吗? 谢谢
这里是有效的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)
};
}
};
答案 0 :(得分:0)
一种可能的解决方案:用字母初始化每个“球”,然后使用ctxt.strokeText
绘制字母而不是圆圈。这是对您的Codepen的修改,在修改后的行中带有注释:
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>