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
|
---
title: Prática de construção de objetos
slug: Aprender/JavaScript/Objetos/Object_building_practice
translation_of: Learn/JavaScript/Objects/Object_building_practice
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}</div>
<p class="summary">Nos artigos anteriores, analisamos todos os detalhes essenciais da teoria e da sintaxe do objeto JavaScript, fornecendo uma base sólida para começar. Neste artigo, vamos mergulhar em um exercício prático, dando a você mais prática na construção de objetos JavaScript personalizados, com um resultado divertido e colorido.</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">Prerequisites:</th>
<td>Conhecimentos básicos de informática, conhecimento básico de HTML e CSS, familiaridade com o básico de JavaScript (veja <a href="/en-US/docs/Learn/JavaScript/First_steps">First steps</a> e <a href="/en-US/docs/Learn/JavaScript/Building_blocks">Building blocks</a>) e o básico de OOJS (veja <a href="/en-US/docs/Learn/JavaScript/Object-oriented/Introduction">Introduction to objects</a>).</td>
</tr>
<tr>
<th scope="row">Objective:</th>
<td>Adiquirir alguma prática no uso de objetos e técnicas de orientação a objetos num contexto real.</td>
</tr>
</tbody>
</table>
<h2 id="Vamos_saltitar_algumas_bolas">Vamos saltitar algumas bolas</h2>
<p>Neste artigo, vamos escrever uma demo clássica de "bolas saltitantes", para mostrar o quão úteis os objetos podem ser em JavaScript. Nossas bolinhas vão saltar pela tela e mudam de cor quando se tocam. O exemplo acabado vai parecer um pouco assim:<img alt="" src="https://mdn.mozillademos.org/files/13865/bouncing-balls.png" style="display: block; height: 614px; margin: 0px auto; width: 800px;"></p>
<ol>
</ol>
<p>Este exemplo fará uso da <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Canvas API</a>, para desenhar as bolas na tela, e da <a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame</a> API para animar toda a exibição — você não precisa ter nenhum conhecimento prévio dessas APIs e esperamos que, no momento em que você terminar este artigo, você esteja interessado em explorá-los mais. Ao longo do caminho, faremos uso de alguns objetos bacanas, e mostraremos algumas técnicas legais, como bolas quicando nas paredes, e verificando se elas se chocaram (também conhecidas como <strong>detecção de colisão</strong>).</p>
<h2 id="Começando">Começando</h2>
<p>Para começar, faça cópias locais de nossos arquivos <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/index.html">index.html</a></code>, <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/style.css">style.css</a></code>, e <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/main.js">main.js</a></code>. Estes contêm o seguinte, respectivamente:</p>
<ol>
<li>Um documento HTML muito simples com um elemento {{HTMLElement("h1")}}, um elemento {{HTMLElement("canvas")}} para desenhar nossas bolas e elementos para aplicar nosso CSS e JavaScript em nosso HTML.</li>
<li>Alguns estilos muito simples, que servem principalmente para estilizar e posicionar o <code><h1></code>, e se livrar de qualquer barra de rolagem ou margem ao redor da borda da página (para que fique bonito e arrumado).</li>
<li>Algum JavaScript que serve para configurar o elemento <code><canvas></code> e fornecer uma função geral que vamos usar.</li>
</ol>
<p>A primeira parte do script é assim:</p>
<pre class="brush: js">const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;</pre>
<p>Esse script obtém uma referência ao elemento <code><canvas></code> e, em seguida, chama o método <code><a href="/en-US/docs/Web/API/HTMLCanvasElement/getContext">getContext()</a></code> para nos fornecer um contexto no qual podemos começar a desenhar. A variável resultante (<code>ctx</code>) é o objeto que representa diretamente a área de desenho da tela e nos permite desenhar formas 2D nela.</p>
<p>Em seguida, definimos variáveis chamadas <code>width</code> e <code>height</code>, e a largura e altura do elemento canvas (representado pelas propriedades <code>canvas.width</code> e <code>canvas.height</code>) para igualar a largura e a altura da viewport do navegador (a área em que a página da Web aparece — isso pode ser obtido das propriedades {{domxref("Window.innerWidth")}} e {{domxref("Window.innerHeight")}} ).</p>
<p>Você verá aqui que estamos encadeando várias tarefas juntas, para que as variáveis sejam todas mais rápidas — isso é perfeitamente aceitável.</p>
<p>A última parte do script inicial é a seguinte:</p>
<pre class="brush: js">function random(min, max) {
const num = Math.floor(Math.random() * (max - min + 1)) + min;
return num;
}</pre>
<p>Essa função usa dois números como argumentos e retorna um número aleatório no intervalo entre os dois.</p>
<h2 id="Modelando_uma_bola_no_nosso_programa">Modelando uma bola no nosso programa</h2>
<p>Nosso programa contará com muitas bolas saltando ao redor da tela. Como todas essas bolas se comportarão da mesma maneira, faz sentido representá-las com um objeto. Vamos começar adicionando o construtor a seguir ao final do código.</p>
<pre class="brush: js">function Ball(x, y, velX, velY, color, size) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.color = color;
this.size = size;
}</pre>
<p>Aqui incluímos alguns parâmetros que definem as propriedades que cada bola precisa para funcionar em nosso programa:</p>
<ul>
<li>coordenadas <code>x</code> e <code>y</code> — coordenadas horizontal e vertical onde a bola vai começar na tela. Isso pode variar entre 0 (canto superior esquerdo) à largura e altura da janela de visualização do navegador (canto inferior direito).</li>
<li>velocidade horizontal e vertical (<code>velX</code> e <code>velY</code>) — cada bola recebe uma velocidade horizontal e vertical; em termos reais, esses valores serão adicionados regularmente aos valores das coordenadas <code>x</code>/<code>y</code> quando começarmos a animar as bolas, para movê-las tanto em cada quadro.</li>
<li><code>color</code> — cada bola recebe uma cor.</li>
<li><code>size</code> — cada bola recebe um tamanho — este será o seu raio, em pixels.</li>
</ul>
<p>Isso classifica as propriedades, mas e os métodos? Queremos realmente fazer com que nossas bolas façam algo em nosso programa.</p>
<h3 id="Desenhando_a_bola">Desenhando a bola</h3>
<p>Primeiro adicione o seguinte método <code>draw()</code> ao <code>prototype</code> do <code>Ball()</code>:</p>
<pre class="brush: js">Ball.prototype.draw = function() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
}</pre>
<p>Usando esta função, podemos dizer a nossa bola para desenhar-se na tela, chamando uma série de membros do contexto de tela 2D que definimos anteriormente (<code>ctx</code>). O contexto é como o papel, e agora queremos comandar nossa caneta para desenhar algo nela:</p>
<ul>
<li>Primeiro, usamos <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath">beginPath()</a></code> para declarar que queremos desenhar uma forma no papel.</li>
<li>Em seguida, usamos <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle">fillStyle</a></code> para definir a cor que queremos que a forma seja — nós a definimos como a propriedade <code>color</code> da nossa bola.</li>
<li>Em seguida, usamos o método <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/arc">arc()</a></code> para traçar uma forma de arco no papel. Seus parâmetros são:
<ul>
<li>A posição <code>x</code> e <code>y</code> do centro do arco — estamos especificando as propriedades <code>x</code> e <code>y</code> da nossa bola.</li>
<li>O raio do nosso arco — estamos especificando a propriedade <code>size</code> da nossa bola.</li>
<li>Os dois últimos parâmetros especificam o número inicial e final de graus em volta do círculo em que o arco é desenhado entre eles. Aqui nós especificamos 0 graus e <code>2 * PI</code>, que é o equivalente a 360 graus em radianos (irritantemente, você tem que especificar isso em radianos). Isso nos dá um círculo completo. Se você tivesse especificado apenas <code>1 * PI</code>, você obteria um semicírculo (180 graus).</li>
</ul>
</li>
<li>Por último, usamos o método <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fill">fill()</a></code>, que basicamente diz "terminar de desenhar o caminho que começamos com <code>beginPath()</code>, e preencher a área que ocupa com a cor que especificamos anteriormente em <code>fillStyle</code>."</li>
</ul>
<p>Você pode começar a testar seu objeto já.</p>
<ol>
<li>Salve o código até o momento e carregue o arquivo HTML em um navegador.</li>
<li>Abra o console JavaScript do navegador e, em seguida, atualize a página para que o tamanho da tela mude para a viewport menor visível deixada quando o console for aberto.</li>
<li>Digite o seguinte para criar uma nova instância de bola:
<pre class="brush: js">let testBall = new Ball(50, 100, 4, 4, 'blue', 10);</pre>
</li>
<li>Tente chamar seus membros:
<pre class="brush: js">testBall.x
testBall.size
testBall.color
testBall.draw()</pre>
</li>
<li>Quando você entra na última linha, você deve ver a bola se desenhando em algum lugar na sua tela.</li>
</ol>
<h3 id="Atualizando_os_dados_da_bola">Atualizando os dados da bola</h3>
<p>Podemos desenhar a bola na posição, mas para começar a mover a bola, precisamos de uma função de atualização de algum tipo. Adicione o seguinte código na parte inferior do seu arquivo JavaScript, para adicionar um método <code>update()</code> ao <code>prototype</code> do <code>Ball()</code>:</p>
<pre class="brush: js">Ball.prototype.update = function() {
if ((this.x + this.size) >= width) {
this.velX = -(this.velX);
}
if ((this.x - this.size) <= 0) {
this.velX = -(this.velX);
}
if ((this.y + this.size) >= height) {
this.velY = -(this.velY);
}
if ((this.y - this.size) <= 0) {
this.velY = -(this.velY);
}
this.x += this.velX;
this.y += this.velY;
}</pre>
<p>As primeiras quatro partes da função verificam se a bola atingiu a borda da tela. Se tiver, invertemos a polaridade da velocidade relevante para fazer a bola viajar na direção oposta. Assim, por exemplo, se a bola estava viajando para cima (positivo <code>velY</code>), então a velocidade vertical é alterada de forma que ela comece a viajar para baixo (negativo <code>velY</code>).</p>
<p>Nos quatro casos, estamos verificando se:</p>
<ul>
<li>Se a coordenada <code>x</code> é maior que a largura da tela (a bola está saindo da borda direita).</li>
<li>Se a coordenada <code>x</code> é menor que 0 (a bola está saindo da borda esquerda).</li>
<li>Se a coordenada <code>y</code> é maior que a altura da tela (a bola está saindo da borda inferior).</li>
<li>Se a coordenada <code>y</code> é menor que 0 (a bola está saindo da borda superior).</li>
</ul>
<p>Em cada caso, estamos incluindo o <code>size</code> da bola no cálculo, porque as coordenadas <code>x</code>/<code>y</code> estão no centro da bola, mas queremos que a borda da bola saia do perímetro — não queremos a bola para fique no meio da tela antes de quicar de volta.</p>
<p>As duas últimas linhas adicionam o valor <code>velX</code> à coordenada <code>x</code>, e o valor <code>velY</code> à coordenada <code>y</code> — a bola é efitivamente movida cada vez que este método é chamado.</p>
<p>Isso é o que será feito por ora; vamos continuar com alguma animação!</p>
<h2 id="Animando_a_bola">Animando a bola</h2>
<p>Agora vamos tornar isso divertido. Vamos começar a adicionar bolas à tela e a animá-las.</p>
<ol>
<li>Primeiro, precisamos de um lugar para armazenar todas as nossas bolas. O array a seguir fará esse trabalho — adicione-o ao final do seu código agora:
<pre class="brush: js">let balls = [];</pre>
<p>Todos os programas que animam as coisas geralmente envolvem um loop de animação, que serve para atualizar as informações no programa e renderizar a visualização resultante em cada quadro da animação; esta é a base para a maioria dos jogos e outros programas.</p>
</li>
<li>Adicione o seguinte ao final do seu código agora:
<pre class="brush: js">function loop() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
ctx.fillRect(0, 0, width, height);
while (balls.length < 25) {
let size = random(10,20);
let ball = new Ball(
// ball position always drawn at least one ball width
// away from the edge of the canvas, to avoid drawing errors
random(0 + size,width - size),
random(0 + size,height - size),
random(-7,7),
random(-7,7),
'rgb(' + random(0,255) + ',' + random(0,255) + ',' + random(0,255) +')',
size
);
balls.push(ball);
}
for (let i = 0; i < balls.length; i++) {
balls[i].draw();
balls[i].update();
}
requestAnimationFrame(loop);
}</pre>
<p>Nossa função <code>loop()</code> faz o seguinte:</p>
<ul>
<li>Define a cor de preenchimento da tela como preto semitransparente e desenha um retângulo com a cor em toda a largura e altura da tela, usando <code>fillRect()</code> (os quatro parâmetros fornecem uma coordenada de início e uma largura e altura para o retângulo desenhado ). Isso serve para encobrir o desenho do quadro anterior antes que o próximo seja desenhado. Se você não fizer isso, você verá apenas longas cobras se movimentando ao redor da tela, em vez de mover as bolas! A cor do preenchimento é definida como semitransparente, <code>rgba(0,0,0,0.25)</code>, para permitir que os poucos quadros anteriores brilhem levemente, produzindo as pequenas trilhas atrás das bolas à medida que elas se movem. Se você mudou 0,25 para 1, você não vai mais vê-los. Tente variar esse número para ver o efeito que ele tem.</li>
<li>Cria uma nova instância de nossa <code>Ball()</code> usando valores aleatórios gerados com a nossa função <code>random()</code> então <code>push()</code>para o final de nosso array de bolas, mas somente enquanto o número de bolas no array é menor que 25. Então quando temos 25 bolas na tela, não aparecem mais bolas. Você pode tentar variar o número em<code>balls.length < 25</code> para obter mais ou menos bolas na tela. Dependendo de quanto poder de processamento seu computador / navegador possui, especificar vários milhares de bolas pode retardar bastante a animação!</li>
<li>Faz um loop em todas as <code>balls</code> no array de bolas e executa a função <code>draw()</code> e <code>update()</code> de cada bola para desenhar cada uma delas na tela, depois faz as atualizações necessárias para a posição e a velocidade no tempo para o próximo quadro.</li>
<li>Executa a função novamente usando o método <code>requestAnimationFrame()</code> — quando esse método é executado constantemente e passa o mesmo nome de função, ele executará essa função um número definido de vezes por segundo para criar uma animação suave. Isso geralmente é feito de forma recursiva — o que significa que a função está chamando a si mesma toda vez que é executada, portanto, ela será executada repetidas vezes.</li>
</ul>
</li>
<li>Por último mas não menos importante, adicione a seguinte linha à parte inferior do seu código — precisamos chamar a função uma vez para iniciar a animação.
<pre class="brush: js">loop();</pre>
</li>
</ol>
<p>É isso para o básico — tente salvar e atualizar para testar suas bolas quicando!</p>
<h2 id="Adicionando_detecção_de_colisão">Adicionando detecção de colisão</h2>
<p>Agora, para um pouco de diversão, vamos adicionar alguma detecção de colisão ao nosso programa, para que nossas bolas saibam quando bateram em outra bola.</p>
<ol>
<li>Primeiro de tudo, adicione a seguinte definição de método abaixo onde você definiu o método <code>update()</code> (ou seja, o bloco <code>Ball.prototype.update</code>).
<pre class="brush: js">Ball.prototype.collisionDetect = function() {
for (let j = 0; j < balls.length; j++) {
if (!(this === balls[j])) {
const dx = this.x - balls[j].x;
const dy = this.y - balls[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.size + balls[j].size) {
balls[j].color = this.color = 'rgb(' + random(0, 255) + ',' + random(0, 255) + ',' + random(0, 255) +')';
}
}
}
}</pre>
<p>Esse método é um pouco complexo, então não se preocupe se você não entender exatamente como isso funciona agora. Uma explicação a seguir:</p>
<ul>
<li>Para cada bola, precisamos checar todas as outras bolas para ver se ela colidiu com a bola atual. Para fazer isso, abrimos outro loop <code>for</code> para percorrer todas as bolas no array <code>balls[]</code>.</li>
<li>Imediatamente dentro de nosso loop for, usamos uma instrução <code>if</code> para verificar se a bola atual em loop é a mesma bola que estamos verificando no momento. Não queremos verificar se uma bola colidiu consigo mesma! Para fazer isso, verificamos se a bola atual (ou seja, a bola cujo método collisionDetect está sendo invocado) é a mesma que a bola de loop (ou seja, a bola que está sendo referenciada pela iteração atual do loop for no collisionDetect método). Nós então usamos <code>!</code> para negar a verificação, para que o código dentro da instrução if seja executado apenas se eles não forem iguais.</li>
<li>Em seguida, usamos um algoritmo comum para verificar a colisão de dois círculos. Estamos basicamente verificando se alguma das áreas dos dois círculos se sobrepõe. Isso é explicado ainda mais na <a href="/en-US/docs/Games/Techniques/2D_collision_detection">2D collision detection</a>.</li>
<li>Se uma colisão for detectada, o código dentro da instrução <code>if</code> interna será executado. Neste caso, estamos apenas definindo a propriedade <code>color</code> de ambos os círculos para uma nova cor aleatória. Poderíamos ter feito algo muito mais complexo, como fazer com que as bolas saltassem umas das outras de forma realista, mas isso teria sido muito mais complexo de implementar. Para essas simulações físicas, os desenvolvedores tendem a usar jogos ou bibliotecas físicas, como <a href="http://wellcaffeinated.net/PhysicsJS/">PhysicsJS</a>, <a href="http://brm.io/matter-js/">matter.js</a>, <a href="http://phaser.io/">Phaser</a>, etc.</li>
</ul>
</li>
<li>Você também precisa chamar esse método em cada quadro da animação. Adicione o seguinte abaixo do <code>balls[i].update();</code>:
<pre class="brush: js">balls[i].collisionDetect();</pre>
</li>
<li>Salve e atualize a demonstração novamente, e você verá suas bolas mudando de cor quando colidirem!</li>
</ol>
<div class="note">
<p><strong>Note</strong>: If you have trouble getting this example to work, try comparing your JavaScript code against our <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/main-finished.js">finished version</a> (also see it <a href="http://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html">running live</a>).</p>
</div>
<h2 id="Sumário">Sumário</h2>
<p>Esperamos que você tenha se divertido escrevendo seu próprio exemplo de bolas saltitantes aleatórias do mundo real, usando várias técnicas orientadas a objetos e objetos de todo o módulo! Isso deve ter lhe dado alguma prática útil no uso de objetos e um bom contexto do mundo real.</p>
<p>É isso para artigos de objetos — tudo o que resta agora é para você testar suas habilidades na avaliação de objetos.</p>
<h2 id="Veja_também">Veja também</h2>
<ul>
<li><a href="/en-US/docs/Web/API/Canvas_API/Tutorial">Canvas tutorial</a> — um guia para iniciantes sobre o uso de telas (canvas) 2D .</li>
<li><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></li>
<li><a href="/en-US/docs/Games/Techniques/2D_collision_detection">2D collision detection</a></li>
<li><a href="/en-US/docs/Games/Techniques/3D_collision_detection">3D collision detection</a></li>
<li><a href="/en-US/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript">2D breakout game using pure JavaScript</a> — um ótimo tutorial para iniciantes que mostra como criar um jogo 2D.</li>
<li><a href="/en-US/docs/Games/Tutorials/2D_breakout_game_Phaser">2D breakout game using Phaser</a> — explica conceitos básicos da criação de um jogo 2D utilizando uma biblioteca JavaScript.</li>
</ul>
<p>{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}</p>
<h2 id="In_this_module">In this module</h2>
<ul>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics">Object basics</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS">Object-oriented JavaScript for beginners</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">Object prototypes</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Inheritance">Inheritance in JavaScript</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON">Working with JSON data</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_building_practice">Object building practice</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">Adding features to our bouncing balls demo</a></li>
</ul>
|