diff --git a/custom/README.md b/custom/README.md
new file mode 100644
index 0000000..fb3bf83
--- /dev/null
+++ b/custom/README.md
@@ -0,0 +1 @@
+尝试以ES6的方式重写所有的例子
\ No newline at end of file
diff --git a/custom/examples/ch02/04-mouse-position.html b/custom/examples/ch02/04-mouse-position.html
new file mode 100644
index 0000000..ce6aa5a
--- /dev/null
+++ b/custom/examples/ch02/04-mouse-position.html
@@ -0,0 +1,28 @@
+
+
+
+
+ Mouse Position
+
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch02/08-key-names.html b/custom/examples/ch02/08-key-names.html
new file mode 100644
index 0000000..088fc5a
--- /dev/null
+++ b/custom/examples/ch02/08-key-names.html
@@ -0,0 +1,41 @@
+
+
+
+
+ Key Names
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/01-rotate-to-mouse.html b/custom/examples/ch03/01-rotate-to-mouse.html
new file mode 100644
index 0000000..4ec0b44
--- /dev/null
+++ b/custom/examples/ch03/01-rotate-to-mouse.html
@@ -0,0 +1,39 @@
+
+
+
+
+ Rotate to Mouse
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/02-bobbing-1.html b/custom/examples/ch03/02-bobbing-1.html
new file mode 100644
index 0000000..3539304
--- /dev/null
+++ b/custom/examples/ch03/02-bobbing-1.html
@@ -0,0 +1,42 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/04-wave-1.html b/custom/examples/ch03/04-wave-1.html
new file mode 100644
index 0000000..47e5134
--- /dev/null
+++ b/custom/examples/ch03/04-wave-1.html
@@ -0,0 +1,44 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/05-pulse.html b/custom/examples/ch03/05-pulse.html
new file mode 100644
index 0000000..4a427db
--- /dev/null
+++ b/custom/examples/ch03/05-pulse.html
@@ -0,0 +1,42 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/06-random.html b/custom/examples/ch03/06-random.html
new file mode 100644
index 0000000..d0ecca9
--- /dev/null
+++ b/custom/examples/ch03/06-random.html
@@ -0,0 +1,47 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/07-wave-2.html b/custom/examples/ch03/07-wave-2.html
new file mode 100644
index 0000000..3914d65
--- /dev/null
+++ b/custom/examples/ch03/07-wave-2.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/08-circle.html b/custom/examples/ch03/08-circle.html
new file mode 100644
index 0000000..ce404ea
--- /dev/null
+++ b/custom/examples/ch03/08-circle.html
@@ -0,0 +1,41 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/09-oval.html b/custom/examples/ch03/09-oval.html
new file mode 100644
index 0000000..94cc4b2
--- /dev/null
+++ b/custom/examples/ch03/09-oval.html
@@ -0,0 +1,42 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/10-distance.html b/custom/examples/ch03/10-distance.html
new file mode 100644
index 0000000..65f76c6
--- /dev/null
+++ b/custom/examples/ch03/10-distance.html
@@ -0,0 +1,45 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/11-mouse-distance.html b/custom/examples/ch03/11-mouse-distance.html
new file mode 100644
index 0000000..51c29f0
--- /dev/null
+++ b/custom/examples/ch03/11-mouse-distance.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch03/classes/arrow.js b/custom/examples/ch03/classes/arrow.js
new file mode 100644
index 0000000..9a18cbc
--- /dev/null
+++ b/custom/examples/ch03/classes/arrow.js
@@ -0,0 +1,30 @@
+export default class Arrow {
+ constructor() {
+ this.x = 0;
+ this.y = 0;
+ this.color = "#ffff00";
+ this.rotation = 0;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+
+ context.lineWidth = 2;
+ context.fillStyle = this.color;
+ context.beginPath();
+ context.moveTo(-50, -25);
+ context.lineTo(0, -25);
+ context.lineTo(0, -50);
+ context.lineTo(50, 0);
+ context.lineTo(0, 50);
+ context.lineTo(0, 25);
+ context.lineTo(-50, 25);
+ context.lineTo(-50, -25);
+ context.closePath();
+ context.fill();
+ context.stroke();
+
+ context.restore();
+ }
+}
diff --git a/custom/examples/ch03/classes/ball.js b/custom/examples/ch03/classes/ball.js
new file mode 100644
index 0000000..385d17f
--- /dev/null
+++ b/custom/examples/ch03/classes/ball.js
@@ -0,0 +1,34 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+}
diff --git a/custom/examples/ch04/01-drawing-app.html b/custom/examples/ch04/01-drawing-app.html
new file mode 100644
index 0000000..3f2691b
--- /dev/null
+++ b/custom/examples/ch04/01-drawing-app.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch04/02-drawing-curves.html b/custom/examples/ch04/02-drawing-curves.html
new file mode 100644
index 0000000..5768b49
--- /dev/null
+++ b/custom/examples/ch04/02-drawing-curves.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch04/03-curve-through-point.html b/custom/examples/ch04/03-curve-through-point.html
new file mode 100644
index 0000000..24e6213
--- /dev/null
+++ b/custom/examples/ch04/03-curve-through-point.html
@@ -0,0 +1,45 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch04/04-multi-curve-1.html b/custom/examples/ch04/04-multi-curve-1.html
new file mode 100644
index 0000000..65f9450
--- /dev/null
+++ b/custom/examples/ch04/04-multi-curve-1.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch04/05-multi-curve-2.html b/custom/examples/ch04/05-multi-curve-2.html
new file mode 100644
index 0000000..b904d47
--- /dev/null
+++ b/custom/examples/ch04/05-multi-curve-2.html
@@ -0,0 +1,59 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch04/06-multi-curve-3.html b/custom/examples/ch04/06-multi-curve-3.html
new file mode 100644
index 0000000..df15e33
--- /dev/null
+++ b/custom/examples/ch04/06-multi-curve-3.html
@@ -0,0 +1,60 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch04/07-gradient-fill-1.html b/custom/examples/ch04/07-gradient-fill-1.html
new file mode 100644
index 0000000..1418a3c
--- /dev/null
+++ b/custom/examples/ch04/07-gradient-fill-1.html
@@ -0,0 +1,28 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/08-gradient-fill-2.html b/custom/examples/ch04/08-gradient-fill-2.html
new file mode 100644
index 0000000..40809b9
--- /dev/null
+++ b/custom/examples/ch04/08-gradient-fill-2.html
@@ -0,0 +1,29 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/09-gradient-fill-radial.html b/custom/examples/ch04/09-gradient-fill-radial.html
new file mode 100644
index 0000000..953425b
--- /dev/null
+++ b/custom/examples/ch04/09-gradient-fill-radial.html
@@ -0,0 +1,49 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/10-load-image.html b/custom/examples/ch04/10-load-image.html
new file mode 100644
index 0000000..21357d4
--- /dev/null
+++ b/custom/examples/ch04/10-load-image.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/11-embed-image.html b/custom/examples/ch04/11-embed-image.html
new file mode 100644
index 0000000..c41068c
--- /dev/null
+++ b/custom/examples/ch04/11-embed-image.html
@@ -0,0 +1,30 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/12-video-frames.html b/custom/examples/ch04/12-video-frames.html
new file mode 100644
index 0000000..e95ad35
--- /dev/null
+++ b/custom/examples/ch04/12-video-frames.html
@@ -0,0 +1,39 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/13-invert-color.html b/custom/examples/ch04/13-invert-color.html
new file mode 100644
index 0000000..321e68a
--- /dev/null
+++ b/custom/examples/ch04/13-invert-color.html
@@ -0,0 +1,39 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/14-grayscale.html b/custom/examples/ch04/14-grayscale.html
new file mode 100644
index 0000000..66db1a4
--- /dev/null
+++ b/custom/examples/ch04/14-grayscale.html
@@ -0,0 +1,41 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/15-pixel-move.html b/custom/examples/ch04/15-pixel-move.html
new file mode 100644
index 0000000..c0e2d02
--- /dev/null
+++ b/custom/examples/ch04/15-pixel-move.html
@@ -0,0 +1,57 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/16-spray-paint.html b/custom/examples/ch04/16-spray-paint.html
new file mode 100644
index 0000000..27ba8d6
--- /dev/null
+++ b/custom/examples/ch04/16-spray-paint.html
@@ -0,0 +1,58 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch04/assets/movieclip.mp4 b/custom/examples/ch04/assets/movieclip.mp4
new file mode 100644
index 0000000..81d11df
Binary files /dev/null and b/custom/examples/ch04/assets/movieclip.mp4 differ
diff --git a/custom/examples/ch04/assets/movieclip.ogv b/custom/examples/ch04/assets/movieclip.ogv
new file mode 100644
index 0000000..e128d82
Binary files /dev/null and b/custom/examples/ch04/assets/movieclip.ogv differ
diff --git a/custom/examples/ch04/assets/movieclip.webm b/custom/examples/ch04/assets/movieclip.webm
new file mode 100644
index 0000000..42e2130
Binary files /dev/null and b/custom/examples/ch04/assets/movieclip.webm differ
diff --git a/custom/examples/ch04/assets/picture.jpg b/custom/examples/ch04/assets/picture.jpg
new file mode 100644
index 0000000..4d3b9a2
Binary files /dev/null and b/custom/examples/ch04/assets/picture.jpg differ
diff --git a/custom/examples/ch05/01-velocity-1.html b/custom/examples/ch05/01-velocity-1.html
new file mode 100644
index 0000000..84caf7f
--- /dev/null
+++ b/custom/examples/ch05/01-velocity-1.html
@@ -0,0 +1,36 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/02-velocity-2.html b/custom/examples/ch05/02-velocity-2.html
new file mode 100644
index 0000000..53fdfa2
--- /dev/null
+++ b/custom/examples/ch05/02-velocity-2.html
@@ -0,0 +1,38 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/03-velocity-angle.html b/custom/examples/ch05/03-velocity-angle.html
new file mode 100644
index 0000000..f19a869
--- /dev/null
+++ b/custom/examples/ch05/03-velocity-angle.html
@@ -0,0 +1,40 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/04-follow-mouse-1.html b/custom/examples/ch05/04-follow-mouse-1.html
new file mode 100644
index 0000000..f506e39
--- /dev/null
+++ b/custom/examples/ch05/04-follow-mouse-1.html
@@ -0,0 +1,44 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/05-rotational-velocity.html b/custom/examples/ch05/05-rotational-velocity.html
new file mode 100644
index 0000000..72f805b
--- /dev/null
+++ b/custom/examples/ch05/05-rotational-velocity.html
@@ -0,0 +1,37 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/06-acceleration-1.html b/custom/examples/ch05/06-acceleration-1.html
new file mode 100644
index 0000000..7e65676
--- /dev/null
+++ b/custom/examples/ch05/06-acceleration-1.html
@@ -0,0 +1,38 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/07-acceleration-2.html b/custom/examples/ch05/07-acceleration-2.html
new file mode 100644
index 0000000..0523a6d
--- /dev/null
+++ b/custom/examples/ch05/07-acceleration-2.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/08-acceleration-3.html b/custom/examples/ch05/08-acceleration-3.html
new file mode 100644
index 0000000..ac90ff5
--- /dev/null
+++ b/custom/examples/ch05/08-acceleration-3.html
@@ -0,0 +1,67 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/09-gravity.html b/custom/examples/ch05/09-gravity.html
new file mode 100644
index 0000000..0e2926a
--- /dev/null
+++ b/custom/examples/ch05/09-gravity.html
@@ -0,0 +1,72 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/10-follow-mouse-2.html b/custom/examples/ch05/10-follow-mouse-2.html
new file mode 100644
index 0000000..bbf6623
--- /dev/null
+++ b/custom/examples/ch05/10-follow-mouse-2.html
@@ -0,0 +1,49 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/11-ship-sim.html b/custom/examples/ch05/11-ship-sim.html
new file mode 100644
index 0000000..1c418a4
--- /dev/null
+++ b/custom/examples/ch05/11-ship-sim.html
@@ -0,0 +1,79 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch05/classes/arrow.js b/custom/examples/ch05/classes/arrow.js
new file mode 100644
index 0000000..9a18cbc
--- /dev/null
+++ b/custom/examples/ch05/classes/arrow.js
@@ -0,0 +1,30 @@
+export default class Arrow {
+ constructor() {
+ this.x = 0;
+ this.y = 0;
+ this.color = "#ffff00";
+ this.rotation = 0;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+
+ context.lineWidth = 2;
+ context.fillStyle = this.color;
+ context.beginPath();
+ context.moveTo(-50, -25);
+ context.lineTo(0, -25);
+ context.lineTo(0, -50);
+ context.lineTo(50, 0);
+ context.lineTo(0, 50);
+ context.lineTo(0, 25);
+ context.lineTo(-50, 25);
+ context.lineTo(-50, -25);
+ context.closePath();
+ context.fill();
+ context.stroke();
+
+ context.restore();
+ }
+}
diff --git a/custom/examples/ch05/classes/ball.js b/custom/examples/ch05/classes/ball.js
new file mode 100644
index 0000000..385d17f
--- /dev/null
+++ b/custom/examples/ch05/classes/ball.js
@@ -0,0 +1,34 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+}
diff --git a/custom/examples/ch05/classes/ship.js b/custom/examples/ch05/classes/ship.js
new file mode 100644
index 0000000..9aae67d
--- /dev/null
+++ b/custom/examples/ch05/classes/ship.js
@@ -0,0 +1,34 @@
+export default class Ship {
+ constructor() {
+ this.x = 0;
+ this.y = 0;
+ this.width = 25;
+ this.height = 20;
+ this.rotation = 0;
+ this.showFlame = false;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+
+ context.lineWidth = 1;
+ context.strokeStyle = "#ffffff";
+ context.beginPath();
+ context.moveTo(10, 0);
+ context.lineTo(-10, 10);
+ context.lineTo(-5, 0);
+ context.lineTo(-10, -10);
+ context.lineTo(10, 0);
+ context.stroke();
+
+ if (this.showFlame) {
+ context.beginPath();
+ context.moveTo(-7.5, -5);
+ context.lineTo(-15, 0);
+ context.lineTo(-7.5, 5);
+ context.stroke();
+ }
+ context.restore();
+ }
+}
\ No newline at end of file
diff --git a/custom/examples/ch06/01-removal.html b/custom/examples/ch06/01-removal.html
new file mode 100644
index 0000000..b317254
--- /dev/null
+++ b/custom/examples/ch06/01-removal.html
@@ -0,0 +1,90 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/02-fountain.html b/custom/examples/ch06/02-fountain.html
new file mode 100644
index 0000000..865d240
--- /dev/null
+++ b/custom/examples/ch06/02-fountain.html
@@ -0,0 +1,95 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/03-ship-sim-2.html b/custom/examples/ch06/03-ship-sim-2.html
new file mode 100644
index 0000000..3bea9f5
--- /dev/null
+++ b/custom/examples/ch06/03-ship-sim-2.html
@@ -0,0 +1,102 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/04-bouncing-1.html b/custom/examples/ch06/04-bouncing-1.html
new file mode 100644
index 0000000..cdd0008
--- /dev/null
+++ b/custom/examples/ch06/04-bouncing-1.html
@@ -0,0 +1,72 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/05-bouncing-2.html b/custom/examples/ch06/05-bouncing-2.html
new file mode 100644
index 0000000..15c2a09
--- /dev/null
+++ b/custom/examples/ch06/05-bouncing-2.html
@@ -0,0 +1,85 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/06-friction-1.html b/custom/examples/ch06/06-friction-1.html
new file mode 100644
index 0000000..be8a694
--- /dev/null
+++ b/custom/examples/ch06/06-friction-1.html
@@ -0,0 +1,65 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/07-friction-2.html b/custom/examples/ch06/07-friction-2.html
new file mode 100644
index 0000000..2119956
--- /dev/null
+++ b/custom/examples/ch06/07-friction-2.html
@@ -0,0 +1,55 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/08-ship-sim-friction.html b/custom/examples/ch06/08-ship-sim-friction.html
new file mode 100644
index 0000000..3050ed8
--- /dev/null
+++ b/custom/examples/ch06/08-ship-sim-friction.html
@@ -0,0 +1,115 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch06/classes/ball.js b/custom/examples/ch06/classes/ball.js
new file mode 100644
index 0000000..bf970d3
--- /dev/null
+++ b/custom/examples/ch06/classes/ball.js
@@ -0,0 +1,36 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+}
diff --git a/custom/examples/ch06/classes/ship.js b/custom/examples/ch06/classes/ship.js
new file mode 100644
index 0000000..9aae67d
--- /dev/null
+++ b/custom/examples/ch06/classes/ship.js
@@ -0,0 +1,34 @@
+export default class Ship {
+ constructor() {
+ this.x = 0;
+ this.y = 0;
+ this.width = 25;
+ this.height = 20;
+ this.rotation = 0;
+ this.showFlame = false;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+
+ context.lineWidth = 1;
+ context.strokeStyle = "#ffffff";
+ context.beginPath();
+ context.moveTo(10, 0);
+ context.lineTo(-10, 10);
+ context.lineTo(-5, 0);
+ context.lineTo(-10, -10);
+ context.lineTo(10, 0);
+ context.stroke();
+
+ if (this.showFlame) {
+ context.beginPath();
+ context.moveTo(-7.5, -5);
+ context.lineTo(-15, 0);
+ context.lineTo(-7.5, 5);
+ context.stroke();
+ }
+ context.restore();
+ }
+}
\ No newline at end of file
diff --git a/custom/examples/ch07/01-mouse-events.html b/custom/examples/ch07/01-mouse-events.html
new file mode 100644
index 0000000..5ca1aa1
--- /dev/null
+++ b/custom/examples/ch07/01-mouse-events.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch07/02-touch-events.html b/custom/examples/ch07/02-touch-events.html
new file mode 100644
index 0000000..9b70ab1
--- /dev/null
+++ b/custom/examples/ch07/02-touch-events.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch07/03-mouse-move-drag.html b/custom/examples/ch07/03-mouse-move-drag.html
new file mode 100644
index 0000000..4fea8b1
--- /dev/null
+++ b/custom/examples/ch07/03-mouse-move-drag.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch07/04-drag-and-move-1.html b/custom/examples/ch07/04-drag-and-move-1.html
new file mode 100644
index 0000000..eecc73b
--- /dev/null
+++ b/custom/examples/ch07/04-drag-and-move-1.html
@@ -0,0 +1,103 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch07/06-throwing.html b/custom/examples/ch07/06-throwing.html
new file mode 100644
index 0000000..48d0b85
--- /dev/null
+++ b/custom/examples/ch07/06-throwing.html
@@ -0,0 +1,117 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch07/classes/ball.js b/custom/examples/ch07/classes/ball.js
new file mode 100644
index 0000000..2f8f3a2
--- /dev/null
+++ b/custom/examples/ch07/classes/ball.js
@@ -0,0 +1,44 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+ getBounds() {
+ return {
+ x: this.x - this.radius,
+ y: this.y - this.radius,
+ width: this.radius * 2,
+ height: this.radius * 2
+ };
+ }
+}
diff --git a/custom/examples/ch08/01-easing-1.html b/custom/examples/ch08/01-easing-1.html
new file mode 100644
index 0000000..3caa1ac
--- /dev/null
+++ b/custom/examples/ch08/01-easing-1.html
@@ -0,0 +1,44 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/02-easing-2.html b/custom/examples/ch08/02-easing-2.html
new file mode 100644
index 0000000..d25a73d
--- /dev/null
+++ b/custom/examples/ch08/02-easing-2.html
@@ -0,0 +1,61 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/03-easing-off.html b/custom/examples/ch08/03-easing-off.html
new file mode 100644
index 0000000..6f795aa
--- /dev/null
+++ b/custom/examples/ch08/03-easing-off.html
@@ -0,0 +1,56 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/04-ease-to-mouse.html b/custom/examples/ch08/04-ease-to-mouse.html
new file mode 100644
index 0000000..263c57f
--- /dev/null
+++ b/custom/examples/ch08/04-ease-to-mouse.html
@@ -0,0 +1,41 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/05-spring-1.html b/custom/examples/ch08/05-spring-1.html
new file mode 100644
index 0000000..6eacb11
--- /dev/null
+++ b/custom/examples/ch08/05-spring-1.html
@@ -0,0 +1,50 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/06-spring-2.html b/custom/examples/ch08/06-spring-2.html
new file mode 100644
index 0000000..78e9fb0
--- /dev/null
+++ b/custom/examples/ch08/06-spring-2.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/07-spring-3.html b/custom/examples/ch08/07-spring-3.html
new file mode 100644
index 0000000..e3e23eb
--- /dev/null
+++ b/custom/examples/ch08/07-spring-3.html
@@ -0,0 +1,50 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/08-spring-4.html b/custom/examples/ch08/08-spring-4.html
new file mode 100644
index 0000000..8888ab2
--- /dev/null
+++ b/custom/examples/ch08/08-spring-4.html
@@ -0,0 +1,47 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/09-spring-5.html b/custom/examples/ch08/09-spring-5.html
new file mode 100644
index 0000000..8520edb
--- /dev/null
+++ b/custom/examples/ch08/09-spring-5.html
@@ -0,0 +1,59 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/10-chain.html b/custom/examples/ch08/10-chain.html
new file mode 100644
index 0000000..e4dccbd
--- /dev/null
+++ b/custom/examples/ch08/10-chain.html
@@ -0,0 +1,69 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/11-chain-array.html b/custom/examples/ch08/11-chain-array.html
new file mode 100644
index 0000000..421f7db
--- /dev/null
+++ b/custom/examples/ch08/11-chain-array.html
@@ -0,0 +1,75 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/12-multi-spring.html b/custom/examples/ch08/12-multi-spring.html
new file mode 100644
index 0000000..b456073
--- /dev/null
+++ b/custom/examples/ch08/12-multi-spring.html
@@ -0,0 +1,98 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/13-offset-spring.html b/custom/examples/ch08/13-offset-spring.html
new file mode 100644
index 0000000..f934683
--- /dev/null
+++ b/custom/examples/ch08/13-offset-spring.html
@@ -0,0 +1,67 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/14-double-spring.html b/custom/examples/ch08/14-double-spring.html
new file mode 100644
index 0000000..1e450a2
--- /dev/null
+++ b/custom/examples/ch08/14-double-spring.html
@@ -0,0 +1,114 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/15-triple-spring.html b/custom/examples/ch08/15-triple-spring.html
new file mode 100644
index 0000000..c231a83
--- /dev/null
+++ b/custom/examples/ch08/15-triple-spring.html
@@ -0,0 +1,161 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/16-multi-spring.html b/custom/examples/ch08/16-multi-spring.html
new file mode 100644
index 0000000..1aa6b01
--- /dev/null
+++ b/custom/examples/ch08/16-multi-spring.html
@@ -0,0 +1,220 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/custom/examples/ch08/classes/ball-with-text.js b/custom/examples/ch08/classes/ball-with-text.js
new file mode 100644
index 0000000..12d4696
--- /dev/null
+++ b/custom/examples/ch08/classes/ball-with-text.js
@@ -0,0 +1,52 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ this.text = '';
+ this.angle = 0;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.text) {
+ context.font = "24px serif";
+ context.fillStyle = '#000000';
+ context.fillText(this.text, -6, 6);
+ // context.fillText(this.angle.toFixed(4), this.radius, this.radius)
+ }
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+ getBounds() {
+ return {
+ x: this.x - this.radius,
+ y: this.y - this.radius,
+ width: this.radius * 2,
+ height: this.radius * 2
+ };
+ }
+}
diff --git a/custom/examples/ch08/classes/ball.js b/custom/examples/ch08/classes/ball.js
new file mode 100644
index 0000000..2f8f3a2
--- /dev/null
+++ b/custom/examples/ch08/classes/ball.js
@@ -0,0 +1,44 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+ getBounds() {
+ return {
+ x: this.x - this.radius,
+ y: this.y - this.radius,
+ width: this.radius * 2,
+ height: this.radius * 2
+ };
+ }
+}
diff --git a/custom/examples/ch09/01-object-hit-test.html b/custom/examples/ch09/01-object-hit-test.html
new file mode 100644
index 0000000..4024fa2
--- /dev/null
+++ b/custom/examples/ch09/01-object-hit-test.html
@@ -0,0 +1,64 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/02-boxes.html b/custom/examples/ch09/02-boxes.html
new file mode 100644
index 0000000..6ef3e1d
--- /dev/null
+++ b/custom/examples/ch09/02-boxes.html
@@ -0,0 +1,102 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/03-point-hit-test.html b/custom/examples/ch09/03-point-hit-test.html
new file mode 100644
index 0000000..7878c82
--- /dev/null
+++ b/custom/examples/ch09/03-point-hit-test.html
@@ -0,0 +1,50 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/04-distance-1.html b/custom/examples/ch09/04-distance-1.html
new file mode 100644
index 0000000..711a36c
--- /dev/null
+++ b/custom/examples/ch09/04-distance-1.html
@@ -0,0 +1,54 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/05-distance-2.html b/custom/examples/ch09/05-distance-2.html
new file mode 100644
index 0000000..3425d86
--- /dev/null
+++ b/custom/examples/ch09/05-distance-2.html
@@ -0,0 +1,54 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/06-bubbles-1.html b/custom/examples/ch09/06-bubbles-1.html
new file mode 100644
index 0000000..0d86c87
--- /dev/null
+++ b/custom/examples/ch09/06-bubbles-1.html
@@ -0,0 +1,103 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/07-bubbles-2.html b/custom/examples/ch09/07-bubbles-2.html
new file mode 100644
index 0000000..ba43b14
--- /dev/null
+++ b/custom/examples/ch09/07-bubbles-2.html
@@ -0,0 +1,112 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/08-bubbles-3.html b/custom/examples/ch09/08-bubbles-3.html
new file mode 100644
index 0000000..a3a474c
--- /dev/null
+++ b/custom/examples/ch09/08-bubbles-3.html
@@ -0,0 +1,110 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch09/classes/ball.js b/custom/examples/ch09/classes/ball.js
new file mode 100644
index 0000000..2f8f3a2
--- /dev/null
+++ b/custom/examples/ch09/classes/ball.js
@@ -0,0 +1,44 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+ getBounds() {
+ return {
+ x: this.x - this.radius,
+ y: this.y - this.radius,
+ width: this.radius * 2,
+ height: this.radius * 2
+ };
+ }
+}
diff --git a/custom/examples/ch09/classes/box.js b/custom/examples/ch09/classes/box.js
new file mode 100644
index 0000000..f8331ef
--- /dev/null
+++ b/custom/examples/ch09/classes/box.js
@@ -0,0 +1,35 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Box {
+ constructor(width = 50, height = 50, color = '#ff0000') {
+ this.x = 0;
+ this.x = 0;
+ this.y = 0;
+ this.width = width;
+ this.height = height;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ context.rect(0, 0, this.width, this.height);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+}
\ No newline at end of file
diff --git a/custom/examples/ch10/01-rotate-1.html b/custom/examples/ch10/01-rotate-1.html
new file mode 100644
index 0000000..1605151
--- /dev/null
+++ b/custom/examples/ch10/01-rotate-1.html
@@ -0,0 +1,46 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/02-rotate-2.html b/custom/examples/ch10/02-rotate-2.html
new file mode 100644
index 0000000..7c9d87c
--- /dev/null
+++ b/custom/examples/ch10/02-rotate-2.html
@@ -0,0 +1,54 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/03-rotate-3.html b/custom/examples/ch10/03-rotate-3.html
new file mode 100644
index 0000000..0da5a3d
--- /dev/null
+++ b/custom/examples/ch10/03-rotate-3.html
@@ -0,0 +1,63 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/04-angle-bounce.html b/custom/examples/ch10/04-angle-bounce.html
new file mode 100644
index 0000000..92bd855
--- /dev/null
+++ b/custom/examples/ch10/04-angle-bounce.html
@@ -0,0 +1,91 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/05-angle-bounce-opt.html b/custom/examples/ch10/05-angle-bounce-opt.html
new file mode 100644
index 0000000..9827422
--- /dev/null
+++ b/custom/examples/ch10/05-angle-bounce-opt.html
@@ -0,0 +1,97 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/06-angle-bounce-rotate.html b/custom/examples/ch10/06-angle-bounce-rotate.html
new file mode 100644
index 0000000..265afc4
--- /dev/null
+++ b/custom/examples/ch10/06-angle-bounce-rotate.html
@@ -0,0 +1,106 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/08-angle-bounce-bounds.html b/custom/examples/ch10/08-angle-bounce-bounds.html
new file mode 100644
index 0000000..1e3a2b3
--- /dev/null
+++ b/custom/examples/ch10/08-angle-bounce-bounds.html
@@ -0,0 +1,109 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/09-angle-bounce-final.html b/custom/examples/ch10/09-angle-bounce-final.html
new file mode 100644
index 0000000..a98d101
--- /dev/null
+++ b/custom/examples/ch10/09-angle-bounce-final.html
@@ -0,0 +1,123 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/10-multi-angle-bounce.html b/custom/examples/ch10/10-multi-angle-bounce.html
new file mode 100644
index 0000000..2882aae
--- /dev/null
+++ b/custom/examples/ch10/10-multi-angle-bounce.html
@@ -0,0 +1,142 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch10/classes/ball.js b/custom/examples/ch10/classes/ball.js
new file mode 100644
index 0000000..2f8f3a2
--- /dev/null
+++ b/custom/examples/ch10/classes/ball.js
@@ -0,0 +1,44 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+ getBounds() {
+ return {
+ x: this.x - this.radius,
+ y: this.y - this.radius,
+ width: this.radius * 2,
+ height: this.radius * 2
+ };
+ }
+}
diff --git a/custom/examples/ch10/classes/line.js b/custom/examples/ch10/classes/line.js
new file mode 100644
index 0000000..e463ab5
--- /dev/null
+++ b/custom/examples/ch10/classes/line.js
@@ -0,0 +1,55 @@
+export default class Line {
+ constructor(x1, y1, x2, y2) {
+ this.x = 0;
+ this.y = 0;
+ this.x1 = (x1 === undefined) ? 0 : x1;
+ this.y1 = (y1 === undefined) ? 0 : y1;
+ this.x2 = (x2 === undefined) ? 0 : x2;
+ this.y2 = (y2 === undefined) ? 0 : y2;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.beginPath();
+ context.moveTo(this.x1, this.y1);
+ context.lineTo(this.x2, this.y2);
+ context.closePath();
+ context.stroke();
+ context.restore();
+ }
+ getBounds() {
+ if (this.rotation === 0) {
+ var minX = Math.min(this.x1, this.x2),
+ minY = Math.min(this.y1, this.y2),
+ maxX = Math.max(this.x1, this.x2),
+ maxY = Math.max(this.y1, this.y2);
+ return {
+ x: this.x + minX,
+ y: this.y + minY,
+ width: maxX - minX,
+ height: maxY - minY
+ }
+ } else {
+ var sin = Math.sin(this.rotation),
+ cos = Math.cos(this.rotation),
+ x1r = cos * this.x1 + sin * this.y1,
+ x2r = cos * this.x2 + sin * this.y2,
+ y1r = cos * this.y1 + sin * this.x1,
+ y2r = cos * this.y2 + sin * this.x2;
+ return {
+ x: this.x + Math.min(x1r, x2r),
+ y: this.y + Math.min(y1r, y2r),
+ width: Math.max(x1r, x2r) - Math.min(x1r, x2r),
+ height: Math.max(y1r, y2r) - Math.min(y1r, y2r)
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/custom/examples/ch11/01-billiard-1.html b/custom/examples/ch11/01-billiard-1.html
new file mode 100644
index 0000000..38c86d5
--- /dev/null
+++ b/custom/examples/ch11/01-billiard-1.html
@@ -0,0 +1,72 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch11/02-billiard-2.html b/custom/examples/ch11/02-billiard-2.html
new file mode 100644
index 0000000..ddfebe9
--- /dev/null
+++ b/custom/examples/ch11/02-billiard-2.html
@@ -0,0 +1,75 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch11/03-billiard-3.html b/custom/examples/ch11/03-billiard-3.html
new file mode 100644
index 0000000..427e23e
--- /dev/null
+++ b/custom/examples/ch11/03-billiard-3.html
@@ -0,0 +1,154 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch11/04-billiard-4.html b/custom/examples/ch11/04-billiard-4.html
new file mode 100644
index 0000000..b05025d
--- /dev/null
+++ b/custom/examples/ch11/04-billiard-4.html
@@ -0,0 +1,168 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch11/05-multi-billiard-1.html b/custom/examples/ch11/05-multi-billiard-1.html
new file mode 100644
index 0000000..6ee99a5
--- /dev/null
+++ b/custom/examples/ch11/05-multi-billiard-1.html
@@ -0,0 +1,174 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch11/06-multi-billiard-2.html b/custom/examples/ch11/06-multi-billiard-2.html
new file mode 100644
index 0000000..f509a52
--- /dev/null
+++ b/custom/examples/ch11/06-multi-billiard-2.html
@@ -0,0 +1,153 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch11/classes/ball.js b/custom/examples/ch11/classes/ball.js
new file mode 100644
index 0000000..f97c875
--- /dev/null
+++ b/custom/examples/ch11/classes/ball.js
@@ -0,0 +1,45 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.mass = 1;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+ getBounds() {
+ return {
+ x: this.x - this.radius,
+ y: this.y - this.radius,
+ width: this.radius * 2,
+ height: this.radius * 2
+ };
+ }
+}
diff --git a/custom/examples/ch12/01-gravity.html b/custom/examples/ch12/01-gravity.html
new file mode 100644
index 0000000..f440103
--- /dev/null
+++ b/custom/examples/ch12/01-gravity.html
@@ -0,0 +1,76 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/02-gravity-bounce.html b/custom/examples/ch12/02-gravity-bounce.html
new file mode 100644
index 0000000..a909cb8
--- /dev/null
+++ b/custom/examples/ch12/02-gravity-bounce.html
@@ -0,0 +1,140 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/03-gravity-random.html b/custom/examples/ch12/03-gravity-random.html
new file mode 100644
index 0000000..4d5b5b0
--- /dev/null
+++ b/custom/examples/ch12/03-gravity-random.html
@@ -0,0 +1,141 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/04-orbit.html b/custom/examples/ch12/04-orbit.html
new file mode 100644
index 0000000..8b05efd
--- /dev/null
+++ b/custom/examples/ch12/04-orbit.html
@@ -0,0 +1,145 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/05-orbit-draw.html b/custom/examples/ch12/05-orbit-draw.html
new file mode 100644
index 0000000..b6fc79e
--- /dev/null
+++ b/custom/examples/ch12/05-orbit-draw.html
@@ -0,0 +1,145 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/06-node-garden.html b/custom/examples/ch12/06-node-garden.html
new file mode 100644
index 0000000..112791a
--- /dev/null
+++ b/custom/examples/ch12/06-node-garden.html
@@ -0,0 +1,94 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/07-node-garden-lines.html b/custom/examples/ch12/07-node-garden-lines.html
new file mode 100644
index 0000000..02dc040
--- /dev/null
+++ b/custom/examples/ch12/07-node-garden-lines.html
@@ -0,0 +1,103 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/08-node-garden-mass.html b/custom/examples/ch12/08-node-garden-mass.html
new file mode 100644
index 0000000..ac6f871
--- /dev/null
+++ b/custom/examples/ch12/08-node-garden-mass.html
@@ -0,0 +1,104 @@
+
+
+
+
+ Bobbing 1
+
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch12/classes/ball.js b/custom/examples/ch12/classes/ball.js
new file mode 100644
index 0000000..f97c875
--- /dev/null
+++ b/custom/examples/ch12/classes/ball.js
@@ -0,0 +1,45 @@
+import {parseColor} from '../../include/utils.js';
+
+export default class Ball {
+ constructor(radius, color) {
+ if (radius === undefined) { radius = 40; }
+ if (color === undefined) { color = "#ff0000"; }
+ this.x = 0;
+ this.y = 0;
+ this.radius = radius;
+ this.vx = 0;
+ this.vy = 0;
+ this.mass = 1;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ //x, y, radius, start_angle, end_angle, anti-clockwise
+ context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ context.restore();
+ }
+ getBounds() {
+ return {
+ x: this.x - this.radius,
+ y: this.y - this.radius,
+ width: this.radius * 2,
+ height: this.radius * 2
+ };
+ }
+}
diff --git a/custom/examples/ch13/01-segment.html b/custom/examples/ch13/01-segment.html
new file mode 100644
index 0000000..50a4a58
--- /dev/null
+++ b/custom/examples/ch13/01-segment.html
@@ -0,0 +1,38 @@
+
+
+
+
+ Segment
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch13/02-single-segment.html b/custom/examples/ch13/02-single-segment.html
new file mode 100644
index 0000000..bcb343b
--- /dev/null
+++ b/custom/examples/ch13/02-single-segment.html
@@ -0,0 +1,47 @@
+
+
+
+
+ Segment
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch13/03-two-segments-1.html b/custom/examples/ch13/03-two-segments-1.html
new file mode 100644
index 0000000..24626ea
--- /dev/null
+++ b/custom/examples/ch13/03-two-segments-1.html
@@ -0,0 +1,60 @@
+
+
+
+
+ Segment
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch13/04-two-segments-2.html b/custom/examples/ch13/04-two-segments-2.html
new file mode 100644
index 0000000..df84c20
--- /dev/null
+++ b/custom/examples/ch13/04-two-segments-2.html
@@ -0,0 +1,60 @@
+
+
+
+
+ Segment
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch13/05-walking-1.html b/custom/examples/ch13/05-walking-1.html
new file mode 100644
index 0000000..914b70c
--- /dev/null
+++ b/custom/examples/ch13/05-walking-1.html
@@ -0,0 +1,50 @@
+
+
+
+
+ Segment
+
+
+
+
+
+
+
+
+
diff --git a/custom/examples/ch13/classes/segment.js b/custom/examples/ch13/classes/segment.js
new file mode 100644
index 0000000..b8224a9
--- /dev/null
+++ b/custom/examples/ch13/classes/segment.js
@@ -0,0 +1,59 @@
+export default class Segment {
+ constructor(width, height, color) {
+ this.x = 0;
+ this.y = 0;
+ this.width = width;
+ this.height = height;
+ this.vx = 0;
+ this.vy = 0;
+ this.rotation = 0;
+ this.scaleX = 1;
+ this.scaleY = 1;
+ this.color = (color === undefined) ? "#ffffff" : utils.parseColor(color);
+ this.lineWidth = 1;
+ }
+ draw(context) {
+ var h = this.height,
+ d = this.width + h, //top-right diagonal
+ cr = h / 2; //corner radius
+ context.save();
+ context.translate(this.x, this.y);
+ context.rotate(this.rotation);
+ context.scale(this.scaleX, this.scaleY);
+ context.lineWidth = this.lineWidth;
+ context.fillStyle = this.color;
+ context.beginPath();
+ context.moveTo(0, -cr);
+ context.lineTo(d - 2 * cr, -cr);
+ context.quadraticCurveTo(-cr + d, -cr, -cr + d, 0);
+ context.lineTo(-cr + d, h - 2 * cr);
+ context.quadraticCurveTo(-cr + d, -cr + h, d - 2 * cr, -cr + h);
+ context.lineTo(0, -cr + h);
+ context.quadraticCurveTo(-cr, -cr + h, -cr, h - 2 * cr);
+ context.lineTo(-cr, 0);
+ context.quadraticCurveTo(-cr, -cr, 0, -cr);
+ context.closePath();
+ context.fill();
+ if (this.lineWidth > 0) {
+ context.stroke();
+ }
+ //draw the 2 "pins"
+ context.beginPath();
+ context.arc(0, 0, 2, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.stroke();
+
+ context.beginPath();
+ context.arc(this.width, 0, 2, 0, (Math.PI * 2), true);
+ context.closePath();
+ context.stroke();
+
+ context.restore();
+ }
+ getPin() {
+ return {
+ x: this.x + Math.cos(this.rotation) * this.width,
+ y: this.y + Math.sin(this.rotation) * this.width
+ };
+ }
+}
diff --git a/custom/examples/ch13/classes/slider.js b/custom/examples/ch13/classes/slider.js
new file mode 100644
index 0000000..890be7a
--- /dev/null
+++ b/custom/examples/ch13/classes/slider.js
@@ -0,0 +1,97 @@
+import {captureMouse, rectContainsPoint} from '../../include/utils.js'
+
+export default class Slider {
+ constructor(min, max, value) {
+ this.min = (min === undefined) ? 0 : min;
+ this.max = (max === undefined) ? 100 : max;
+ this.value = (value === undefined) ? 100 : value;
+ this.onchange = null;
+
+ this.x = 0;
+ this.y = 0;
+ this.width = 16;
+ this.height = 100;
+
+ this.backColor = "#cccccc";
+ this.backBorderColor = "#999999";
+ this.backWidth = 4;
+ this.backX = this.width / 2 - this.backWidth / 2;
+
+ this.handleColor = "#eeeeee";
+ this.handleBorderColor = "#cccccc";
+ this.handleHeight = 6;
+ this.handleY = 0;
+
+ this.updatePosition();
+ }
+ draw(context) {
+ context.save();
+ context.translate(this.x, this.y);
+
+ //draw back
+ context.fillStyle = this.backColor;
+ context.beginPath();
+ context.fillRect(this.backX, 0, this.backWidth, this.height);
+ context.closePath();
+
+ //draw handle
+ context.strokeStyle = this.handleBorderColor;
+ context.fillStyle = this.handleColor;
+ context.beginPath();
+ context.rect(0, this.handleY, this.width, this.handleHeight);
+ context.closePath();
+ context.fill();
+ context.stroke();
+
+ context.restore();
+ }
+ updateValue() {
+ var old_value = this.value,
+ handleRange = this.height - this.handleHeight,
+ valueRange = this.max - this.min;
+
+ this.value = (handleRange - this.handleY) / handleRange * valueRange + this.min;
+ if (typeof this.onchange === 'function' && this.value !== old_value) {
+ this.onchange();
+ }
+ }
+ updatePosition() {
+ var handleRange = this.height - this.handleHeight,
+ valueRange = this.max - this.min;
+
+ this.handleY = handleRange - ((this.value - this.min) / valueRange) * handleRange;
+ }
+ captureMouse(element) {
+ var self = this,
+ mouse = captureMouse(element),
+ bounds = {};
+
+ setHandleBounds();
+
+ element.addEventListener('mousedown', function () {
+ if (rectContainsPoint(bounds, mouse)) {
+ element.addEventListener('mouseup', onMouseUp, false);
+ element.addEventListener('mousemove', onMouseMove, false);
+ }
+ }, false);
+
+ function onMouseUp() {
+ element.removeEventListener('mousemove', onMouseMove, false);
+ element.removeEventListener('mouseup', onMouseUp, false);
+ setHandleBounds();
+ }
+
+ function onMouseMove() {
+ var pos_y = mouse.y - self.y;
+ self.handleY = Math.min(self.height - self.handleHeight, Math.max(pos_y, 0));
+ self.updateValue();
+ }
+
+ function setHandleBounds() {
+ bounds.x = self.x;
+ bounds.y = self.y + self.handleY;
+ bounds.width = self.width;
+ bounds.height = self.handleHeight;
+ }
+ }
+}
diff --git a/custom/examples/include/style.css b/custom/examples/include/style.css
new file mode 100644
index 0000000..adfd0ae
--- /dev/null
+++ b/custom/examples/include/style.css
@@ -0,0 +1,138 @@
+/* Some HTML5 Tags
+ */
+
+ aside, footer, header, nav, section {
+ display: block;
+ }
+
+ /* Examples
+ */
+
+ body {
+ background-color: #bbb;
+ color: #383838;
+ }
+
+ #canvas {
+ background-color: #fff;
+ }
+
+ header {
+ padding-bottom: 10px;
+ }
+
+ header a {
+ color: #30f;
+ text-decoration: none;
+ }
+
+ aside {
+ padding-top: 6px;
+ }
+
+ /* Index page
+ */
+
+ #index-body {
+ background-color: #fdeba1;
+ font-family: "Vollkorn", serif;
+ color: #000;
+ }
+
+ #index-body a {
+ text-decoration: none;
+ color: #b30300;
+ }
+
+ #index-body #description, #index-body #exercises {
+ overflow: auto;
+ max-width: 900px;
+ margin: 0px auto 20px auto;
+ padding-left: 15px;
+ padding-bottom: 15px;
+ background-color: #fff;
+ border-radius: 15px;
+ }
+
+ #index-body #description {
+ margin-top: 40px;
+ }
+
+ #index-body h1 {
+ color: #b30300;
+ }
+
+ #index-body #description h2 {
+ margin-bottom: 0;
+ }
+
+ #index-body h1 a {
+ text-decoration: underline;
+ color: #b30300;
+ }
+
+ #index-body li h2, #index-body li h3, #index-body li h4 {
+ color: #000;
+ }
+
+ #index-body li h3 {
+ margin-bottom: 0px;
+ }
+
+ #index-body #description ul {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+ }
+
+ #index-body #description ul li {
+ padding-bottom: 0.6em;
+ }
+ .container {
+ display: table;
+ width: 100%;
+ height: auto;
+ }
+ .container .text {
+ display:table-cell;
+ height:100%;
+ vertical-align:middle;
+ }
+ .container img {
+ padding: 0 20px;
+ display: block;
+ float: right;
+ }
+ .container .clear {
+ clear: both;
+ }
+
+ #exercises ul {
+ margin: 0;
+ padding: 4px 20px 10px 20px;
+ }
+
+ #exercises ol {
+ margin: 0 20px 10px 0;
+ padding: 0;
+ list-style-type: none;
+ }
+
+ #exercises ol li {
+ padding-top: 5px;
+ }
+
+ #exercises ol ol ol {
+ padding-left: 60px;
+ list-style-type: decimal-leading-zero;
+ }
+
+ #exercises ol ol ol li img, #exercises ol ol li img {
+ margin-left: 4px;
+ margin-bottom: -10;
+ }
+
+ #exercises h2 {
+ margin: 10px 0 0 0;
+ }
+
\ No newline at end of file
diff --git a/custom/examples/include/utils.js b/custom/examples/include/utils.js
new file mode 100644
index 0000000..947bbe4
--- /dev/null
+++ b/custom/examples/include/utils.js
@@ -0,0 +1,225 @@
+/**
+ * Normalize the browser animation API across implementations. This requests
+ * the browser to schedule a repaint of the window for the next animation frame.
+ * Checks for cross-browser support, and, failing to find it, falls back to setTimeout.
+ * @param {function} callback Function to call when it's time to update your animation for the next repaint.
+ * @param {HTMLElement} element Optional parameter specifying the element that visually bounds the entire animation.
+ * @return {number} Animation frame request.
+ */
+if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ function (callback) {
+ return window.setTimeout(callback, 17 /*~ 1000/60*/);
+ });
+ }
+
+/**
+ * ERRATA: 'cancelRequestAnimationFrame' renamed to 'cancelAnimationFrame' to reflect an update to the W3C Animation-Timing Spec.
+ *
+ * Cancels an animation frame request.
+ * Checks for cross-browser support, falls back to clearTimeout.
+ * @param {number} Animation frame request.
+ */
+if (!window.cancelAnimationFrame) {
+ window.cancelAnimationFrame = (window.cancelRequestAnimationFrame ||
+ window.webkitCancelAnimationFrame || window.webkitCancelRequestAnimationFrame ||
+ window.mozCancelAnimationFrame || window.mozCancelRequestAnimationFrame ||
+ window.msCancelAnimationFrame || window.msCancelRequestAnimationFrame ||
+ window.oCancelAnimationFrame || window.oCancelRequestAnimationFrame ||
+ window.clearTimeout);
+}
+
+/**
+ * Keeps track of the current mouse position, relative to an element.
+ * @param {HTMLElement} element
+ * @return {object} Contains properties: x, y, event
+ */
+export function captureMouse (element) {
+ let mouse = {x: 0, y: 0, event: null}
+ const {
+ scrollLeft: body_scrollLeft,
+ scrollTop: body_scrollTop
+ } = document.body
+ const {
+ scrollLeft: element_scrollLeft,
+ scrollTop: element_scrollTop
+ } = document.documentElement
+ const {offsetLeft, offsetTop} = element
+
+ element.addEventListener('mousemove', event => {
+ let x, y
+
+ if (event.pageX || event.pageY) {
+ x = event.pageX
+ y = event.pageY
+ } else {
+ x = event.clientX + body_scrollLeft + element_scrollLeft
+ y = event.clientY + body_scrollTop + element_scrollTop
+ }
+
+ x -= offsetLeft
+ y -= offsetTop
+
+ mouse.x = x
+ mouse.y = y
+ mouse.event = event
+ }, false)
+
+ return mouse
+}
+
+/**
+ * Keeps track of the current (first) touch position, relative to an element.
+ * @param {HTMLElement} element
+ * @return {object} Contains properties: x, y, isPressed, event
+ */
+export function captureTouch (element) {
+ let touch = {x: null, y: null, isPressed: false, event: null}
+ const {
+ scrollLeft: body_scrollLeft,
+ scrollTop: body_scrollTop
+ } = document.body
+ const {
+ scrollLeft: element_scrollLeft,
+ scrollTop: element_scrollTop
+ } = document.documentElement
+ const {offsetLeft, offsetTop} = element
+
+ element.addEventListener('touchstart', event => {
+ touch.isPressed = true;
+ touch.event = event;
+ }, false);
+
+ element.addEventListener('touchend', event => {
+ touch.isPressed = false;
+ touch.x = null;
+ touch.y = null;
+ touch.event = event;
+ }, false);
+
+ element.addEventListener('touchmove', event => {
+ let x, y,
+ touch_event = event.touches[0]; //first touch
+
+ if (touch_event.pageX || touch_event.pageY) {
+ x = touch_event.pageX;
+ y = touch_event.pageY;
+ } else {
+ x = touch_event.clientX + body_scrollLeft + element_scrollLeft;
+ y = touch_event.clientY + body_scrollTop + element_scrollTop;
+ }
+ x -= offsetLeft;
+ y -= offsetTop;
+
+ touch.x = x;
+ touch.y = y;
+ touch.event = event;
+ }, false);
+
+ return touch;
+};
+
+/**
+ * Returns a color in the format: '#RRGGBB', or as a hex number if specified.
+ * @param {number|string} color
+ * @param {boolean=} toNumber=false Return color as a hex number.
+ * @return {string|number}
+ */
+export function parseColor (color, toNumber = false) {
+ if (toNumber === true) {
+ if (typeof color === 'number') {
+ return (color | 0); //chop off decimal
+ }
+ if (typeof color === 'string' && color[0] === '#') {
+ color = color.slice(1);
+ }
+ return window.parseInt(color, 16);
+ } else {
+ if (typeof color === 'number') {
+ color = '#' + ('00000' + (color | 0).toString(16)).substr(-6); //pad
+ }
+ return color;
+ }
+};
+
+/**
+ * Converts a color to the RGB string format: 'rgb(r,g,b)' or 'rgba(r,g,b,a)'
+ * @param {number|string} color
+ * @param {number} alpha
+ * @return {string}
+ */
+export function colorToRGB (color, alpha) {
+ //number in octal format or string prefixed with #
+ if (typeof color === 'string' && color[0] === '#') {
+ color = window.parseInt(color.slice(1), 16);
+ }
+ alpha = (alpha === undefined) ? 1 : alpha;
+ //parse hex values
+ var r = color >> 16 & 0xff,
+ g = color >> 8 & 0xff,
+ b = color & 0xff,
+ a = (alpha < 0) ? 0 : ((alpha > 1) ? 1 : alpha);
+ //only use 'rgba' if needed
+ if (a === 1) {
+ return "rgb("+ r +","+ g +","+ b +")";
+ } else {
+ return "rgba("+ r +","+ g +","+ b +","+ a +")";
+ }
+};
+
+/**
+ * Determine if a rectangle contains the coordinates (x,y) within it's boundaries.
+ * @param {object} rect Object with properties: x, y, width, height.
+ * @param {object} point Object with properties: x, y.
+ * @return {boolean}
+ */
+export function rectContainsPoint(rect, point) {
+ return !(point.x < rect.x ||
+ point.x > rect.x + rect.width ||
+ point.y < rect.y ||
+ point.y > rect.y + rect.height);
+}
+
+/**
+ * 检测某个点跟某个圆是否接触
+ * @param {Object} circle 圆 {radius, x, y}
+ * @param {Object} point 点 {x, y}
+ * @return {Boolean} true 接触 false 没接触
+ */
+export function circleContainsPoint(circle, point) {
+ const dx = point.x - circle.x,
+ dy = point.y - circle.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ return distance <= circle.radius
+}
+
+/**
+ * 检测两个圆是否相交
+ * @param {Object} circleA 有x,y,radius的对象
+ * @param {Object} circleB 有x,y,radius的对象
+ * @returns {Boolean}
+ */
+export function interCircles(circleA, circleB) {
+ const dx = circleA.x - circleB.x,
+ dy = circleA.y - circleB.y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ return distance < (circleA.radius + circleB.radius)
+}
+
+/**
+ * Determine if two rectangles overlap.
+ * @param {object} rectA Object with properties: x, y, width, height.
+ * @param {object} rectB Object with properties: x, y, width, height.
+ * @return {boolean}
+ */
+export function intersects(rectA, rectB) {
+ return !(rectA.x + rectA.width < rectB.x ||
+ rectB.x + rectB.width < rectA.x ||
+ rectA.y + rectA.height < rectB.y ||
+ rectB.y + rectB.height < rectA.y);
+};
diff --git a/custom/xtras/keycode.js b/custom/xtras/keycode.js
new file mode 100644
index 0000000..6f483bd
--- /dev/null
+++ b/custom/xtras/keycode.js
@@ -0,0 +1,129 @@
+/**
+ * A list of JavaScript key codes to reference by name.
+ * From 'Foundation HTML5 Animation with JavaScript': http://amzn.com/1430236655?tag=html5anim-20
+ */
+
+const keycode = {
+ BACKSPACE: 8,
+ TAB: 9,
+ ENTER: 13,
+ COMMAND: 15,
+ SHIFT: 16,
+ CONTROL: 17,
+ ALTERNATE: 18,
+ PAUSE: 19,
+ CAPS_LOCK: 20,
+ NUMPAD: 21,
+ ESCAPE: 27,
+ SPACE: 32,
+ PAGE_UP: 33,
+ PAGE_DOWN: 34,
+ END: 35,
+ HOME: 36,
+
+ //arrows
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+
+ INSERT: 45,
+ DELETE: 46,
+
+ //numbers
+ NUMBER_0: 48,
+ NUMBER_1: 49,
+ NUMBER_2: 50,
+ NUMBER_3: 51,
+ NUMBER_4: 52,
+ NUMBER_5: 53,
+ NUMBER_6: 54,
+ NUMBER_7: 55,
+ NUMBER_8: 56,
+ NUMBER_9: 57,
+
+ //letters
+ A: 65,
+ B: 66,
+ C: 67,
+ D: 68,
+ E: 69,
+ F: 70,
+ G: 71,
+ H: 72,
+ I: 73,
+ J: 74,
+ K: 75,
+ L: 76,
+ M: 77,
+ N: 78,
+ O: 79,
+ P: 80,
+ Q: 81,
+ R: 82,
+ S: 83,
+ T: 84,
+ U: 85,
+ V: 86,
+ W: 87,
+ X: 88,
+ Y: 89,
+ Z: 90,
+
+ LEFT_WINDOW_KEY: 91,
+ RIGHT_WINDOW_KEY: 92,
+ SELECT_KEY: 93,
+
+ //number pad
+ NUMPAD_0: 96,
+ NUMPAD_1: 97,
+ NUMPAD_2: 98,
+ NUMPAD_3: 99,
+ NUMPAD_4: 100,
+ NUMPAD_5: 101,
+ NUMPAD_6: 102,
+ NUMPAD_7: 103,
+ NUMPAD_8: 104,
+ NUMPAD_9: 105,
+ NUMPAD_MULTIPLY: 106,
+ NUMPAD_ADD: 107,
+ NUMPAD_ENTER: 108,
+ NUMPAD_SUBTRACT: 109,
+ NUMPAD_DECIMAL: 110,
+ NUMPAD_DIVIDE: 111,
+
+ //function keys
+ F1: 112,
+ F2: 113,
+ F3: 114,
+ F4: 115,
+ F5: 116,
+ F6: 117,
+ F7: 118,
+ F8: 119,
+ F9: 120,
+ F10: 121,
+ F11: 122,
+ F12: 123,
+ F13: 124,
+ F14: 125,
+ F15: 126,
+
+ NUM_LOCK: 144,
+ SCROLL_LOCK: 145,
+
+ //punctuation
+ SEMICOLON: 186,
+ EQUAL: 187,
+ COMMA: 188,
+ MINUS: 189,
+ PERIOD: 190,
+ SLASH: 191,
+ BACKQUOTE: 192,
+ LEFTBRACKET: 219,
+ BACKSLASH: 220,
+ RIGHTBRACKET: 221,
+ QUOTE: 222
+};
+
+export default keycode
\ No newline at end of file
diff --git a/examples/ch03/07-wave-2.html b/examples/ch03/07-wave-2.html
index b226853..cd8b1b7 100644
--- a/examples/ch03/07-wave-2.html
+++ b/examples/ch03/07-wave-2.html
@@ -33,7 +33,7 @@
context.moveTo(xpos, ypos);
//Calculate the new position.
xpos += xspeed;
- angle += yspeed;
+ angle -= yspeed; // Coordinate of canvas is different
ypos = centerY + Math.sin(angle) * range;
context.lineTo(xpos, ypos);
context.stroke();