@@ -3,7 +3,202 @@ const closeButton = document.getElementById("close-btn");
33const rules = document . getElementById ( "rules" ) ;
44const canvas = document . getElementById ( "canvas" ) ;
55const 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+ }
6196
7197// Event Listeners
198+ document . addEventListener ( "keydown" , keyDown ) ;
199+ document . addEventListener ( "keyup" , keyUp ) ;
8200rulesButton . addEventListener ( "click" , ( ) => rules . classList . add ( "show" ) ) ;
9201closeButton . addEventListener ( "click" , ( ) => rules . classList . remove ( "show" ) ) ;
202+
203+ // Init
204+ update ( ) ;
0 commit comments