HTML5 Canvas Interactive Ball Physics

Instructions: Throw the ball around with your cursor. Open in new window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<head>
    <script src="http://www.html5canvastutorials.com/libraries/kinetic-v3.7.4.js">
    </script>
    <script>
        window.requestAnimFrame = (function(callback){
            return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function(callback){
                window.setTimeout(callback, 1000 / 60);
            };
        })();
 
        function animate(lastTime, ball){
            var stage = ball.getStage();
            var layer = ball.getLayer();
            var date = new Date();
            var time = date.getTime();
            var timeDiff = time - lastTime;
 
            // update
            updateBall(timeDiff, ball);
 
            // draw
            layer.draw();
 
            // request new frame
            requestAnimFrame(function(){
                animate(time, ball);
            });
        }
 
        function updateBall(timeDiff, ball){
            var stage = ball.getStage();
            var ballX = ball.x;
            var ballY = ball.y;
 
            // physics variables
            var gravity = 20; // px / second^2
            var speedIncrementFromGravityEachFrame = gravity * timeDiff / 1000;
            var collisionDamper = 0.2; // 20% energy loss
            var floorFriction = 5; // px / second^2
            var floorFrictionSpeedReduction = floorFriction * timeDiff / 1000;
 
            // if dragging and dropping
            if (ball.drag.moving) {
                var mousePos = stage.getMousePosition();
 
                if (mousePos !== null) {
                    var mouseX = mousePos.x;
                    var mouseY = mousePos.y;
 
                    var c = 0.06 * timeDiff;
                    ball.vx = c * (mouseX - ball.lastMouseX);
                    ball.vy = c * (mouseY - ball.lastMouseY);
                    ball.lastMouseX = mouseX;
                    ball.lastMouseY = mouseY;
                }
            }
            else {
                // gravity
                ball.vy += speedIncrementFromGravityEachFrame;
                ballX += ball.vx;
                ballY += ball.vy;
 
                // ceiling condition
                if (ballY < ball.radius) {
                    ballY = ball.radius;
                    ball.vy *= -1;
                    ball.vy *= (1 - collisionDamper);
                }
 
                // floor condition
                if (ballY > (stage.height - ball.radius)) {
                    ballY = stage.height - ball.radius;
                    ball.vy *= -1;
                    ball.vy *= (1 - collisionDamper);
                }
 
                // floor friction
                if (ballY == stage.height - ball.radius) {
                    if (ball.vx > 0.1) {
                        ball.vx -= floorFrictionSpeedReduction;
                    }
                    else if (ball.vx < -0.1) {
                        ball.vx += floorFrictionSpeedReduction;
                    }
                    else {
                        ball.vx = 0;
                    }
                }
 
                // right wall condition
                if (ballX > (stage.width - ball.radius)) {
                    ballX = stage.width - ball.radius;
                    ball.vx *= -1;
                    ball.vx *= (1 - collisionDamper);
                }
 
                // left wall condition
                if (ballX < (ball.radius)) {
                    ballX = ball.radius;
                    ball.vx *= -1;
                    ball.vx *= (1 - collisionDamper);
                }
            }
 
            ball.setPosition(ballX, ballY);
        }
 
        window.onload = function(){
            var stage = new Kinetic.Stage("container", 578, 200);
            var layer = new Kinetic.Layer();
            var radius = 20;
 
            var ball = new Kinetic.Shape(function(){
                var context = this.getContext();
                context.beginPath();
                context.arc(0, 0, radius, 0, 2 * Math.PI, false);
                context.fillStyle = "blue";
                context.fill();
            });
 
            // add custom properties
            ball.vx = 0;
            ball.vy = 0;
            ball.radius = radius;
 
            ball.on("dragstart", function(){
                ball.vx = 0;
                ball.vy = 0;
            });
 
            ball.on("mouseover", function(){
                document.body.style.cursor = "pointer";
            });
 
            ball.on("mouseout", function(){
                document.body.style.cursor = "default";
            });
 
            ball.setPosition(stage.width / 2, stage.height / 2);
 
            ball.draggable(true);
 
            layer.add(ball);
            stage.add(layer);
 
            var date = new Date();
            var time = date.getTime();
            animate(time, ball);
        };
    </script>
</head>
<body onmousedown="return false;">
    <div id="container">
    </div>
</body>
Modified on February 19th, 2012 by Eric Rowell
comments powered by Disqus