aboutsummaryrefslogtreecommitdiff
path: root/files/de/web/api/canvas_api/tutorial/basic_animations/index.html
blob: 20c72d98cb7b355808a181d513f1d7c6a8811680 (plain)
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
---
title: Einfache Animationen
slug: Web/API/Canvas_API/Tutorial/Basic_animations
translation_of: Web/API/Canvas_API/Tutorial/Basic_animations
original_slug: Web/Guide/HTML/Canvas_Tutorial/Basic_animations
---
<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}</div>

<div class="summary">
<p>Seitdem wir JavaScript benutzen, um {{HTMLElement("canvas")}}-Elemente zu steuern, ist es auch sehr einfach, interaktive Animationen zu erzeugen. In diesem Kapitel werden wir uns ein paar einfachen Animationen widmen.</p>
</div>

<p>Die wahrscheinlich größte Einschränkung ist, dass jede Form, die einmal gezeichnet wird, genauso bleibt wie anfänglich. Wenn wir sie bewegen wollen, müssen wir sie neuzeichnen. Aber: Auch alle anderen Formen, die wir davor schon gezeichnet haben, müssen auch neu gezeichnet werden! Es beansprucht leider einiges an Zeit, komplexe Figuren neu zu zeichnen.</p>

<h2 id="Basic_animation_steps" name="Basic_animation_steps">Grundlagen der Animation</h2>

<p>Diese Schritte müssen Sie befolgen, um ein neues Frame zu zeichnen:</p>

<ol>
 <li><strong>Bereinigen Sie die Zeichenfläche <em>(canvas)</em></strong><br>
  Sofern die Form, die Sie zeichnen wollen, nicht den gesamten Platz der Zeichenfläche einnimmt, müssen Sie alle vorherigen Formen entfernen. Am einfachsten erreichen Sie dies über die {{domxref("CanvasRenderingContext2D.clearRect", "clearRect()")}}-Methode.</li>
 <li><strong>Sichern Sie den Canvas-Zustand</strong><br>
  Wenn Sie irgendeine Einstellung verändern (wie das Layout, Transformtationen, etc.), die den Status der Zeichenfläche beeinflussen, sollten Sie den Ursprungszustand sichern. Nur so gewährleisten Sie, dass der Ursprungszustand für jedes neue Frame verwendet wird. Verwenden Sie hierfür die <code>save()</code>-Methode.</li>
 <li><strong>Zeichnen Sie die animierte Form</strong><br>
  Hier erzeugen Sie nun endlich die eigentliche Animation.</li>
 <li><strong>Setzen Sie den Canvas-Zustand zurück.</strong><br>
  Mit der <code>restore()</code>-Methode können Sie auf den Ursprungszustand zurückwechseln, um ein neues Frame zu erzeugen.</li>
</ol>

<h2 id="Controlling_an_animation" name="Controlling_an_animation">Steuerung einer Animation</h2>

<p>Formen werden auf eine Zeichenfläche appliziert, indem die entsprechende Canvas-Methode verwendet wird oder eine vorher erzeugte Funktion aufgerufen wird. Im Normalfall erscheint die Canvas-Zeichnung erst, nachdem das Skript vollständig ausgeführt wurde. So ist es nicht möglich, eine Animation durch eine <code>for</code>-Schleife zu erzeugen.</p>

<p>Das bedeutet nun, dass wir einen Weg finden müssen, die Zeichenfunktion für eine bestimmte Zeitdauer ausführen zu können. Dafür gibt es nun zwei Wege, um eine Animation so zu steuern:</p>

<h3 id="Updates_nach_Zeitplan">Updates nach Zeitplan</h3>

<p>Einerseits gibt es die {{domxref("window.setInterval()")}}-, {{domxref("window.setTimeout()")}}- und {{domxref("window.requestAnimationFrame()")}}-Funktionen, die benutzt werden, um eine bestimmte Funktion nach einer Zeitspanne aufzurufen.</p>

<dl>
 <dt>{{domxref("WindowTimers.setInterval", "setInterval(function, delay)")}}</dt>
 <dd>Ruft die unter <code>function</code> spezifierte Funktion wiederholend nach der in <code>delay</code> (Milisekunden) definierten Zeitspanne auf.</dd>
 <dt>{{domxref("WindowTimers.setTimeout", "setTimeout(function, delay)")}}</dt>
 <dd>Ruft die spezifizierte Funktion nach der unter <code>delay</code> festgelegten Zeitspanne einmalig auf.</dd>
 <dt>{{domxref("Window.requestAnimationFrame()", "requestAnimationFrame(callback)")}}</dt>
 <dd>Tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint.</dd>
</dl>

