Skip to content

Commit 40f0eac

Browse files
+ 修复ch11/04里面计算误差的问题
+ 完成ch11/05和ch11/06的改写
1 parent d1a42b8 commit 40f0eac

File tree

3 files changed

+328
-1
lines changed

3 files changed

+328
-1
lines changed

custom/examples/ch11/04-billiard-4.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
function rotate(x, y, sin, cos, reverse = false) {
7878
return {
7979
x: reverse ? (x * cos + y * sin) : (x * cos - y * sin),
80-
y: reverse ? (y * cos - y * sin) : (x * cos + y * sin)
80+
y: reverse ? (y * cos - x * sin) : (y * cos + x * sin)
8181
}
8282
}
8383

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Bobbing 1</title>
6+
<link rel="stylesheet" href="../include/style.css">
7+
<style type="text/css">
8+
#canvas {
9+
background: #fff;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<header>
15+
Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a>
16+
</header>
17+
<canvas id="canvas" width="400" height="400"></canvas>
18+
19+
<script type="module">
20+
import Ball from './classes/ball.js'
21+
22+
window.onload = function () {
23+
const canvas = document.getElementById('canvas'),
24+
context = canvas.getContext('2d'),
25+
{width, height} = canvas,
26+
centerPointY = height / 2,
27+
ballNum = 8,
28+
balls = [],
29+
bouncing = -1.0;
30+
31+
for (let i = 0; i < ballNum; i++) {
32+
let radius = Math.random() * 20 + 15;
33+
let ball = new Ball(radius);
34+
ball.mass = radius;
35+
ball.x = i * 100;
36+
ball.y = i * 50;
37+
ball.vx = Math.random() * 10 -5;
38+
ball.vy = Math.random() * 10 -5;
39+
balls.push(ball);
40+
}
41+
42+
43+
function checkBoundary(ball) {
44+
const {x, y, radius} = ball;
45+
46+
if (x - radius < 0) {
47+
ball.x = radius;
48+
ball.vx *= bouncing;
49+
} else if (x + radius > width) {
50+
ball.x = width - radius;
51+
ball.vx *= bouncing;
52+
}
53+
54+
if (y - radius < 0) {
55+
ball.y = radius;
56+
ball.vy *= bouncing;
57+
} else if (y + radius > height) {
58+
ball.y = height - radius;
59+
ball.vy *= bouncing;
60+
}
61+
}
62+
63+
function hitProcess(m0, v0, m1, v1) {
64+
const totalV = v0 - v1;
65+
const v0Final = ((m0 - m1) * v0 + 2 * m1 * v1) /
66+
(m0 + m1);
67+
const v1Final = v0Final + totalV;
68+
69+
return {
70+
v0: v0Final,
71+
v1: v1Final
72+
}
73+
}
74+
75+
function rotate(x, y, sin, cos, reverse = false) {
76+
return {
77+
x: reverse ? (x * cos + y * sin) : (x * cos - y * sin),
78+
y: reverse ? (y * cos - x * sin) : (y * cos + x * sin)
79+
}
80+
}
81+
82+
function hitDetection(ballA, ballB) {
83+
const minDist = ballA.radius + ballB.radius;
84+
85+
// ballB 相对于 ballA旋转
86+
const dx = ballB.x - ballA.x,
87+
dy = ballB.y - ballA.y,
88+
dist = Math.sqrt(dx * dx + dy * dy);
89+
90+
if (dist >= minDist) {
91+
return;
92+
}
93+
94+
let angle = Math.atan2(dy, dx),
95+
sin = Math.sin(angle),
96+
cos = Math.cos(angle);
97+
98+
// rotate
99+
let posA = {x: 0, y: 0},
100+
posB = rotate(dx, dy, sin, cos, true),
101+
vA = rotate(ballA.vx, ballA.vy, sin, cos, true),
102+
vB = rotate(ballB.vx, ballB.vy, sin, cos, true);
103+
104+
let innerX = minDist - dist;
105+
106+
// make sure hit is just at interface
107+
if (innerX) {
108+
console.log('inner hit')
109+
// vA.x * t + vB.x * t = innerX
110+
// (vA.x + vB.x) * t = innerX;
111+
let t = innerX / (Math.abs(vA.x) + Math.abs(vB.x));
112+
let sA = Math.abs(vA.x * t),
113+
sB = Math.abs(vB.x * t);
114+
115+
posA.x -= sA;
116+
posB.x += sB;
117+
}
118+
119+
// hit
120+
let {v0: vAX, v1: vBX} = hitProcess(ballA.mass, vA.x, ballB.mass, vB.x);
121+
vA.x = vAX;
122+
vB.x = vBX;
123+
124+
// update position
125+
posA.x += vA.x;
126+
posB.x += vB.x;
127+
128+
// rotate position back
129+
let posAFinal = rotate(posA.x, posA.y, sin, cos),
130+
posBFinal = rotate(posB.x, posB.y, sin, cos);
131+
132+
// B先于A计算,避免A的值改动导致B计算不正确
133+
ballB.x = ballA.x + posBFinal.x;
134+
ballB.y = ballA.y + posBFinal.y;
135+
ballA.x = ballA.x + posAFinal.x;
136+
ballA.y = ballA.y + posAFinal.y;
137+
138+
// rotate velocities back
139+
let vAFinal = rotate(vA.x, vA.y, sin, cos),
140+
vBFinal = rotate(vB.x, vB.y, sin, cos);
141+
142+
ballA.vx = vAFinal.x;
143+
ballA.vy = vAFinal.y;
144+
ballB.vx = vBFinal.x;
145+
ballB.vy = vBFinal.y;
146+
}
147+
148+
(function draw(){
149+
window.requestAnimationFrame(draw);
150+
context.clearRect(0, 0, width, height);
151+
152+
balls.forEach(ball => {
153+
ball.x += ball.vx;
154+
ball.y += ball.vy;
155+
})
156+
157+
for (let i = 0; i < ballNum; i++) {
158+
for(let j = i + 1; j < ballNum; j++) {
159+
let ballA = balls[i],
160+
ballB = balls[j];
161+
162+
hitDetection(ballA, ballB);
163+
}
164+
}
165+
166+
balls.forEach(ball => {
167+
checkBoundary(ball);
168+
ball.draw(context);
169+
})
170+
}())
171+
}
172+
</script>
173+
</body>
174+
</html>
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Bobbing 1</title>
6+
<link rel="stylesheet" href="../include/style.css">
7+
<style type="text/css">
8+
#canvas {
9+
background: #fff;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<header>
15+
Example from <a href="http://amzn.com/1430236655?tag=html5anim-20"><em>Foundation HTML5 Animation with JavaScript</em></a>
16+
</header>
17+
<canvas id="canvas" width="400" height="400"></canvas>
18+
19+
<script type="module">
20+
import Ball from './classes/ball.js'
21+
22+
window.onload = function () {
23+
const canvas = document.getElementById('canvas'),
24+
context = canvas.getContext('2d'),
25+
{width, height} = canvas,
26+
centerPointY = height / 2,
27+
ballNum = 15,
28+
balls = [],
29+
bouncing = -1.0;
30+
31+
for (let i = 0; i < ballNum; i++) {
32+
let radius = Math.random() * 20 + 15;
33+
let ball = new Ball(radius, Math.random() * 0xffffff);
34+
ball.mass = radius;
35+
ball.x = Math.random() * width;
36+
ball.y = Math.random() * height;
37+
ball.vx = Math.random() * 10 -5;
38+
ball.vy = Math.random() * 10 -5;
39+
balls.push(ball);
40+
}
41+
42+
function checkBoundary(ball) {
43+
const {x, y, radius} = ball;
44+
45+
if (x - radius < 0) {
46+
ball.x = radius;
47+
ball.vx *= bouncing;
48+
} else if (x + radius > width) {
49+
ball.x = width - radius;
50+
ball.vx *= bouncing;
51+
}
52+
53+
if (y - radius < 0) {
54+
ball.y = radius;
55+
ball.vy *= bouncing;
56+
} else if (y + radius > height) {
57+
ball.y = height - radius;
58+
ball.vy *= bouncing;
59+
}
60+
}
61+
62+
function rotate(x, y, sin, cos, reverse = false) {
63+
return {
64+
x: reverse ? (x * cos + y * sin) : (x * cos - y * sin),
65+
y: reverse ? (y * cos - x * sin) : (y * cos + x * sin)
66+
}
67+
}
68+
69+
function hitDetection(ballA, ballB) {
70+
const minDist = ballA.radius + ballB.radius;
71+
72+
// ballB 相对于 ballA旋转
73+
const dx = ballB.x - ballA.x,
74+
dy = ballB.y - ballA.y,
75+
dist = Math.sqrt(dx * dx + dy * dy);
76+
77+
if (dist >= minDist) {
78+
return;
79+
}
80+
81+
let angle = Math.atan2(dy, dx),
82+
sin = Math.sin(angle),
83+
cos = Math.cos(angle);
84+
85+
// rotate
86+
let posA = {x: 0, y: 0},
87+
posB = rotate(dx, dy, sin, cos, true),
88+
vA = rotate(ballA.vx, ballA.vy, sin, cos, true),
89+
vB = rotate(ballB.vx, ballB.vy, sin, cos, true),
90+
vxTotal = vA.x - vB.x;
91+
92+
93+
// hit
94+
vA.x = ((ballA.mass - ballB.mass) * vA.x + 2 * ballB.mass * vB.x) /
95+
(ballA.mass + ballB.mass);
96+
vB.x = vxTotal + vA.x;
97+
98+
let absV = Math.abs(vA.x) + Math.abs(vB.x),
99+
overlap = minDist - Math.abs(posA.x - posB.x);
100+
101+
// update position - to avoid objects becoming stuck together
102+
posA.x += vA.x / absV * overlap;
103+
posB.x += vB.x / absV * overlap;
104+
105+
// rotate position back
106+
let posAFinal = rotate(posA.x, posA.y, sin, cos, false),
107+
posBFinal = rotate(posB.x, posB.y, sin, cos, false);
108+
109+
// B先于A计算,避免A的值改动导致B计算不正确
110+
ballB.x = ballA.x + posBFinal.x;
111+
ballB.y = ballA.y + posBFinal.y;
112+
ballA.x = ballA.x + posAFinal.x;
113+
ballA.y = ballA.y + posAFinal.y;
114+
115+
// rotate velocities back
116+
let vAFinal = rotate(vA.x, vA.y, sin, cos, false),
117+
vBFinal = rotate(vB.x, vB.y, sin, cos, false);
118+
119+
ballA.vx = vAFinal.x;
120+
ballA.vy = vAFinal.y;
121+
ballB.vx = vBFinal.x;
122+
ballB.vy = vBFinal.y;
123+
}
124+
125+
function move(ball) {
126+
ball.x += ball.vx;
127+
ball.y += ball.vy;
128+
checkBoundary(ball);
129+
}
130+
131+
(function draw(){
132+
window.requestAnimationFrame(draw);
133+
context.clearRect(0, 0, width, height);
134+
135+
balls.forEach(ball => move(ball))
136+
137+
for (let i = 0, len = ballNum - 1; i < len; i++) {
138+
for(let j = i + 1; j < ballNum; j++) {
139+
let ballA = balls[i],
140+
ballB = balls[j];
141+
142+
hitDetection(ballA, ballB);
143+
}
144+
}
145+
146+
balls.forEach(ball => {
147+
ball.draw(context);
148+
})
149+
}())
150+
}
151+
</script>
152+
</body>
153+
</html>

0 commit comments

Comments
 (0)