-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathold.ts
309 lines (258 loc) · 7.81 KB
/
old.ts
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
const canvas = document.querySelector("#myCanvas") as HTMLCanvasElement;
const context = canvas.getContext("2d");
let slingshotInputElement = <HTMLInputElement>document.getElementById("slingshot");
let randomBallSizeInput = <HTMLInputElement>document.getElementById("randomBallSize");
let clearButton = <HTMLButtonElement>document.getElementById("clear");
/**
* Used to fix blurry view on Retina devices
* @type {number}
*/
let canvasMultiplier = 2;
/**
* Set canvas width to take up full screen
* @type {number}
*/
canvas.width = window.innerWidth * canvasMultiplier;
canvas.height = window.innerHeight * canvasMultiplier;
canvas.style.width = "100%";
canvas.style.height = "100%";
/**
* Array of possible colours each ball will use
* @source https://color.adobe.com/create/color-wheel/?copy=true&base=1&rule=Custom&selected=0&name=Copy%20of%20Phaedra&mode=rgb&rgbvalues=1,0.38009,0.218215,1,1,0.615686,0.744159,0.92,0.623819,0.474904,0.74,0.55981,0,0.64,0.534545&swatchOrder=0,1,2,3,4
* @type {[string,string,string,string,string]}
*/
const colours = ["#ff6138", "#ffff9d", "#beeb9f", "#79bd8f", "#00a388", "#ff9ddb"];
/**
* Start with empty array (balls will be pushed on click)
* @type {Array}
*/
let balls = [];
/**
* Slingshot and line start x/y position variables
* Set once in 'mousedown' functions and destroyed on mouseup
*/
let xSlingshotStart, ySlingshotStart;
let xLineStart, yLineStart;
/**
* Line is created on 'mousedown' and destroyed on 'mouseup' in Slingshot mode
* @type {any}
*/
let line = null; let startLine = false;
/**
* Min and max ball sizes
* @type {number}
*/
let maxBallSzie = 30; let minBallSize = 10;
/**
* Checks for different browsers
* @type {((callback:FrameRequestCallback)=>number)|any}
*/
const reqAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
/**
* Ball class to construct new balls
* @class Ball
*/
class Ball {
private radius: number;
private speed: number;
private xPos: number;
private yPos: number;
private colour: string;
private xVelocity: number;
private yVelocity: number;
constructor(xPos, yPos, colour, xVelocity, yVelocity, radius) {
this.radius = radius;
this.speed = 10;
this.xPos = xPos * canvasMultiplier;
this.yPos = yPos * canvasMultiplier;
this.colour = colour;
this.xVelocity = xVelocity;
this.yVelocity = yVelocity;
}
public update = () => {
/**
* Begin drawing new individual ball / updating existing ball position
* Contains animation logic (should follow bouncing ball animation with gravity)
* @method beginPath()
*/
context.beginPath();
let radius = this.radius;
let sAngle = 0;
let eAngle = Math.PI * 2;
let counterclockwise = false;
const gravity = 0.2;
/**
* Larger balls have less bounce
* @type {number}
*/
const bounce = 0.9 * minBallSize / radius;
const traction = 0.8;
/**
* Bouncing animation logic - modified from a JS tutorial on StackOverflow of a similar nature
* @author Shomz : https://stackoverflow.com/questions/29982228/how-to-apply-gravity-to-bouncing-balls-in-javascript
*/
if (this.xPos + radius >= canvas.width) {
this.xVelocity = -this.xVelocity * bounce;
this.xPos = canvas.width - radius;
} else if (this.xPos - radius <= 0) {
this.xVelocity = -this.xVelocity * bounce;
this.xPos = radius;
}
if (this.yPos + radius >= canvas.height) {
this.yVelocity = -this.yVelocity * bounce;
this.yPos = canvas.height - radius;
this.xVelocity *= traction;
} else if (this.yPos - radius <= 0) {
this.yVelocity = -this.yVelocity * bounce;
this.yPos = radius;
}
this.yVelocity += gravity;
this.xPos += this.xVelocity;
this.yPos += this.yVelocity;
let xPos = this.xPos;
let yPos = this.yPos;
/**
* Each ball is redrawn
* @method arc
* @param xPos, x-coordinate of the center of the circle
* @param yPos, y-coordinate of the center of the circle
* @param radius, The radius of the circle (no change)
* @param sAngle, The starting angle, in radians (0 is at the 3 o'clock position of the arc's circle) - no change
* @param eAngle, The ending angle, in radians (no change) - uses Math.PI * 2 to ensure a full circle is drawn
* @param counterclockwise (optional), Specifies whether the drawing should be counterclockwise or clockwise
*/
context.arc(xPos, yPos, radius, sAngle, eAngle, counterclockwise);
/**
* Finish drawing new individual ball / updating existing ball position
* @method closePath()
*/
context.closePath();
/**
* Define a colour for the ball
* @type {string}
*/
context.fillStyle = this.colour;
/**
* Fill in the ball with colour
* @method fill()
*/
context.fill();
}
}
/**
* Line drawn by dragging mouse in slingshot mode
* @class
*/
class Line {
private xLineStart: number;
private yLineStart: number;
private xLineEnd: number;
private yLineEnd: number;
constructor(xLineStart, yLineStart, xLineEnd, yLineEnd) {
this.xLineStart = xLineStart * canvasMultiplier;
this.yLineStart = yLineStart * canvasMultiplier;
this.xLineEnd = xLineEnd * canvasMultiplier;
this.yLineEnd = yLineEnd * canvasMultiplier;
}
public drawLine = () => {
context.beginPath();
context.moveTo(this.xLineStart, this.yLineStart);
context.lineTo(this.xLineEnd, this.yLineEnd);
context.lineWidth = 2;
context.strokeStyle = "rgba(255,255,255,0.15)";
context.stroke();
}
}
/**
* Draw each individual ball
* @method drawBall
*/
function addNewBall(xPos, yPos, xVelocity, yVelocity) {
let colour = colours[randomIntFromInterval(0, colours.length)];
let randomBallSizeChecked = randomBallSizeInput.checked;
/**
* If randomBallSize input is checked, generate random size balls, otherwise use medium sizes ball
* Larger ball sizes will have less bounce than smaller ball sizes
* @type {number}
*/
let radius = randomBallSizeChecked ? randomIntFromInterval(minBallSize, maxBallSzie) : maxBallSzie / 2;
let ball = new Ball(xPos, yPos, colour, xVelocity, yVelocity, radius);
balls.push(ball);
}
/**
* Fetch a random number between min and max number
*/
function randomIntFromInterval(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
/**
* Draw or re-draw the entire canvas
* reqAnimationFrame gets called roughly 60 times per second (decided by the browser)
* @method draw
*/
function draw() {
/**
* Clear whole canvas
*/
context.clearRect(0, 0, canvas.width, canvas.width);
/**
* Dark background theme for canvas
* @type {string}
*/
context.fillStyle = "#333";
/**
* Fill canvas with dark bg
* @method fillRect
*/
context.fillRect(0, 0, canvas.width, canvas.width);
/**
* Update each ball's position
* update() function should follow a bouncing ball animation with gravity
*/
for (let i = 0; i < balls.length; i++) {
let myBall = balls[i];
myBall.update();
}
/**
* Draw line
*/
if (line != null) {
line.drawLine();
}
reqAnimationFrame(draw);
}
/**
* Event listeners
*/
function addEventListeners() {
/**
* Add click listener for canvas
*/
canvas.addEventListener("click", (e) => {
let xPos = e.clientX;
let yPos = e.clientY;
/**
* Random direction (x and y velocities)
* @type {number}
*/
let xVelocity = randomIntFromInterval(-10, 10);
let yVelocity = randomIntFromInterval(-10, 10);
if (!slingshotInputElement.checked) {
addNewBall(xPos, yPos, xVelocity, yVelocity);
}
});
/**
* Add click listener for clear
*/
clearButton.addEventListener("click", (e) => {
this.balls = [];
});
}
/**
* Add event listeners
*/
addEventListeners();
/**
* Draw canvas and start animation process
*/
draw();