<p>If you don't want any user interaction you can use the <code>setInterval()</code> function which repeatedly executes the supplied code. If we wanted to make a game, we could use keyboard or mouse events to control the animation and use <code>setTimeout()</code>. By setting {{domxref("EventListener")}}s, we catch any user interaction and execute our animation functions.</p>

<div class="note">
<p>In the examples below, we'll use the {{domxref("window.requestAnimationFrame()")}} method to control the animation. The <code>requestAnimationFrame</code> method provides a smoother and more efficient way for animating by calling the animation frame when the system is ready to paint the frame. The number of callbacks is usually 60 times per second and may be reduced to a lower rate when running in background tabs. For more information about the animation loop, especially for games, see the article <a href="/en-US/docs/Games/Anatomy">Anatomy of a video game</a> in our <a href="/en-US/docs/Games">Game development zone</a>.</p>
</div>

<h2 id="An_animated_solar_system">An animated solar system</h2>

<p>This example animates a small model of our solar system.</p>

<pre class="brush: js">var sun = new Image();
var moon = new Image();
var earth = new Image();
function init() {
  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
  earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
  window.requestAnimationFrame(draw);
}

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  ctx.globalCompositeOperation = 'destination-over';
  ctx.clearRect(0, 0, 300, 300); // clear canvas

  ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
  ctx.strokeStyle = 'rgba(0, 153, 255, 0.4)';
  ctx.save();
  ctx.translate(150, 150);

  // Earth
  var time = new Date();
  ctx.rotate(((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds());
  ctx.translate(105, 0);
  ctx.fillRect(0, -12, 50, 24); // Shadow
  ctx.drawImage(earth, -12, -12);

  // Moon
  ctx.save();
  ctx.rotate(((2 * Math.PI) / 6) * time.getSeconds() + ((2 * Math.PI) / 6000) * time.getMilliseconds());
  ctx.translate(0, 28.5);
  ctx.drawImage(moon, -3.5, -3.5);
  ctx.restore();

  ctx.restore();

  ctx.beginPath();
  ctx.arc(150, 150, 105, 0, Math.PI * 2, false); // Earth orbit
  ctx.stroke();

  ctx.drawImage(sun, 0, 0, 300, 300);

  window.requestAnimationFrame(draw);
}

init();
</pre>

<div class="hidden">
<pre class="brush: html">&lt;canvas id="canvas" width="300" height="300"&gt;&lt;/canvas&gt;</pre>
</div>

<p>{{EmbedLiveSample("An_animated_solar_system", "310", "310", "https://mdn.mozillademos.org/files/202/Canvas_animation1.png")}}</p>

<h2 id="An_animated_clock">An animated clock</h2>

<p>This example draws an animated clock, showing your current time.</p>

<pre class="brush: js">function clock() {
  var now = new Date();
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.save();
  ctx.clearRect(0, 0, 150, 150);
  ctx.translate(75, 75);
  ctx.scale(0.4, 0.4);
  ctx.rotate(-Math.PI / 2);
  ctx.strokeStyle = 'black';
  ctx.fillStyle = 'white';
  ctx.lineWidth = 8;
  ctx.lineCap = 'round';

  // Hour marks
  ctx.save();
  for (var i = 0; i &lt; 12; i++) {
    ctx.beginPath();
    ctx.rotate(Math.PI / 6);
    ctx.moveTo(100, 0);
    ctx.lineTo(120, 0);
    ctx.stroke();
  }
  ctx.restore();

  // Minute marks
  ctx.save();
  ctx.lineWidth = 5;
  for (i = 0; i &lt; 60; i++) {
    if (i % 5!= 0) {
      ctx.beginPath();
      ctx.moveTo(117, 0);
      ctx.lineTo(120, 0);
      ctx.stroke();
    }
    ctx.rotate(Math.PI / 30);
  }
  ctx.restore();

  var sec = now.getSeconds();
  var min = now.getMinutes();
  var hr  = now.getHours();
  hr = hr &gt;= 12 ? hr - 12 : hr;

  ctx.fillStyle = 'black';

  // write Hours
  ctx.save();
  ctx.rotate(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) *sec);
  ctx.lineWidth = 14;
  ctx.beginPath();
  ctx.moveTo(-20, 0);
  ctx.lineTo(80, 0);
  ctx.stroke();
  ctx.restore();

  // write Minutes
  ctx.save();
  ctx.rotate((Math.PI / 30) * min + (Math.PI / 1800) * sec);
  ctx.lineWidth = 10;
  ctx.beginPath();
  ctx.moveTo(-28, 0);
  ctx.lineTo(112, 0);
  ctx.stroke();
  ctx.restore();

  // Write seconds
  ctx.save();
  ctx.rotate(sec * Math.PI / 30);
  ctx.strokeStyle = '#D40000';
  ctx.fillStyle = '#D40000';
  ctx.lineWidth = 6;
  ctx.beginPath();
  ctx.moveTo(-30, 0);
  ctx.lineTo(83, 0);
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(0, 0, 10, 0, Math.PI * 2, true);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(95, 0, 10, 0, Math.PI * 2, true);
  ctx.stroke();
  ctx.fillStyle = 'rgba(0, 0, 0, 0)';
  ctx.arc(0, 0, 3, 0, Math.PI * 2, true);
  ctx.fill();
  ctx.restore();

  ctx.beginPath();
  ctx.lineWidth = 14;
  ctx.strokeStyle = '#325FA2';
  ctx.arc(0, 0, 142, 0, Math.PI * 2, true);
  ctx.stroke();

  ctx.restore();

  window.requestAnimationFrame(clock);
}

