Сообщение от рони
|
мне бы шарики прыг-скок от стенки и от друг друга на js.
|
Это физика:
Impulse,
Momentum,
Two-dimensional elastic collision.
В этом примере учитывается позиция, импульс, масса. А вы можете добавить ещё больше физики, например, учитывать температуру, деформацию, гравитацию и пр. И сделать более интересный пример!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html, body, canvas {
display: block;
margin: 0;
background: black;
}
</style>
</head>
<body>
<script>
function getRandomRGBColorString() {
return "#" + ("000000" + Math.floor(0xffffff * Math.random()).toString(16)).slice(-6);
}
function distance(x1, y1, x2, y2) {
const xDist = x2 - x1;
const yDist = y2 - y1;
return Math.sqrt(xDist ** 2 + yDist ** 2);
}
function rotate(velocity, angle) {
return {
x: velocity.x * Math.cos(angle) - velocity.y * Math.sin(angle),
y: velocity.x * Math.sin(angle) + velocity.y * Math.cos(angle)
};
}
function resolveCollision(a, b) {
const dvx = a.velocity.x - b.velocity.x;
const dvy = a.velocity.y - b.velocity.y;
const dx = b.x - a.x;
const dy = b.y - a.y;
if (dvx * dx + dvy * dy >= 0) {
const angle = -Math.atan2(dy, dx);
const m1 = a.mass;
const m2 = b.mass;
const u1 = rotate(a.velocity, angle);
const u2 = rotate(b.velocity, angle);
const v1 = {
x: (u1.x * (m1 - m2) + 2 * m2 * u2.x) / (m1 + m2),
y: u1.y
};
const v2 = {
x: (u2.x * (m2 - m1) + 2 * m1 * u1.x) / (m1 + m2),
y: u2.y
};
a.velocity = rotate(v1, -angle);
b.velocity = rotate(v2, -angle);
}
}
class Circle {
constructor(x, y, r) {
const speed = 20 * Math.random();
this.x = x;
this.y = y;
this.r = r;
this.color = getRandomRGBColorString();
this.mass = r / 3;
this.velocity = {
x: (Math.random() - 0.5) * speed,
y: (Math.random() - 0.5) * speed
}
}
render(context) {
context.fillStyle = this.color;
context.beginPath();
context.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
context.fill();
}
}
class App {
constructor(node) {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
node.appendChild(canvas);
addEventListener("resize", () => {
canvas.width = innerWidth;
canvas.height = innerHeight;
this.init();
});
dispatchEvent(new Event("resize"));
this.render(context);
}
init() {
this.circles = [];
for (let i = 0; i < 30; i++) {
const r = 10 + 20 * Math.random();
let x = Math.random() * (innerWidth - r * 2) + r;
let y = Math.random() * (innerHeight - r * 2) + r;
if (i !== 0) {
for (let j = 0, k = 0; j < this.circles.length; j++) {
if (distance(x, y, this.circles[j].x, this.circles[j].y) - r * 2 < 0) {
x = Math.random() * (innerWidth - r * 2) + r;
y = Math.random() * (innerHeight - r * 2) + r;
j = -1;
/* если вдруг новый шарик не удалось разместить */
if(++k > 1000) break;
}
}
}
this.circles.push(new Circle(x, y, r));
}
}
render(context) {
context.clearRect(0, 0, innerWidth, innerHeight);
for (const circle of this.circles) {
if (circle.x + circle.r > innerWidth || circle.x - circle.r < 0) {
circle.velocity.x = -circle.velocity.x;
}
if (circle.y + circle.r > innerHeight || circle.y - circle.r < 0) {
circle.velocity.y = -circle.velocity.y;
}
circle.x += circle.velocity.x;
circle.y += circle.velocity.y;
for(const anOtherCircle of this.circles) {
if (circle === anOtherCircle) continue;
if (distance(circle.x, circle.y, anOtherCircle.x, anOtherCircle.y) - circle.r - anOtherCircle.r < 0)
resolveCollision(circle, anOtherCircle);
}
circle.render(context);
}
requestAnimationFrame(() => this.render(context));
}
}
new App(document.body);
</script>
</body>
</html>