1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+ < head >
4+ < meta charset ="UTF-8 ">
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6+ < meta http-equiv ="X-UA-Compatible " content ="ie=edge ">
7+ < title > Projectile Motion</ title >
8+
9+ < style >
10+ .btns , .inputs {
11+ margin : 20px ;
12+ }
13+
14+ input [type = "number" ] {
15+ border-radius : 10px ;
16+ border : 2px solid # aaa ;
17+ padding : 5px ;
18+ font-size : 1.1em ;
19+ margin-bottom : 10px ;
20+ }
21+
22+ button {
23+ border : none;
24+ padding : 10px 15px ;
25+ border-radius : 5px ;
26+ margin-right : 20px ;
27+ color : # fff ;
28+ font-size : 1.05em ;
29+ font-weight : 600 ;
30+ cursor : pointer;
31+ }
32+
33+ # launch {
34+ background-color : deepskyblue;
35+ }
36+
37+ # reset {
38+ background-color : crimson;
39+ }
40+
41+ .control {
42+ display : flex;
43+ align-items : center;
44+ flex-wrap : wrap;
45+ justify-content : center;
46+ }
47+
48+ .board {
49+ position : relative;
50+ margin : 20px ;
51+ height : 450px ;
52+ }
53+
54+ .board ::before {
55+ content : "" ;
56+ position : absolute;
57+ bottom : 0 ;
58+ transform : translateY (50% );
59+ width : 100% ;
60+ height : 2px ;
61+ background-color : black;
62+ }
63+
64+ .board ::after {
65+ content : "" ;
66+ position : absolute;
67+ left : 0 ;
68+ transform : translateX (-50% );
69+ width : 2px ;
70+ height : 100% ;
71+ background-color : black;
72+ z-index : -1 ;
73+ }
74+
75+ .x {
76+ position : absolute;
77+ bottom : 0 ;
78+ right : 0 ;
79+ transform : translateX (200% ) translateY (50% );
80+ }
81+
82+ .y {
83+ position : absolute;
84+ top : 0 ;
85+ left : 0 ;
86+ transform : translateX (-50% ) translateY (-100% );
87+ }
88+
89+ .y ::before {
90+ position : absolute;
91+ content : "" ;
92+ display : inline-block;
93+ width : 0.6em ;
94+ height : 0.6em ;
95+ border-right : 0.2em solid black;
96+ border-top : 0.2em solid black;
97+ transform : rotate (-45deg );
98+ left : -1px ;
99+ bottom : -14px ;
100+ }
101+
102+ .x ::before {
103+ position : absolute;
104+ content : "" ;
105+ display : inline-block;
106+ width : 0.6em ;
107+ height : 0.6em ;
108+ border-right : 0.2em solid black;
109+ border-top : 0.2em solid black;
110+ transform : rotate (45deg );
111+ left : -23px ;
112+ bottom : 3px ;
113+ }
114+
115+ # projectile {
116+ position : absolute;
117+ width : 30px ;
118+ height : 30px ;
119+ background-color : darkred;
120+ left : 0 ;
121+ bottom : 0 ;
122+ transform : translate (-50% , 50% );
123+ border-radius : 50% ;
124+ }
125+
126+ .path {
127+ position : absolute;
128+ width : 10px ;
129+ height : 10px ;
130+ background-color : black;
131+ border-radius : 50% ;
132+ z-index : -1 ;
133+ }
134+
135+ .yDist {
136+ position : absolute;
137+ transform : translate (-50% , -110% );
138+ background-color : black;
139+ color : white;
140+ padding : 5px ;
141+ font-size : 1.1em ;
142+ }
143+
144+ .yDist ::before {
145+ content : "" ;
146+ position : absolute;
147+ border : 10px solid black;
148+ border-color : black transparent transparent transparent;
149+ left : 50% ;
150+ bottom : 0 ;
151+ transform : translate (-50% , 100% );
152+ }
153+
154+ .xDist {
155+ position : absolute;
156+ transform : translate (75% , 50% );
157+ background-color : black;
158+ color : white;
159+ padding : 5px ;
160+ font-size : 1.1em ;
161+ }
162+
163+ .xDist ::before {
164+ content : "" ;
165+ position : absolute;
166+ border : 10px solid black;
167+ border-color : transparent black transparent transparent;
168+ left : 0 ;
169+ bottom : 50% ;
170+ transform : translate (-100% , calc (-1px + 50% ));
171+ }
172+ </ style >
173+
174+ </ head >
175+ < body >
176+ < div class ="control ">
177+ < div class ="inputs ">
178+ < input type ="number " name ="velocity " id ="velocity " placeholder ="Initial Velocity ">
179+ < input type ="number " name ="angle " id ="angle " placeholder ="Angle ">
180+ </ div >
181+ < div class ="btns ">
182+ < button id ="launch "> Launch</ button >
183+ < button id ="reset "> Reset</ button >
184+ </ div >
185+ </ div >
186+ < div class ="board ">
187+ < span class ="x "> X</ span >
188+ < span class ="y "> Y</ span >
189+ < div id ="projectile "> </ div >
190+ </ div >
191+
192+ < script >
193+
194+ class Projectile {
195+ element ;
196+ x ;
197+ y ;
198+ g ;
199+ horizontalRange ;
200+ maxHeight ;
201+ actualHorizontalRange ;
202+
203+
204+ constructor ( element , x , y ) {
205+ this . element = element ;
206+ this . x = x ;
207+ this . y = y ;
208+ }
209+
210+ moveHorizontal ( speed ) {
211+ let intervalHorizontal = setInterval ( ( ) => {
212+ if ( this . x >= this . horizontalRange ) {
213+ if ( this . y > 0 )
214+ this . horizontalRange *= 2 ;
215+ else {
216+
217+ clearInterval ( intervalHorizontal ) ;
218+
219+ let xDist = document . createElement ( 'div' ) ;
220+ let text = document . createTextNode ( this . actualHorizontalRange ) ;
221+ xDist . appendChild ( text ) ;
222+ xDist . classList . add ( 'xDist' ) ;
223+ xDist . style . left = this . x + 'px' ;
224+ xDist . style . bottom = this . y + 'px' ;
225+ this . element . parentElement . appendChild ( xDist ) ;
226+ }
227+ }
228+
229+ if ( this . x % 25 === 0 ) {
230+ let path = document . createElement ( 'div' ) ;
231+ path . classList . add ( 'path' ) ;
232+ path . style . left = this . x + 'px' ;
233+ path . style . bottom = this . y + 'px' ;
234+ this . element . parentElement . appendChild ( path ) ;
235+ }
236+
237+ this . x ++ ;
238+ this . element . style . left = this . x + 'px' ;
239+ } , 500 / speed ) ;
240+ }
241+
242+ moveVertical ( velocity ) {
243+
244+ let interval = ( ) => new Promise ( resolve => {
245+ let intervalUp = ( ) => {
246+
247+ // velocity -= this.g * 1/velocity;
248+ velocity = Math . sqrt ( velocity * velocity - 2 * this . g ) ;
249+
250+ let oneInterval = setTimeout ( intervalUp , 500 / velocity ) ;
251+
252+ if ( this . y >= this . maxHeight && isNaN ( velocity ) ) {
253+ clearTimeout ( oneInterval ) ;
254+
255+ let yDist = document . createElement ( 'div' ) ;
256+ let text = document . createTextNode ( this . maxHeight ) ;
257+ yDist . appendChild ( text ) ;
258+ yDist . classList . add ( 'yDist' ) ;
259+ yDist . style . left = this . x + 'px' ;
260+ yDist . style . bottom = this . y + 'px' ;
261+ this . element . parentElement . appendChild ( yDist ) ;
262+
263+ resolve ( ) ;
264+ }
265+ this . y ++ ;
266+ this . element . style . bottom = this . y + 'px' ;
267+ }
268+
269+ setTimeout ( intervalUp , 500 / velocity ) ;
270+ } ) ;
271+
272+ interval ( ) . then ( ( ) => {
273+ let intervalDown = ( ) => {
274+
275+ // velocity += this.g * 1/velocity;
276+ velocity = Math . sqrt ( velocity * velocity + 2 * this . g ) ;
277+
278+ let oneInterval = setTimeout ( intervalDown , 500 / velocity ) ;
279+
280+ if ( this . y <= 0 ) {
281+ clearTimeout ( oneInterval ) ;
282+
283+ this . horizontalRange = this . x ;
284+ }
285+ this . y -- ;
286+ this . element . style . bottom = this . y + 'px' ;
287+ }
288+ velocity = 0 ;
289+ // setTimeout(intervalDown, 0);
290+ intervalDown ( ) ;
291+ } ) ;
292+ }
293+ }
294+
295+ let initVelocity = document . getElementById ( 'velocity' ) ;
296+ let angle = document . getElementById ( 'angle' ) ;
297+
298+ let launch = document . getElementById ( 'launch' ) ;
299+ let reset = document . getElementById ( 'reset' ) ;
300+
301+ let projectileObj = new Projectile (
302+ document . getElementById ( 'projectile' ) ,
303+ 0 ,
304+ 0
305+ ) ;
306+
307+ launch . onclick = ( ) => {
308+ let initVelocityValue = parseInt ( initVelocity . value ) ;
309+ let angleValue = parseInt ( angle . value ) ;
310+
311+ if (
312+ ! isNaN ( initVelocityValue ) &&
313+ ! isNaN ( angleValue ) &&
314+ initVelocityValue > 0 &&
315+ angleValue > 0 &&
316+ angleValue <= 90 &&
317+ ! projectileObj . x &&
318+ ! projectileObj . y
319+ ) {
320+ projectileObj . g = 9.81 ;
321+
322+ let vx = initVelocityValue * Math . cos ( Math . PI * angleValue / 180 ) ;
323+ let vy0 = initVelocityValue * Math . sin ( Math . PI * angleValue / 180 ) ;
324+ let maxHeight = Math . round ( ( vy0 * vy0 ) / ( 2 * projectileObj . g ) ) ;
325+ let horizontalRange = Math . round ( vx * ( 2 * vy0 / projectileObj . g ) ) ;
326+
327+ projectileObj . maxHeight = maxHeight ;
328+ projectileObj . horizontalRange = horizontalRange ;
329+ projectileObj . actualHorizontalRange = horizontalRange ;
330+
331+ if ( Math . floor ( vx ) > 0 ) projectileObj . moveHorizontal ( vx ) ;
332+ projectileObj . moveVertical ( vy0 ) ;
333+ }
334+ }
335+
336+ window . onload = ( ) => {
337+ initVelocity . value = '' ;
338+ angle . value = '' ;
339+ }
340+
341+ reset . onclick = ( ) => {
342+ initVelocity . value = '' ;
343+ angle . value = '' ;
344+ projectileObj . x = 0 ;
345+ projectileObj . y = 0 ;
346+ projectileObj . element . style . left = '0px' ;
347+ projectileObj . element . style . bottom = '0px' ;
348+
349+ let elements = document . getElementsByClassName ( 'path' ) ;
350+ while ( elements . length > 0 ) {
351+ elements [ 0 ] . parentNode . removeChild ( elements [ 0 ] ) ;
352+ }
353+ let yDist = document . getElementsByClassName ( 'yDist' ) ;
354+ yDist [ 0 ] . parentNode . removeChild ( yDist [ 0 ] ) ;
355+ let xDist = document . getElementsByClassName ( 'xDist' ) ;
356+ xDist [ 0 ] . parentNode . removeChild ( xDist [ 0 ] ) ;
357+ }
358+
359+ </ script >
360+ </ body >
361+ </ html >
0 commit comments