window.requestAnimationFrame(clock);</pre>

<div class="hidden">
<pre class="brush: html">&lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;</pre>
</div>

<p>{{EmbedLiveSample("An_animated_clock", "180", "180", "https://mdn.mozillademos.org/files/203/Canvas_animation2.png")}}</p>

<h2 id="A_looping_panorama">A looping panorama</h2>

<p>In this example, a panorama is scrolled left-to-right. We're using <a href="http://commons.wikimedia.org/wiki/File:Capitan_Meadows,_Yosemite_National_Park.jpg">an image of Yosemite National Park</a> we took from Wikipedia, but you could use any image that's larger than the canvas.</p>

<pre class="brush: js">var img = new Image();

// User Variables - customize these to change the image being scrolled, its
// direction, and the speed.

img.src = 'https://mdn.mozillademos.org/files/4553/Capitan_Meadows,_Yosemite_National_Park.jpg';
var CanvasXSize = 800;
var CanvasYSize = 200;
var speed = 30; // lower is faster
var scale = 1.05;
var y = -4.5; // vertical offset

// Main program

var dx = 0.75;
var imgW;
var imgH;
var x = 0;
var clearX;
var clearY;
var ctx;

img.onload = function() {
    imgW = img.width * scale;
    imgH = img.height * scale;

    if (imgW &gt; CanvasXSize) { x = CanvasXSize - imgW; } // image larger than canvas
    if (imgW &gt; CanvasXSize) { clearX = imgW; } // image width larger than canvas
    else { clearX = CanvasXSize; }
    if (imgH &gt; CanvasYSize) { clearY = imgH; } // image height larger than canvas
    else { clearY = CanvasYSize; }

    // get canvas context
    ctx = document.getElementById('canvas').getContext('2d');

    // set refresh rate
    return setInterval(draw, speed);
}

function draw() {
    ctx.clearRect(0, 0, clearX, clearY); // clear the canvas

    // if image is &lt;= Canvas Size
    if (imgW &lt;= CanvasXSize) {
        // reset, start from beginning
        if (x &gt; CanvasXSize) { x = -imgW + x; }
        // draw additional image1
        if (x &gt; 0) { ctx.drawImage(img, -imgW + x, y, imgW, imgH); }
        // draw additional image2
        if (x - imgW &gt; 0) { ctx.drawImage(img, -imgW * 2 + x, y, imgW, imgH); }
    }

    // if image is &gt; Canvas Size
    else {
        // reset, start from beginning
        if (x &gt; (CanvasXSize)) { x = CanvasXSize - imgW; }
        // draw aditional image
        if (x &gt; (CanvasXSize-imgW)) { ctx.drawImage(img, x - imgW + 1, y, imgW, imgH); }
    }
    // draw image
    ctx.drawImage(img, x, y,imgW, imgH);
    // amount to move
    x += dx;
}
</pre>

<p>Below is the {{HTMLElement("canvas")}} in which the image is scrolled. Note that the width and height specified here must match the values of the <code>CanvasXZSize</code> and <code>CanvasYSize</code> variables in the JavaScript code.</p>

<pre class="brush: html">&lt;canvas id="canvas" width="800" height="200"&gt;&lt;/canvas&gt;</pre>

<p>{{EmbedLiveSample("A_looping_panorama", "830", "230")}}</p>

<h2 id="Other_examples" name="Other_examples">Other examples</h2>

<dl>
 <dt><a href="/en-US/docs/Web/API/Canvas_API/A_basic_ray-caster" title="/en-US/docs/Web/Guide/HTML/A_basic_ray-caster">A basic ray-caster</a></dt>
 <dd>A good example of how to do animations using keyboard controls.</dd>
 <dt><a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Advanced_animations">Advanced animations</a></dt>
 <dd>We will have a look at some advanced animation techniques and physics in the next chapter.</dd>
</dl>

<p>{{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}</p>