@@ -3,7 +3,202 @@ const closeButton = document.getElementById("close-btn");
3
3
const rules = document . getElementById ( "rules" ) ;
4
4
const canvas = document . getElementById ( "canvas" ) ;
5
5
const ctx = canvas . getContext ( "2d" ) ;
6
+ const color = getComputedStyle ( document . documentElement ) . getPropertyValue (
7
+ "--background-color"
8
+ ) ;
9
+ const secondaryColor = getComputedStyle (
10
+ document . documentElement
11
+ ) . getPropertyValue ( "--background-secondary-color" ) ;
12
+ let score = 0 ;
13
+ const brickRowCount = 9 ;
14
+ const brickColumnCount = 5 ;
15
+
16
+ // Reference: https://stackoverflow.com/questions/34772957/how-to-make-canvas-responsive
17
+ // https://stackoverflow.com/questions/39771732/drawing-to-responsive-canvas-that-is-100-width-and-height
18
+ const heightRatio = 0.75 ;
19
+ canvas . height = canvas . width * heightRatio ;
20
+ ctx . canvas . width = 800 ;
21
+ ctx . canvas . height = ctx . canvas . width * heightRatio ;
22
+
23
+ // Elements
24
+ const ball = {
25
+ x : canvas . width / 2 ,
26
+ y : canvas . height / 2 ,
27
+ size : 10 ,
28
+ speed : 4 ,
29
+ dx : 4 ,
30
+ dy : - 4 ,
31
+ } ;
32
+
33
+ const paddle = {
34
+ x : canvas . width / 2 - 40 ,
35
+ y : canvas . height - 20 ,
36
+ w : 80 ,
37
+ h : 10 ,
38
+ speed : 8 ,
39
+ dx : 0 ,
40
+ } ;
41
+
42
+ const brickInfo = {
43
+ w : 70 ,
44
+ h : 20 ,
45
+ padding : 10 ,
46
+ offsetX : 45 ,
47
+ offsetY : 60 ,
48
+ visible : true ,
49
+ } ;
50
+
51
+ const bricks = [ ] ;
52
+ for ( let i = 0 ; i < brickRowCount ; i ++ ) {
53
+ bricks [ i ] = [ ] ;
54
+ for ( let j = 0 ; j < brickColumnCount ; j ++ ) {
55
+ const x = i * ( brickInfo . w + brickInfo . padding ) + brickInfo . offsetX ;
56
+ const y = j * ( brickInfo . h + brickInfo . padding ) + brickInfo . offsetY ;
57
+ bricks [ i ] [ j ] = { x, y, ...brickInfo } ;
58
+ }
59
+ }
60
+
61
+ // Create Elements
62
+ function drawBall ( ) {
63
+ ctx . beginPath ( ) ;
64
+ ctx . arc ( ball . x , ball . y , ball . size , 0 , Math . PI * 2 ) ;
65
+ ctx . fillStyle = secondaryColor ;
66
+ ctx . fill ( ) ;
67
+ ctx . closePath ( ) ;
68
+ }
69
+
70
+ function drawPaddle ( ) {
71
+ ctx . beginPath ( ) ;
72
+ ctx . rect ( paddle . x , paddle . y , paddle . w , paddle . h ) ;
73
+ ctx . fillStyle = color ;
74
+ ctx . fill ( ) ;
75
+ ctx . closePath ( ) ;
76
+ }
77
+
78
+ function drawScore ( ) {
79
+ ctx . font = '20px "Balsamiq Sans"' ;
80
+ ctx . fillText ( `Score: ${ score } ` , canvas . width - 100 , 30 ) ;
81
+ }
82
+
83
+ function drawBricks ( ) {
84
+ bricks . forEach ( ( column ) => {
85
+ column . forEach ( ( brick ) => {
86
+ ctx . beginPath ( ) ;
87
+ ctx . rect ( brick . x , brick . y , brick . w , brick . h ) ;
88
+ ctx . fillStyle = brick . visible ? color : "transparent" ;
89
+ ctx . fill ( ) ;
90
+ ctx . closePath ( ) ;
91
+ } ) ;
92
+ } ) ;
93
+ }
94
+
95
+ function draw ( ) {
96
+ // clear
97
+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
98
+ // draw
99
+ drawBall ( ) ;
100
+ drawPaddle ( ) ;
101
+ drawScore ( ) ;
102
+ drawBricks ( ) ;
103
+ }
104
+
105
+ // Animate Elements
106
+ function movePaddle ( ) {
107
+ paddle . x += paddle . dx ;
108
+ if ( paddle . x + paddle . w > canvas . width ) paddle . x = canvas . width - paddle . w ;
109
+ if ( paddle . x < 0 ) paddle . x = 0 ;
110
+ }
111
+
112
+ function moveBall ( ) {
113
+ ball . x += ball . dx ;
114
+ ball . y += ball . dy ;
115
+ // wall collision
116
+ if ( ball . x + ball . size > canvas . width || ball . x - ball . size < 0 ) {
117
+ // right and left
118
+ ball . dx *= - 1 ;
119
+ }
120
+ if ( ball . y + ball . size > canvas . height || ball . y - ball . size < 0 ) {
121
+ // top and bottom
122
+ ball . dy *= - 1 ;
123
+ }
124
+ // paddle
125
+ if (
126
+ ball . x - ball . size > paddle . x &&
127
+ ball . x + ball . size < paddle . x + paddle . w &&
128
+ ball . y + ball . size > paddle . y
129
+ ) {
130
+ ball . dy = - ball . speed ;
131
+ }
132
+ // bricks
133
+ bricks . forEach ( ( column ) => {
134
+ column . forEach ( ( brick ) => {
135
+ if ( brick . visible ) {
136
+ if (
137
+ ball . x - ball . size > brick . x && // left brick side check
138
+ ball . x + ball . size < brick . x + brick . w && // right brick side check
139
+ ball . y + ball . size > brick . y && // top brick side check
140
+ ball . y - ball . size < brick . y + brick . h // bottom brick side check
141
+ ) {
142
+ ball . dy *= - 1 ;
143
+ brick . visible = false ;
144
+ increaseScore ( ) ;
145
+ }
146
+ }
147
+ } ) ;
148
+ } ) ;
149
+ // game over
150
+ if ( ball . y + ball . size > canvas . height ) {
151
+ showAllBricks ( ) ;
152
+ score = 0 ;
153
+ }
154
+ }
155
+
156
+ function increaseScore ( ) {
157
+ score ++ ;
158
+ if ( score % ( brickRowCount * brickRowCount ) === 0 ) {
159
+ // no remainder
160
+ showAllBricks ( ) ;
161
+ }
162
+ }
163
+
164
+ function showAllBricks ( ) {
165
+ bricks . forEach ( ( column ) => {
166
+ column . forEach ( ( brick ) => ( brick . visible = true ) ) ;
167
+ } ) ;
168
+ }
169
+
170
+ // Handle Key Events
171
+ function keyDown ( e ) {
172
+ if ( e . key === "Right" || e . key === "ArrowRight" ) paddle . dx = paddle . speed ;
173
+ else if ( e . key === "Left" || e . key === "ArrowLeft" ) paddle . dx = - paddle . speed ;
174
+ }
175
+
176
+ function keyUp ( e ) {
177
+ if (
178
+ e . key === "Right" ||
179
+ e . key === "ArrowRight" ||
180
+ e . key === "Left" ||
181
+ e . key === "ArrowLeft"
182
+ ) {
183
+ paddle . dx = 0 ;
184
+ }
185
+ }
186
+
187
+ // Update Canvas
188
+ function update ( ) {
189
+ // update
190
+ movePaddle ( ) ;
191
+ moveBall ( ) ;
192
+ // draw
193
+ draw ( ) ;
194
+ requestAnimationFrame ( update ) ;
195
+ }
6
196
7
197
// Event Listeners
198
+ document . addEventListener ( "keydown" , keyDown ) ;
199
+ document . addEventListener ( "keyup" , keyUp ) ;
8
200
rulesButton . addEventListener ( "click" , ( ) => rules . classList . add ( "show" ) ) ;
9
201
closeButton . addEventListener ( "click" , ( ) => rules . classList . remove ( "show" ) ) ;
202
+
203
+ // Init
204
+ update ( ) ;
0 commit comments