From 4ab365b110f2f1f2b736326b7059244a32115089 Mon Sep 17 00:00:00 2001 From: Florian Merz Date: Thu, 11 Feb 2021 14:45:38 +0100 Subject: unslug de: move --- .../api/audiocontext/decodeaudiodata/index.html | 218 ---- .../baseaudiocontext/decodeaudiodata/index.html | 218 ++++ files/de/web/api/canvas_api/index.html | 120 ++ .../tutorial/advanced_animations/index.html | 380 +++++++ .../tutorial/applying_styles_and_colors/index.html | 785 +++++++++++++ .../tutorial/basic_animations/index.html | 307 +++++ .../api/canvas_api/tutorial/basic_usage/index.html | 154 +++ .../canvas_api/tutorial/drawing_shapes/index.html | 453 ++++++++ .../canvas_api/tutorial/drawing_text/index.html | 165 +++ files/de/web/api/canvas_api/tutorial/index.html | 51 + .../tutorial/optimizing_canvas/index.html | 118 ++ .../canvas_api/tutorial/using_images/index.html | 324 ++++++ .../api/document/readystatechange_event/index.html | 86 ++ files/de/web/api/document_object_model/index.html | 66 ++ files/de/web/api/file/typ/index.html | 65 -- files/de/web/api/file/type/index.html | 65 ++ .../using_files_from_web_applications/index.html | 512 +++++++++ .../index.html | 512 --------- files/de/web/api/fullscreen_api/index.html | 305 +++++ files/de/web/api/geolocation_api/index.html | 291 +++++ files/de/web/api/history_api/index.html | 227 ++++ files/de/web/api/html_drag_and_drop_api/index.html | 11 + .../de/web/api/htmlelement/change_event/index.html | 109 ++ files/de/web/api/htmlelement/innertext/index.html | 90 ++ files/de/web/api/htmlheadelement/index.html | 28 + .../basic_concepts_behind_indexeddb/index.html | 194 ++++ .../grundkonzepte_hinter_indexeddb/index.html | 194 ---- .../indexeddb_api/indexeddb_verwenden/index.html | 1180 -------------------- .../api/indexeddb_api/using_indexeddb/index.html | 1180 ++++++++++++++++++++ .../web-based_protocol_handlers/index.html | 127 +++ .../webbasierte_protokoll-handler/index.html | 127 --- files/de/web/api/node/innertext/index.html | 90 -- files/de/web/api/vollbild_api/index.html | 305 ----- .../3d-objekte_mit_webgl_erstellen/index.html | 126 --- .../index.html | 238 ++++ .../animating_objects_with_webgl/index.html | 118 ++ .../animating_textures_in_webgl/index.html | 89 ++ .../animierte_texturen_in_webgl/index.html | 89 -- .../tutorial/beleuchtung_in_webgl/index.html | 172 --- .../creating_3d_objects_using_webgl/index.html | 126 +++ .../einf\303\274hrung_in_webgl/index.html" | 73 -- .../index.html" | 97 -- .../tutorial/getting_started_with_webgl/index.html | 73 ++ .../index.html" | 238 ---- .../tutorial/lighting_in_webgl/index.html | 172 +++ .../objekte_mit_webgl_animieren/index.html | 118 -- .../texturen_in_webgl_verwenden/index.html | 159 --- .../index.html | 97 ++ .../tutorial/using_textures_in_webgl/index.html | 159 +++ files/de/web/api/websockets_api/index.html | 193 ++++ .../writing_websocket_servers/index.html | 250 +++++ .../api/window/domcontentloaded_event/index.html | 156 +++ files/de/web/api/window/load_event/index.html | 88 ++ files/de/web/api/windowbase64/btoa/index.html | 145 --- files/de/web/api/windowbase64/index.html | 121 -- .../api/windoworworkerglobalscope/btoa/index.html | 145 +++ .../web/api/windoworworkerglobalscope/index.html | 121 ++ .../settimeout/index.html | 329 ++++++ files/de/web/api/windowtimers/index.html | 125 --- .../de/web/api/windowtimers/settimeout/index.html | 329 ------ 60 files changed, 8720 insertions(+), 4483 deletions(-) delete mode 100644 files/de/web/api/audiocontext/decodeaudiodata/index.html create mode 100644 files/de/web/api/baseaudiocontext/decodeaudiodata/index.html create mode 100644 files/de/web/api/canvas_api/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/advanced_animations/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/basic_animations/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/basic_usage/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/drawing_shapes/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/drawing_text/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/optimizing_canvas/index.html create mode 100644 files/de/web/api/canvas_api/tutorial/using_images/index.html create mode 100644 files/de/web/api/document/readystatechange_event/index.html create mode 100644 files/de/web/api/document_object_model/index.html delete mode 100644 files/de/web/api/file/typ/index.html create mode 100644 files/de/web/api/file/type/index.html create mode 100644 files/de/web/api/file/using_files_from_web_applications/index.html delete mode 100644 files/de/web/api/file/zugriff_auf_dateien_von_webapplikationen/index.html create mode 100644 files/de/web/api/fullscreen_api/index.html create mode 100644 files/de/web/api/geolocation_api/index.html create mode 100644 files/de/web/api/history_api/index.html create mode 100644 files/de/web/api/html_drag_and_drop_api/index.html create mode 100644 files/de/web/api/htmlelement/change_event/index.html create mode 100644 files/de/web/api/htmlelement/innertext/index.html create mode 100644 files/de/web/api/htmlheadelement/index.html create mode 100644 files/de/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html delete mode 100644 files/de/web/api/indexeddb_api/grundkonzepte_hinter_indexeddb/index.html delete mode 100644 files/de/web/api/indexeddb_api/indexeddb_verwenden/index.html create mode 100644 files/de/web/api/indexeddb_api/using_indexeddb/index.html create mode 100644 files/de/web/api/navigator/registerprotocolhandler/web-based_protocol_handlers/index.html delete mode 100644 files/de/web/api/navigator/registerprotocolhandler/webbasierte_protokoll-handler/index.html delete mode 100644 files/de/web/api/node/innertext/index.html delete mode 100644 files/de/web/api/vollbild_api/index.html delete mode 100644 files/de/web/api/webgl_api/tutorial/3d-objekte_mit_webgl_erstellen/index.html create mode 100644 files/de/web/api/webgl_api/tutorial/adding_2d_content_to_a_webgl_context/index.html create mode 100644 files/de/web/api/webgl_api/tutorial/animating_objects_with_webgl/index.html create mode 100644 files/de/web/api/webgl_api/tutorial/animating_textures_in_webgl/index.html delete mode 100644 files/de/web/api/webgl_api/tutorial/animierte_texturen_in_webgl/index.html delete mode 100644 files/de/web/api/webgl_api/tutorial/beleuchtung_in_webgl/index.html create mode 100644 files/de/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html delete mode 100644 "files/de/web/api/webgl_api/tutorial/einf\303\274hrung_in_webgl/index.html" delete mode 100644 "files/de/web/api/webgl_api/tutorial/farben_mittels_shader_in_einen_webgl-kontext_hinzuf\303\274gen/index.html" create mode 100644 files/de/web/api/webgl_api/tutorial/getting_started_with_webgl/index.html delete mode 100644 "files/de/web/api/webgl_api/tutorial/hinzuf\303\274gen_von_2d_inhalten_in_einen_webgl-kontext/index.html" create mode 100644 files/de/web/api/webgl_api/tutorial/lighting_in_webgl/index.html delete mode 100644 files/de/web/api/webgl_api/tutorial/objekte_mit_webgl_animieren/index.html delete mode 100644 files/de/web/api/webgl_api/tutorial/texturen_in_webgl_verwenden/index.html create mode 100644 files/de/web/api/webgl_api/tutorial/using_shaders_to_apply_color_in_webgl/index.html create mode 100644 files/de/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html create mode 100644 files/de/web/api/websockets_api/index.html create mode 100644 files/de/web/api/websockets_api/writing_websocket_servers/index.html create mode 100644 files/de/web/api/window/domcontentloaded_event/index.html create mode 100644 files/de/web/api/window/load_event/index.html delete mode 100644 files/de/web/api/windowbase64/btoa/index.html delete mode 100644 files/de/web/api/windowbase64/index.html create mode 100644 files/de/web/api/windoworworkerglobalscope/btoa/index.html create mode 100644 files/de/web/api/windoworworkerglobalscope/index.html create mode 100644 files/de/web/api/windoworworkerglobalscope/settimeout/index.html delete mode 100644 files/de/web/api/windowtimers/index.html delete mode 100644 files/de/web/api/windowtimers/settimeout/index.html (limited to 'files/de/web/api') diff --git a/files/de/web/api/audiocontext/decodeaudiodata/index.html b/files/de/web/api/audiocontext/decodeaudiodata/index.html deleted file mode 100644 index 32cfda28eb..0000000000 --- a/files/de/web/api/audiocontext/decodeaudiodata/index.html +++ /dev/null @@ -1,218 +0,0 @@ ---- -title: AudioContext.decodeAudioData() -slug: Web/API/AudioContext/decodeAudioData -translation_of: Web/API/BaseAudioContext/decodeAudioData ---- -

{{ APIRef("Web Audio API") }}

- -
-

Die Methode decodeAudioData() im Interface {{ domxref("AudioContext") }} wird dazu benutzt um in einem {{ domxref("ArrayBuffer")}} enthaltene Audiodaten asynchron zu dekodieren. Im Normalfall wird der ArrayBuffer mit der  {{domxref("XMLHttpRequest")}} response Eigenschaft befüllt, nachdem der responseType auf arraybuffer gesetzt wurde. Der dekodierte AudioBuffer wird auf die Samplerate des AudioContextes angepasst und anschließend an ein Callback oder Promise weitergegeben.

-
- -

Dies ist die bevorzugte Methode im eine Audioquelle für die Web Audio API aus einem Audiotrack zu generieren.

- -

Syntax

- -

Alte Callbacksyntax:

- -
audioCtx.decodeAudioData(audioData, function(decodedData) {
-  // use the dec​oded data here
-});
- -

Neue Promise basierte Syntax:

- -
audioCtx.decodeAudioData(audioData).then(function(decodedData) {
-  // use the decoded data here
-});
- -

Beispiel

- -

In diesem Abschnitt wird zuerst die ältere Callback basierte Syntax und anschließend die neue Promise basierte Syntax behandelt.

- -

Ältere Callbacksyntax

- -

In diesem Beispiel nutzt die getData() Funktion XHR um einen Audiotrack zu laden, indem sie die responseType Eigenschaft setzt um einen ArrayBuffer als Antwort zu erhalten, welcher anschließend in der audioData variable gespeichert wird. Dieser Buffer wird nun an die decodeAudioData() Funktion übergeben; Das success Callback nutzt die erfolgreich dekodierten PCM Daten, erstellt einen {{ domxref("AudioBufferSourceNode") }} mit Hilfe von {{ domxref("AudioContext.createBufferSource()") }}, verbindet diese Quelle mit {{domxref("AudioContext.destination") }} und setzt den Schleifenmodus.

- -

Die Buttons in diesem Beispiel rufen lediglich getData() um die Daten zu laden und die Wiedergabe zu starten, sowie stop() um die Wiedergabe anzuhalten auf. Wenn die stop() Methode der Quelle aufgerufen wird die Quelle geleert.

- -
-

Note: You can run the example live (or view the source.)

-
- -
// Variablen definieren
-
-var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
-var source;
-
-var pre = document.querySelector('pre');
-var myScript = document.querySelector('script');
-var play = document.querySelector('.play');
-var stop = document.querySelector('.stop');
-
-// Benutzt XHR um einen Track zu laden und
-// decodeAudioData um diesen zu dekodieren und in einen Buffer zu kopieren.
-// Anschließend wird der Buffer in eine Quelle kopiert.
-
-function getData() {
-  source = audioCtx.createBufferSource();
-  var request = new XMLHttpRequest();
-
-  request.open('GET', 'viper.ogg', true);
-
-  request.responseType = 'arraybuffer';
-
-
-  request.onload = function() {
-    var audioData = request.response;
-
-    audioCtx.decodeAudioData(audioData, function(buffer) {
-        source.buffer = buffer;
-
-        source.connect(audioCtx.destination);
-        source.loop = true;
-      },
-
-      function(e){"Error with decoding audio data" + e.err});
-
-  }
-
-  request.send();
-}
-
-// Buttons setzen um Wiedergabe zu starten und stoppen
-
-play.onclick = function() {
-  getData();
-  source.start(0);
-  play.setAttribute('disabled', 'disabled');
-}
-
-stop.onclick = function() {
-  source.stop(0);
-  play.removeAttribute('disabled');
-}
-
-
-// Script ausgeben
-
-pre.innerHTML = myScript.innerHTML;
- -

Neue Promise basierte Syntax

- -
ctx.decodeAudioData(compressedBuffer).then(function(decodedData) {
- // Die dekodierten PCM Daten können hier verwendet werden
-});
- -

Parameter

- -
-
ArrayBuffer
-
Ein ArrayBuffer der die zu dekodierenden Daten enthält. Dieser wird normalerweise durch einen {{domxref("XMLHttpRequest")}} befüllt nachdem der responseType auf arraybuffer gesetzt wurde.
-
DecodeSuccessCallback
-
Ein Callback das aufgerufen wird wenn die Dekodierung erfolgreich abgeschlossen wird. Der einzige Parameter der an diese Funktion übergeben wird stellt einen AudioBuffer dar, der die dekodierten Audiodaten enthält. Normalerweise wird dieser Buffer an einen {{domxref("AudioBufferSourceNode")}} übergeben, von wo er wiedergeben und verändert werden kann.
-
DecodeErrorCallback
-
Ein optionales Callback das bei einem Fehler während der Dekodierung aufgerufen wird.
-
- -

Rückgabewerte

- -

Ein {{domxref("AudioBuffer") }} der die dekodierten Audiodaten enthällt.

- -

Spezifikationen

- - - - - - - - - - - - - - -
SpezifikationStatusKommentar
{{SpecName('Web Audio API', '#widl-AudioContext-decodeAudioData-Promise-AudioBuffer--ArrayBuffer-audioData-DecodeSuccessCallback-successCallback-DecodeErrorCallback-errorCallback', 'decodeAudioData()')}}{{Spec2('Web Audio API')}} 
- -

Browserunterstützung

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatChrome(10.0)}}{{property_prefix("webkit")}}{{CompatGeckoDesktop(25.0)}} {{CompatNo}}15.0{{property_prefix("webkit")}}
- 22 (unprefixed)
6.0{{property_prefix("webkit")}}
Promise-based syntax{{CompatChrome(49.0)}}{{CompatVersionUnknown}}{{CompatNo}}{{CompatVersionUnknown}}{{CompatNo}}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureAndroidAndroid WebviewFirefox Mobile (Gecko)Firefox OSIE MobileOpera MobileSafari MobileChrome for Android
Basic support{{CompatUnknown}}{{CompatVersionUnknown}}26.01.2{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatChrome(33.0)}}
Promise-based syntax{{CompatUnknown}}{{CompatChrome(49.0)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatNo}}{{CompatUnknown}}{{CompatUnknown}}{{CompatChrome(49.0)}}
-
- -

Siehe auch

- - diff --git a/files/de/web/api/baseaudiocontext/decodeaudiodata/index.html b/files/de/web/api/baseaudiocontext/decodeaudiodata/index.html new file mode 100644 index 0000000000..32cfda28eb --- /dev/null +++ b/files/de/web/api/baseaudiocontext/decodeaudiodata/index.html @@ -0,0 +1,218 @@ +--- +title: AudioContext.decodeAudioData() +slug: Web/API/AudioContext/decodeAudioData +translation_of: Web/API/BaseAudioContext/decodeAudioData +--- +

{{ APIRef("Web Audio API") }}

+ +
+

Die Methode decodeAudioData() im Interface {{ domxref("AudioContext") }} wird dazu benutzt um in einem {{ domxref("ArrayBuffer")}} enthaltene Audiodaten asynchron zu dekodieren. Im Normalfall wird der ArrayBuffer mit der  {{domxref("XMLHttpRequest")}} response Eigenschaft befüllt, nachdem der responseType auf arraybuffer gesetzt wurde. Der dekodierte AudioBuffer wird auf die Samplerate des AudioContextes angepasst und anschließend an ein Callback oder Promise weitergegeben.

+
+ +

Dies ist die bevorzugte Methode im eine Audioquelle für die Web Audio API aus einem Audiotrack zu generieren.

+ +

Syntax

+ +

Alte Callbacksyntax:

+ +
audioCtx.decodeAudioData(audioData, function(decodedData) {
+  // use the dec​oded data here
+});
+ +

Neue Promise basierte Syntax:

+ +
audioCtx.decodeAudioData(audioData).then(function(decodedData) {
+  // use the decoded data here
+});
+ +

Beispiel

+ +

In diesem Abschnitt wird zuerst die ältere Callback basierte Syntax und anschließend die neue Promise basierte Syntax behandelt.

+ +

Ältere Callbacksyntax

+ +

In diesem Beispiel nutzt die getData() Funktion XHR um einen Audiotrack zu laden, indem sie die responseType Eigenschaft setzt um einen ArrayBuffer als Antwort zu erhalten, welcher anschließend in der audioData variable gespeichert wird. Dieser Buffer wird nun an die decodeAudioData() Funktion übergeben; Das success Callback nutzt die erfolgreich dekodierten PCM Daten, erstellt einen {{ domxref("AudioBufferSourceNode") }} mit Hilfe von {{ domxref("AudioContext.createBufferSource()") }}, verbindet diese Quelle mit {{domxref("AudioContext.destination") }} und setzt den Schleifenmodus.

+ +

Die Buttons in diesem Beispiel rufen lediglich getData() um die Daten zu laden und die Wiedergabe zu starten, sowie stop() um die Wiedergabe anzuhalten auf. Wenn die stop() Methode der Quelle aufgerufen wird die Quelle geleert.

+ +
+

Note: You can run the example live (or view the source.)

+
+ +
// Variablen definieren
+
+var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+var source;
+
+var pre = document.querySelector('pre');
+var myScript = document.querySelector('script');
+var play = document.querySelector('.play');
+var stop = document.querySelector('.stop');
+
+// Benutzt XHR um einen Track zu laden und
+// decodeAudioData um diesen zu dekodieren und in einen Buffer zu kopieren.
+// Anschließend wird der Buffer in eine Quelle kopiert.
+
+function getData() {
+  source = audioCtx.createBufferSource();
+  var request = new XMLHttpRequest();
+
+  request.open('GET', 'viper.ogg', true);
+
+  request.responseType = 'arraybuffer';
+
+
+  request.onload = function() {
+    var audioData = request.response;
+
+    audioCtx.decodeAudioData(audioData, function(buffer) {
+        source.buffer = buffer;
+
+        source.connect(audioCtx.destination);
+        source.loop = true;
+      },
+
+      function(e){"Error with decoding audio data" + e.err});
+
+  }
+
+  request.send();
+}
+
+// Buttons setzen um Wiedergabe zu starten und stoppen
+
+play.onclick = function() {
+  getData();
+  source.start(0);
+  play.setAttribute('disabled', 'disabled');
+}
+
+stop.onclick = function() {
+  source.stop(0);
+  play.removeAttribute('disabled');
+}
+
+
+// Script ausgeben
+
+pre.innerHTML = myScript.innerHTML;
+ +

Neue Promise basierte Syntax

+ +
ctx.decodeAudioData(compressedBuffer).then(function(decodedData) {
+ // Die dekodierten PCM Daten können hier verwendet werden
+});
+ +

Parameter

+ +
+
ArrayBuffer
+
Ein ArrayBuffer der die zu dekodierenden Daten enthält. Dieser wird normalerweise durch einen {{domxref("XMLHttpRequest")}} befüllt nachdem der responseType auf arraybuffer gesetzt wurde.
+
DecodeSuccessCallback
+
Ein Callback das aufgerufen wird wenn die Dekodierung erfolgreich abgeschlossen wird. Der einzige Parameter der an diese Funktion übergeben wird stellt einen AudioBuffer dar, der die dekodierten Audiodaten enthält. Normalerweise wird dieser Buffer an einen {{domxref("AudioBufferSourceNode")}} übergeben, von wo er wiedergeben und verändert werden kann.
+
DecodeErrorCallback
+
Ein optionales Callback das bei einem Fehler während der Dekodierung aufgerufen wird.
+
+ +

Rückgabewerte

+ +

Ein {{domxref("AudioBuffer") }} der die dekodierten Audiodaten enthällt.

+ +

Spezifikationen

+ + + + + + + + + + + + + + +
SpezifikationStatusKommentar
{{SpecName('Web Audio API', '#widl-AudioContext-decodeAudioData-Promise-AudioBuffer--ArrayBuffer-audioData-DecodeSuccessCallback-successCallback-DecodeErrorCallback-errorCallback', 'decodeAudioData()')}}{{Spec2('Web Audio API')}} 
+ +

Browserunterstützung

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatChrome(10.0)}}{{property_prefix("webkit")}}{{CompatGeckoDesktop(25.0)}} {{CompatNo}}15.0{{property_prefix("webkit")}}
+ 22 (unprefixed)
6.0{{property_prefix("webkit")}}
Promise-based syntax{{CompatChrome(49.0)}}{{CompatVersionUnknown}}{{CompatNo}}{{CompatVersionUnknown}}{{CompatNo}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidAndroid WebviewFirefox Mobile (Gecko)Firefox OSIE MobileOpera MobileSafari MobileChrome for Android
Basic support{{CompatUnknown}}{{CompatVersionUnknown}}26.01.2{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatChrome(33.0)}}
Promise-based syntax{{CompatUnknown}}{{CompatChrome(49.0)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatNo}}{{CompatUnknown}}{{CompatUnknown}}{{CompatChrome(49.0)}}
+
+ +

Siehe auch

+ + diff --git a/files/de/web/api/canvas_api/index.html b/files/de/web/api/canvas_api/index.html new file mode 100644 index 0000000000..50e6da1aae --- /dev/null +++ b/files/de/web/api/canvas_api/index.html @@ -0,0 +1,120 @@ +--- +title: Canvas +slug: Web/HTML/Canvas +tags: + - API + - Canvas + - HTML5 +translation_of: Web/API/Canvas_API +--- +
{{CanvasSidebar}}
+ +

Mit der Einführung von HTML5 wurde ein neues "Leinwand" Element zum Zeichnen von Grafiken mittels JavaScript-API vorgestellt: {{HTMLElement("canvas")}}. Die Anwendungsgebiete sind äußerst flexibel, so ist es möglich, mit Canvas Diagramme zu zeichnen, Bilder zu bearbeiten, Animationen zu erstellen oder sogar Videos zu bearbeiten und zu rendern.

+ +

Der Support für <canvas> in Mozilla-Anwendungen wurde ab Gecko 1.8 (sprich ab Firefox 1.5) implementiert. Canvas stammt ursprünglich von Apple und wurde für das OS X Dashboard und den Safari-browser entwickelt. Der Internet Explorer unterstützt Canvas ab Version 9, wobei es durch ein Skript von Google's Explorer Canvas Projekt auch in früheren Versionen des IE lauffähig ist. Google Chrome und Opera ab Version 9 unterstützen <canvas> ebenfalls problemlos.

+ +

Das <canvas> Element wird zudem von WebGL für die hardwarebeschleunigte Darstellung von 3D-Grafiken auf Webseiten eingesetzt.

+ +

Beispiel

+ +

Dies ist ein einfacher Code-Ausschnitt mit der {{domxref("CanvasRenderingContext2D.fillRect()")}} Methode.

+ +

HTML

+ +
<canvas id="canvas"></canvas>
+
+ +

JavaScript

+ +
var canvas = document.getElementById("canvas");
+var ctx = canvas.getContext("2d");
+
+ctx.fillStyle = "green";
+ctx.fillRect(10, 10, 100, 100);
+
+ +

Bearbeite den untenstehenden Code und sieh live Updates im Canvas: (Funktion kaputt auf Deutsch, die Englische Seite funktioniert, bitte oben umschalten auf Englisch )

+ +

{{ EmbedLiveSample('Playable_code', 700, 360) }}

+ +

Referenzen

+ +
+ +
+ +

Die Schnittstellen, die sich auf WebGLRenderingContext beziehen, werden unter WebGL zusammengefasst.

+ +

Leitfäden und Anleitungen

+ +
+
Canvas Tutorial
+
Eine ausführliche Anleitung, die sowohl grundlegende Nutzung als auch fortgeschrittene Funktionen umfasst
+
Code-Ausschnitte: Canvas
+
Einige Entwickler-orientierte Code-Ausschnitte, die <canvas> enthalten.
+
Demo: A basic ray-caster
+
A demo of ray-tracing animation using canvas.
+
DOM-Objekte in ein canvas zeichnen
+
Wie man DOM Inhalt, wie zum Beispiel HTML-Elemente, in ein canvas zeichnet.
+
Videos mit canvas manipulieren
+
{{HTMLElement("video")}} und {{HTMLElement("canvas")}} kombinieren, um Videos in Echtzeit zu manipulieren.
+
+ +

Quellen

+ +

Allgemeines

+ + + +

Libraries

+ + + +

Spezifikationen

+ + + + + + + + + + + + + + + + +
SpezifikationStatusKommentar
{{SpecName('HTML WHATWG', "the-canvas-element.html", "Canvas")}}{{Spec2('HTML WHATWG')}} 
+ +

Siehe auch

+ + diff --git a/files/de/web/api/canvas_api/tutorial/advanced_animations/index.html b/files/de/web/api/canvas_api/tutorial/advanced_animations/index.html new file mode 100644 index 0000000000..10c4a7650c --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/advanced_animations/index.html @@ -0,0 +1,380 @@ +--- +title: Fortgeschrittene Animationen +slug: Web/Guide/HTML/Canvas_Tutorial/Advanced_animations +tags: + - Canvas + - Graphics + - Tutoria +translation_of: Web/API/Canvas_API/Tutorial/Advanced_animations +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}
+ +
+

In dem vorherigem Kapitel machten wir einige Basisanimationen und lernten Möglichkeiten, Dinge zu bewegen. In diesem Kapitel werden wir uns die Bewegung selbst anschauen und werden etwas Physik einfügen um unsere Animationen fortgeschrittener zu machen.

+
+ +

Einen Ball zeichnen

+ +

Wir werden einen Ball für unsere Animationen benutzen, deshalb zeichnen wir zuerst einen Ball mit dem folgendem Code in die Canvas.

+ +
<canvas id="canvas" width="600" height="300"></canvas>
+
+ +

Wie gewöhnlich brauchen wir zuerst ein draw context. Um den Ball zu zeichnen, werden wir ein ball - Objekt erstellen, welches Eigenschaften und eine draw() - Methode enthält, um es in die Canvas zu zeichnen.

+ +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+var ball = {
+  x: 100,
+  y: 100,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+ball.draw();
+ +

Nichts Besonderes hier, der Ball ist momentan ein einfacher Kreis und wird mit der {{domxref("CanvasRenderingContext2D.arc()", "arc()")}} - Methode gezeichnet.

+ +

Geschwindigkeit hinzufügen

+ +

Nun haben wir einen Ball und sind bereit, eine Basisanimation hinzuzufügen, wie wir es im vorherigen Kapitel von diesem Tutorial lernten. Wieder hilft uns {{domxref("window.requestAnimationFrame()")}}, die Animation zu kontrollieren. Der Ball bewegt sich durch Hinzufügen von einem Geschwindigkeitsvektor zu der Position. Für jedes Frame {{domxref("CanvasRenderingContext2D.clearRect", "clean", "", 1)}} wir die Canvas, um alte Kreise zu entfernen.

+ +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+var raf;
+
+var ball = {
+  x: 100,
+  y: 100,
+  vx: 5,
+  vy: 2,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+function draw() {
+  ctx.clearRect(0,0, canvas.width, canvas.height);
+  ball.draw();
+  ball.x += ball.vx;
+  ball.y += ball.vy;
+  raf = window.requestAnimationFrame(draw);
+}
+
+canvas.addEventListener('mouseover', function(e){
+  raf = window.requestAnimationFrame(draw);
+});
+
+canvas.addEventListener("mouseout",function(e){
+  window.cancelAnimationFrame(raf);
+});
+
+ball.draw();
+
+ +

Gebundenheit

+ +

Ohne jede gebundene Kollisionsüberprüfung fliegt unser Ball schnell aus dem Canvas. Wir müssen die x - und yposition von dem Ball überprüfen und wenn der Ball außerhalb des Canvas ist, die Richtung der Geschwindigkeitsvektoren umkehren.

+ +
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
+  ball.vy = -ball.vy;
+}
+if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
+  ball.vx = -ball.vx;
+}
+ +

Erste Demo

+ +

Lass uns sehen, wie das in Aktion aussieht. Bewegen Sie die Maus in die Canvas, um die Animation zu starten.

+ + + +

{{EmbedLiveSample("Erste_Demo", "610", "310")}}

+ +

Acceleration

+ +

Um die Bewegung realistischer zu machen, können Sie mit Geschwindigkeit spielen, zum Beispiel:

+ +
ball.vy *= .99;
+ball.vy += .25;
+ +

Dies verlangsamt die vertikale Geschwindigkeit in jedem Frame, sodass der Ball am Ende nur noch am Boden hüpft.

+ + + +

{{EmbedLiveSample("Acceleration", "610", "310")}}

+ +

Spureneffekt

+ +

Bis jetzt haben wir die {{domxref("CanvasRenderingContext2D.clearRect", "clearRect")}}  - Methode benutzt, wenn wir Frames clearten. Wenn Sie diese Methode durch {{domxref("CanvasRenderingContext2D.fillRect", "fillRect")}} ersetzen, können Sie einfach einen Spureneffekt erzeugen.

+ +
ctx.fillStyle = 'rgba(255,255,255,0.3)';
+ctx.fillRect(0,0,canvas.width,canvas.height);
+ + + +

{{EmbedLiveSample("Spureneffekt", "610", "310")}}

+ +

Mauskontrolle hinzufügen

+ +

Um etwas Kontrolle über den Ball zu bekommen, können wir machen, dass er die Maus verfolgt, indem wir beispielsweise das mousemove - Ereignis benutzen. Das click - Ereignis lässt den Ball wieder hüpfen.

+ + + +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+var raf;
+var running = false;
+
+var ball = {
+  x: 100,
+  y: 100,
+  vx: 5,
+  vy: 1,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+function clear() {
+  ctx.fillStyle = 'rgba(255,255,255,0.3)';
+  ctx.fillRect(0,0,canvas.width,canvas.height);
+}
+
+function draw() {
+  clear();
+  ball.draw();
+  ball.x += ball.vx;
+  ball.y += ball.vy;
+
+  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
+    ball.vy = -ball.vy;
+  }
+  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
+    ball.vx = -ball.vx;
+  }
+
+  raf = window.requestAnimationFrame(draw);
+}
+
+canvas.addEventListener('mousemove', function(e){
+  if (!running) {
+    clear();
+    ball.x = e.clientX;
+    ball.y = e.clientY;
+    ball.draw();
+  }
+});
+
+canvas.addEventListener("click",function(e){
+  if (!running) {
+    raf = window.requestAnimationFrame(draw);
+    running = true;
+  }
+});
+
+canvas.addEventListener("mouseout",function(e){
+  window.cancelAnimationFrame(raf);
+  running = false;
+});
+
+ball.draw();
+
+ +

Bewegen Sie den Ball mit der Maus und klicken Sie, um ihn loszulassen.

+ +

{{EmbedLiveSample("Mauskontrolle_hinzuf%C3%BCgen", "610", "310")}}

+ +

Breakout

+ +

Dieses kurze Kapitel erklärt nur einige Möglichkeiten, fortgeschrittene Animationen zu erstellen. Es gibt viel mehr! Was darüber, ein Brett und einige Blöcke hizuzufügen und diese Demo in ein Breakout - Spiel zu verwandeln? Schauen Sie sich unseren Spieleentwickelungsbereich an für mehr auf Spiele bezogene Artikel.

+ +

Siehe auch

+ + + +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}

diff --git a/files/de/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html b/files/de/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html new file mode 100644 index 0000000000..6caa27b3ef --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html @@ -0,0 +1,785 @@ +--- +title: Stile und Farben verwenden +slug: Web/Guide/HTML/Canvas_Tutorial/Applying_styles_and_colors +translation_of: Web/API/Canvas_API/Tutorial/Applying_styles_and_colors +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_shapes", "Web/API/Canvas_API/Tutorial/Drawing_text")}}
+ +
+

Im Kapitel über das Zeichnen von Formen haben wir zum Einstieg nur die Standard-Stile für Linien und Füllungen benutzt. Nun möchten wir uns etwas näher mit Möglichkeiten beschäftigen, unsere Zeichnungen etwas attraktiver zu gestalten. Wir werden lernen, unterschiedliche Farben, Linienstile, Verläufe, Muster und Schatten in unseren Zeichnungen anzuwenden.

+
+ +

Farben

+ +

Bis jetzt haben wir nur Methoden im unmittelbaren Zusammenhang mit dem Zeichnen gelernt. Möchten wir einer Form eine Farbe zuordnen, stehen uns folgenden zwei Eigenschaften zur Verfügung: fillStyle und strokeStyle.

+ +
+
{{domxref("CanvasRenderingContext2D.fillStyle", "fillStyle = color")}}
+
+

 Bestimmt den Stil in dem die Form gefüllt wird.

+
+
{{domxref("CanvasRenderingContext2D.strokeStyle", "strokeStyle = color")}}
+
+

Bestimmt den Stil der Umrandungslinien.

+
+
+ +

color steht entweder für einen CSS {{cssxref("<color>")}} Farbwert, ein Gradienten-Objekt, oder ein Muster-Objekt. Auf letztere gehen wir später noch ein. Standardmäßig sind Strich- und Füllfarbe auf Schwarz eingestellt (CSS-Farbwert #000000).

+ +
+

Anmerkung: Nach dem Setzen von strokeStyle und/oder fillStyle wird der neue Wert zum Standardwert für alle nachfolgend gezeichneten Formen. Für jede in einer abweichenden Farbe gewünschte Form müssen fillStyle bzw. strokeStyle neu definiert werden.

+
+ +

Der String color sollte, entsprechend der Spezifikation, ein gültiger CSS {{cssxref("<color>")}} -Wert sein. Alle folgenden Beispiele definieren die selbe Farbe.

+ +
// these all set the fillStyle to 'orange'
+
+ctx.fillStyle = "orange";
+ctx.fillStyle = "#FFA500";
+ctx.fillStyle = "rgb(255,165,0)";
+ctx.fillStyle = "rgba(255,165,0,1)";
+
+ +

Beispiel für fillStyle

+ +

In diesem Beispiel nutzen wir wieder zwei Schleifen um ein Gitter aus gefüllten Quadraten zu zeichnen, jedes in einer anderen Farbe. Das resultierende Bild sollte etwa dem Screenshot unten entsprechen. Außergewöhnliches passiert hier nicht, wir nutzen einfach die beiden Variablen i und j um jedem Quadrat eine eigene Farbe zu geben, wobei wir nur die Werte für den Rot- und Grünanteil ändern; der Blauwert bleibt unverändert. Durch Modifikation der Farbkanäle können Sie verschiedene Paletten erzeugen; eine Erhöhung der Schrittweite erzeugt z.B. ein Muster, das an die Farbpaletten in Photoshop erinnert.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  for (var i=0;i<6;i++){
+    for (var j=0;j<6;j++){
+      ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' +
+                       Math.floor(255-42.5*j) + ',0)';
+      ctx.fillRect(j*25,i*25,25,25);
+    }
+  }
+}
+ + + +

Das Ergebnis:

+ +

{{EmbedLiveSample("A_fillStyle_example", 160, 160, "https://mdn.mozillademos.org/files/5417/Canvas_fillstyle.png")}}

+ +

Beispiel für strokeStyle

+ +

Dieses Beispiel ähnelt dem vorangegangenen, nutzt aber die strokeStyle Eigenschaft, um die Farben der Umrisslinien zu ändern. Mit der Methode arc() zeichnen wir Kreise an Stelle der Quadrate.

+ +
  function draw() {
+    var ctx = document.getElementById('canvas').getContext('2d');
+    for (var i=0;i<6;i++){
+      for (var j=0;j<6;j++){
+        ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' +
+                         Math.floor(255-42.5*j) + ')';
+        ctx.beginPath();
+        ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
+        ctx.stroke();
+      }
+    }
+  }
+
+ + + +

The result looks like this:

+ +

{{EmbedLiveSample("A_strokeStyle_example", "180", "180", "https://mdn.mozillademos.org/files/253/Canvas_strokestyle.png")}}

+ +

Transparenz

+ +

Zusätzlich zu deckenden Formen können wir auch teiltransparente (oder durchscheinende) Formen zeichnen. Dies geschieht entweder durch das Setzen der Eigenschaft globalAlpha oder die Zuordnung einer teiltransparenten Farbe zu einem Zeichen- oder Füllstil.

+ +
+
{{domxref("CanvasRenderingContext2D.globalAlpha", "globalAlpha = transparencyValue")}}
+
+

Wendet den angegebenen Transparenz-Wert auf alle nachfolgend gezeichneten Formen an. Der Wert muss zwischen 0.0 (vollständig transparent) und 1.0 (vollständig deckend) liegen. Der Standardwert ist 1.0.

+
+
+ +

Die Eigenschaft globalTransparency ist nützlich, wenn man viele Formen mit gleicher Transparenz zeichnen möchte; meist ist es aber praktischer, die Transparenz jeder einzelnen Form gemeinsam mit ihrer Farbe zu definieren.

+ +

Die Eigenschaften strokeStyle und fillStyle akzeptieren CSS rgba Farbwerte, daher kann die Transparenz direkt bei der Zuordnung einer Farbe wie folgt gesetzt werden:

+ +
// Assigning transparent colors to stroke and fill style
+
+ctx.strokeStyle = "rgba(255,0,0,0.5)";
+ctx.fillStyle = "rgba(255,0,0,0.5)";
+
+ +

Die rgba()-Funktion entspricht der rgb()-Funktion, allerdings mit einem zusätzlichen Parameter am Ende. Dieser Wert bestimmt die Transparenz dieser bestimmten Farbe. Der Gültigkeitsbereich umfasst Werte zwischen 0.0 (völlig transpartent) und 1.0 (vollständig deckend).

+ +

Beispiel für globalAlpha

+ +

In diesem Beispiel zeichnen wir einen Hintergrund aus vier unterschiedlich gefärbten Quadraten. Über diese legen wir dann einige transparente Kreise. Die Eigenschaft globalAlpha wird auf den Wert 0.2 gesetzt, der dann für alle folgend gezeichneten Formen verwendet wird. Jeder Durchlauf der for-Schleife zeichnet einen Satz Kreise mit zunehmendem Radius. Das Endresultat ist ein kreisförmiger Verlauf. Durch das Übereinanderlagern von immer mehr Kreisen reduzieren wir letztlich die Transparenz bereits gezeichneter Kreise. Erhöhen wir die Anzahl der Durchläufe (und damit der gezeichneten Kreise) weiter, wird der Hintergrund in der Bildmitte irgendwann völlig überdeckt.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  // draw background
+  ctx.fillStyle = '#FD0';
+  ctx.fillRect(0,0,75,75);
+  ctx.fillStyle = '#6C0';
+  ctx.fillRect(75,0,75,75);
+  ctx.fillStyle = '#09F';
+  ctx.fillRect(0,75,75,75);
+  ctx.fillStyle = '#F30';
+  ctx.fillRect(75,75,75,75);
+  ctx.fillStyle = '#FFF';
+
+  // set transparency value
+  ctx.globalAlpha = 0.2;
+
+  // Draw semi transparent circles
+  for (i=0;i<7;i++){
+    ctx.beginPath();
+    ctx.arc(75,75,10+10*i,0,Math.PI*2,true);
+    ctx.fill();
+  }
+}
+ + + +

{{EmbedLiveSample("A_globalAlpha_example", "180", "180", "https://mdn.mozillademos.org/files/232/Canvas_globalalpha.png")}}

+ +

Beispiel für die Verwendung von rgba()

+ +

Das zweite Beispiel ist ähnlich dem ersten, aber hier überlagern wir farbige Flächen mit schmalen, weißen Rechtecken zunehmender Deckkraft. Die Verwendung von rgba() an Stelle von globalAlpha erlaubt uns mehr Kontrolle und Flexibilität, weil wir damit Strich- und Füllstil unterschiedlich behandeln können.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Draw background
+  ctx.fillStyle = 'rgb(255,221,0)';
+  ctx.fillRect(0,0,150,37.5);
+  ctx.fillStyle = 'rgb(102,204,0)';
+  ctx.fillRect(0,37.5,150,37.5);
+  ctx.fillStyle = 'rgb(0,153,255)';
+  ctx.fillRect(0,75,150,37.5);
+  ctx.fillStyle = 'rgb(255,51,0)';
+  ctx.fillRect(0,112.5,150,37.5);
+
+  // Draw semi transparent rectangles
+  for (var i=0;i<10;i++){
+    ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';
+    for (var j=0;j<4;j++){
+      ctx.fillRect(5+i*14,5+j*37.5,14,27.5);
+    }
+  }
+}
+ + + +

{{EmbedLiveSample("An_example_using_rgba()", "180", "180", "https://mdn.mozillademos.org/files/246/Canvas_rgba.png")}}

+ +

Linienstile

+ +

Mehrere Eigenschaften ermöglichen den Zugriff auf Linienstile:

+ +
+
{{domxref("CanvasRenderingContext2D.lineWidth", "lineWidth = value")}}
+
Setzt die Breite später gezeichneter Linien.
+
{{domxref("CanvasRenderingContext2D.lineCap", "lineCap = type")}}
+
Definiert die Form der Linienenden.
+
{{domxref("CanvasRenderingContext2D.lineJoin", "lineJoin = type")}}
+
Definiert die Form der „Ecken“, an denen sich Linien treffen.
+
{{domxref("CanvasRenderingContext2D.miterLimit", "miterLimit = value")}}
+
Definiert einen Grenzwert für die Gehrung (Schräge) am spitzen Winkel zwischen zwei Linien; damit wird die Dicke des Verbindungsbereichs begrenzt.
+
+ +
+
{{domxref("CanvasRenderingContext2D.getLineDash", "getLineDash()")}}
+
+

Gibt den aktuellen Array für das Strichlierungsmuster zurück (eine gerade Anzahl nicht-negativer Zahlen).

+
+
{{domxref("CanvasRenderingContext2D.setLineDash", "setLineDash(segments)")}}
+
+

Definiert das aktuelle Strichlierungsmuster.

+
+
{{domxref("CanvasRenderingContext2D.lineDashOffset", "lineDashOffset = value")}}
+
+

Beschreibt wo das Strichlierunsmuster startet.

+
+
+ +

Die Beispiele unten sollen Ihnen das Verständnis dieser Angaben erleichtern.

+ +

Beispiel für lineWidth

+ +

Diese Eigenschaft bestimmt die aktuelle Linienbreite. Der Standardwert ist 1.0, und es sind nur positive Zahlen erlaubt.

+ +

Die Linienbreite entspricht der Dicke des Strichs, zentriert über der Strecke zwischen den Punkten. Anders ausgedrückt dehnt sich die Fläche der gezeichneten Linie je zur Hälfte links und rechts der Strecke aus. Weil Canvas-Koordinaten sich nicht unmittelbar auf Pixel beziehen müssen, sollte man etwas Sorgfalt walten lassen um „scharfe“ bzw. definierte horizontale und vertikale Linien zu erhalten.

+ +

Im folgenden Beispiel werden zehn gerade Linien zunehmender Breite gezeichnet. Die Linie ganz links ist 1.0 Einheiten breit. Allerdings erscheint diese Linie - und auch alle anderen Linien mit einem ungeraden Wert für die Liniendicke - nicht wirklich scharf; schuld daran ist die Positionierung.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  for (var i = 0; i < 10; i++){
+    ctx.lineWidth = 1+i;
+    ctx.beginPath();
+    ctx.moveTo(5+i*14,5);
+    ctx.lineTo(5+i*14,140);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_lineWidth_example", "180", "180", "https://mdn.mozillademos.org/files/239/Canvas_linewidth.png")}}

+ +

Um scharfe Linien zu erzeugen muss man erst verstehen, wie Linien gezeichnet werden. In den Bildern unten steht das Gitter für das Koordinatensystem im Canvas. Die Quadrate zwischen den Gitterlinien entsprechen reellen Pixeln der Bildschirmdarstellung. Im ersten Bild wird ein Rechteck zwischen (2,1) und (5,5) gefüllt. Das gesamte Rechteck zwischen diesen Koordinaten (im Bild hellrot) fällt mit den Pixelgrenzen zusammen, und es resultiert eine Rechteckfläche mit scharfen Abgrenzungen.

+ +

+ +

Stellen wir uns jetzt einen Pfad von (3,1) nach (3,5) mit einer Linienbreite von 1.0 vor, dann erhalten wir die Situation im zweiten Bild. Die zu füllende Fläche erstreckt sich jeweils nur zur Hälfte über die links und rechts angrenzenden Pixel. Dieser Zustand kann nur näherungsweise umgesetzt werden, so dass die betroffenen Pixel nur in der halben Intensität gefüllt werden. Genau das passierte mit der 1.0 Einheiten breiten Linie im vorhergehenden Programm.

+ +

Um das zu korrigieren muss man bei der Definition der Verbindungsstrecke besonders genau sein. Mit dem Wissen, dass eine Linie der Breite 1.0 sich jeweils zur Hälfte auf beide Seiten ausdehnt, kann man die Strecke von (3.5,1) bis (3.5,5) legen und erhält die Situation im dritten Bild - die eine Einheit breite Linie füllt exakt eine Pixelreihe.

+ +
+

Anmerkung: Bitte beachten Sie, dass in dem Beispiel der vertikalen Linie die Y-Position sich immer noch auf eine ganzzahlige Position bezieht - andernfalls würden an den Endpunkten die Pixel nur halb gedeckt.(Beachten Sie aber auch, dass dieses Verhalten zusätzlich vom lineCap-Stil abhängt, der standardmäßig auf butt eingestellt ist. Möchten Sie einheitliche Striche mit Koordinaten in halben Pixeln für ungerade Liniendicken berechnen, können Sie dafür den lineCap-Sti auf square setzten, wodurch der Aussenrand des Strichs am Endpunkt automatisch über den ganzen Pixel ausgedehnt wird.)

+ +

Beachten Sie auch, dass nur der Start- und Zielpunkt einer Strecke betroffen ist. Bei einer mit closePath() geschlossenen Strecke git es keinen solchen Start- bzw. Zielpunkt, stattdessen werden alle Endpunkte mit den vorhergehenden und nachfolgenden Segmenten entsprechend dem aktuellen lineJoin-Stil verbunden; dessen Standardwert ist miter, was eine automatische Ausweitung der äußeren Linienränder bis zu ihrem Schnittpunkt bewirkt, so dass der gezeichnete Strich an jedem Endpunkt exakt volle Pixel abdeckt, wenn die verbundenen Segmente horizontal und/oder vertikal verlaufen. Die folgenden zwei Abschnitte demonstrieren das Verhalten dieser zusätzlichen Linien-Stile.

+
+ +

Für scharfe Ränder bei geradzahligen Linienbreiten definieren wir den Pfad zwischen den Pixeln (z.B. (3,1) bis (3,5)), so dass jede Hälfte des Strichs einer ganzzahligen Anzahl von Pixeln entspricht.

+ +

Wenn die sorgfältige Arbeit mit dem Pixelraster in 2D-Grafiken anfangs auch noch etwas anstrengend erscheinen mag, so gewährleistet sie letztlich eine korrekte Abbildung der Grafiken, unabhänging von Skalierungen oder anderen Transformationen. Eine korrekt positionierte Linie mit einer Breite von 1.0 wird nach einer Vergrößerung um den Faktor 2 eine Linie der Breite 2.0 ergeben, wiederum scharf umrissen und an der richtigen Position.

+ +

Beispiel für lineCap

+ +

Die Eigenschaft lineCap bestimmt das Erschinungsbild der Linienenden. Sie kann drei verschiedene Werte annehmen: butt (Standardwert), round und square.

+ +

+ +
+
butt
+
+

Glatte Enden an den Endpunkten.

+
+
round
+
+

Abgerundete Enden.

+
+
square
+
+

Die Enden werden glatt abgeschnitten, es wird aber vorher ein Rechteck angefügt, gleicher Breite wie die Linie und halb so lang wie breit.

+
+
+ +

In diesem Beispiel ziehen wir drei Linien, jede davon mit einem unterschiedlichen Wert für lineCap. Zwei Hilfslinien helfen dabei die Unterschiede zu verdeutlichen. Jede Linie beginnt und endet exakt an den Hilfslinien.

+ +

Die linke Linie verwendet die Option butt. Sie sehen, dass sie exakt an den Hilfslinien endet. Die zweite Linie verwendet die Option round, es wird dadurch ein Halbkreis mit einem Radius entsprechend der halben Linienbreite angehängt. Die recht Linie verwendet die Option square. Diese hängt ein Rechteck von gleicher Breite und einer Länge der halben Linienbreite an.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var lineCap = ['butt','round','square'];
+
+  // Draw guides
+  ctx.strokeStyle = '#09f';
+  ctx.beginPath();
+  ctx.moveTo(10,10);
+  ctx.lineTo(140,10);
+  ctx.moveTo(10,140);
+  ctx.lineTo(140,140);
+  ctx.stroke();
+
+  // Draw lines
+  ctx.strokeStyle = 'black';
+  for (var i=0;i<lineCap.length;i++){
+    ctx.lineWidth = 15;
+    ctx.lineCap = lineCap[i];
+    ctx.beginPath();
+    ctx.moveTo(25+i*50,10);
+    ctx.lineTo(25+i*50,140);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_lineCap_example", "180", "180", "https://mdn.mozillademos.org/files/236/Canvas_linecap.png")}}

+ +

Beispiel für lineJoin

+ +

Die Eigenschaft lineJoin bestimmt, wie zwei zusammenhängende Segmente (Linien-, Kurven- oder Kreissegmente) länger als Null in einer Form verbunden werden („degenerierte“ Elemente mit zusammenfallenden Start- und Zielpunkten, also mit der Länge Null, werden dabei übersprungen).

+ +

Diese Eigenschaft kann drei mögliche Werte annehmen: round, bevel und miter (Standardeinstellung). Beachten Sie, dass die lineJoin-Einstellung keine Auswirkungen hat wenn die Segemente in gleicher Richtung verlaufen, da in diesem Falle keine Verbindungsfläche eingefügt wird.

+ +

+ +
+
round
+
+

Rundet die Ecke ab indem ein zusätzlicher Kreisausschnitt am gemeinsamen Endpunkt der verbundenen Segmente eingefügt wird. Der Radius entspricht der halben Liniendicke.

+
+
bevel
+
+

Füllt die Fläche zwischen dem gemeinsamen Endpunkt und den beiden getrennten äußeren Ecken der Segmente mit einem Dreieck.

+
+
miter
+
+

Verlängert die Aussenränder der Segmente bis sie sich in einem Punkt schneiden, wobei eine rautenförmige Fläche eingefügt wird. Diese Einstellung wird durch die Eigenschaft miterLimit beeinflusst, welche später erklärt wird.

+
+
+ +

Das folgenden Beispielprogramm verdeutlicht die Auswirkungen der drei Varianten von lineJoin anhand von drei Linienzügen; das Ergebnis sehen Sie oben.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var lineJoin = ['round','bevel','miter'];
+  ctx.lineWidth = 10;
+  for (var i=0;i<lineJoin.length;i++){
+    ctx.lineJoin = lineJoin[i];
+    ctx.beginPath();
+    ctx.moveTo(-5,5+i*40);
+    ctx.lineTo(35,45+i*40);
+    ctx.lineTo(75,5+i*40);
+    ctx.lineTo(115,45+i*40);
+    ctx.lineTo(155,5+i*40);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_lineJoin_example", "180", "180", "https://mdn.mozillademos.org/files/237/Canvas_linejoin.png")}}

+ +

Beispiel für die Eigenschaft miterLimit

+ +

Wie wir im vorhergehenden Beispiel gesehen haben, werden mit der Option miter die äußeren Kanten von zwei schneidenden Linien bis zu ihrem Schnittpunkt verlängert. Schneiden sich die Linien unter einem großen Winkel, liegt dieser äußere Schnittpunkt nicht allzu weit vom inneren entfernt. Mit kleiner werdendem Winkel vergrößert sich die Länge dieses Bereichs (genannt Gehrung = engl. miter) aber exponentiell.

+ +

Die Eigenschaft miterLimit bestimmt, wie weit der äußere Verbindungspunkt maximal vom inneren entfernt sein darf. Wird dieser Wert überschritten, wird stattdessen eine Fase gezogen, ähnlich der Einstellung bevel. Beachten Sie: Die maximale Gehrungslänge ist das Produkt aus der Liniendicke, gemessen im aktuellen Koordinatensystem, und dem Wert von miterLimit (Standardwert 10.0 im HTML {{HTMLElement("canvas")}}). Daher kann miterLimit unabhängig vom aktuellen Vergrößerungsmaßstab oder irgendeiner affinen Transformation der Strecke gesetzt werden; es beeinflusst nur die reell abgebildete Form der Kanten.

+ +

Präziser gesagt ist beschreibt miterLimit das maximale Verhältnis der Ausdehnung der Verlängerung nach außen (diese wird im HTML Canvas zwischen dem Schnittpunkt der äußeren Kanten der schneidenden Linien und dem gemeinsamen Endpunkt der Strecken gemessen) zur halben Liniendicke. Dazu gleichwertig ist die Definition als das maximal erlaubte Verhältnis der Entfernung zwischen dem inneren und äußeren Eckpunkt der Verbindung und der vollen Liniendicke. Er entspricht mathematisch dem Kosekans des halben minimalen Innenwinkels zwischen den verbundenen Segmenten, unterhalb dessen nur eine Fase ohne weitere Ausdehnung gezeichnet wird.

+ + + +

In dieser einfachen Demonstration können Sie den Wert für miterLimit dynamisch einstellen und dabei verfolgen, wie sich die Form der Ecken ändert. Die blauen Linien zeigen an wo Start- und Zielpunkt jedes Zickzack-Segments liegen.

+ +

Stellen Sie in diesem Programm ein miterLimit kleiner als 4.2 ein, wird an keiner der sichtbaren Ecken zu einer zusätzlichen Ausdehnung durch die Gehrung kommen; stattdessen entsteht an der blauen Linie eine Fase. Ist miterLimit größer als zehn, verbinden sich die meisten Linien in diesem Beispiel in einer Gehrung die weit über die blauen Linie hinausreicht, wobei sich die Höhe von links nach rechts wegen der zunehmenden Winkel verringert. Für dazwischenliegende Werte bilden die linken Elemente eine ausgedehnte Gehrung, während nach rechts die Spitzen durch Fasen ersetzt werden.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Clear canvas
+  ctx.clearRect(0,0,150,150);
+
+  // Draw guides
+  ctx.strokeStyle = '#09f';
+  ctx.lineWidth   = 2;
+  ctx.strokeRect(-5,50,160,50);
+
+  // Set line styles
+  ctx.strokeStyle = '#000';
+  ctx.lineWidth = 10;
+
+  // check input
+  if (document.getElementById('miterLimit').value.match(/\d+(\.\d+)?/)) {
+    ctx.miterLimit = parseFloat(document.getElementById('miterLimit').value);
+  } else {
+    alert('Value must be a positive number');
+  }
+
+  // Draw lines
+  ctx.beginPath();
+  ctx.moveTo(0,100);
+  for (i=0;i<24;i++){
+    var dy = i%2==0 ? 25 : -25 ;
+    ctx.lineTo(Math.pow(i,1.5)*2,75+dy);
+  }
+  ctx.stroke();
+  return false;
+}
+
+ + + +

{{EmbedLiveSample("A_demo_of_the_miterLimit_property", "400", "180", "https://mdn.mozillademos.org/files/240/Canvas_miterlimit.png")}}

+ +

Strichlierte Linien verwenden

+ +

Die Methode setLineDash und die Eigenschaft lineDashOffset definieren die Strichlierung von Linien. setLineDash akzeptiert eine Liste von Zahlenwerten, die abwechselnd die Abstände von Strichen und Zwischenräumen definieren, wobei lineDashOffset einen Versatz am Start des Musters definiert.

+ +

In diesem Beispiel erzeugen wir einen „marschierende Ameisen“-Effekt. Diese Animation finden wir oft bei Auswahlwerkzeugen von Bildbearbeitungsprogrammen. Es macht dort die Grenze zwischen Auswahlbereich und Hintergrund sichtbar. Später werden Sie in diesem Tutorial noch lernen solche oder ähnliche einfache Animationen zu erzeugen.

+ + + +
var ctx = document.getElementById('canvas').getContext('2d');
+var offset = 0;
+
+function draw() {
+  ctx.clearRect(0,0, canvas.width, canvas.height);
+  ctx.setLineDash([4, 2]);
+  ctx.lineDashOffset = -offset;
+  ctx.strokeRect(10,10, 100, 100);
+}
+
+function march() {
+  offset++;
+  if (offset > 16) {
+    offset = 0;
+  }
+  draw();
+  setTimeout(march, 20);
+}
+
+march();
+ +

{{EmbedLiveSample("Using_line_dashes", "120", "120", "https://mdn.mozillademos.org/files/9853/marching-ants.png")}}

+ +

Gradienten

+ +

Wie in jedem anderen Zeichenprogramm können wir auch im Canvas linien- und kreisförmige Verläufe zum Zeichnen und Füllen von Formen verwenden. Mit den folgenden Befehlen können wir ein {{domxref("CanvasGradient")}}-Objekt erzeugen; dieses ordnen wir dann einer fillStyle- oder strokeStyle-Eigenschaft zu.

+ +
+
{{domxref("CanvasRenderingContext2D.createLinearGradient", "createLinearGradient(x1, y1, x2, y2)")}}
+
+

Erzeugt eine lineares Verlaufsobjekt mit Startpunkt (x1, y1) und Zielpunkt (x2, y2).

+
+
{{domxref("CanvasRenderingContext2D.createRadialGradient", "createRadialGradient(x1, y1, r1, x2, y2, r2)")}}
+
+

Erzeugt einen kreisförmige Verlauf. Die Parameter beschreiben zwei Kreise, der erste mit dem Mittelpunkt bei (x1, y1) und einem Radius von r1, der zweite mit Mittelpunkt (x2, y2) und demRadius r2.

+
+
+ +

For example:

+ +
var lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
+var radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);
+
+ +

Sobald wir ein canvasGradient-Objekt erzeugt haben, können wir mit der Methode addColorStop() Farben zuordnen.

+ +
+
{{domxref("CanvasGradient.addColorStop", "gradient.addColorStop(position, color)")}}
+
+

Erzeugt einen neuen Farbwert am Verlaufsobjekt. Das Positionsargument ist eine Zahl zwischen 0.0 und 1.0, und es definiert die relative Position der Farbe innerhalb der Verlaufsstrecke. Das Farbargument ist eine Zeichenkette für eine CSS {{cssxref("<color>")}} und beschreibt den Farbwert, den der Farbverlauf bis zur angegebenen Position erreicht hat.

+
+
+ +

Die Anzahl der Farbschritte ist beliebig. Es folgt ein Beispiel für einen einfachen Verlauf von Weiss nach Schwarz.

+ +
var lineargradient = ctx.createLinearGradient(0,0,150,150);
+lineargradient.addColorStop(0, 'white');
+lineargradient.addColorStop(1, 'black');
+
+ +

Beispiel für createLinearGradient

+ +

In diesem Beispiel erzeugen wir zwei unterschiedliche Verläufe. Wie Sie sehen, akzeptieren sowohl strokeStyle als auch fillStyle ein canvasGradient-Objekt als Eingabe.

+ +

 

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Create gradients
+  var lingrad = ctx.createLinearGradient(0,0,0,150);
+  lingrad.addColorStop(0, '#00ABEB');
+  lingrad.addColorStop(0.5, '#fff');
+  lingrad.addColorStop(0.5, '#26C000');
+  lingrad.addColorStop(1, '#fff');
+
+  var lingrad2 = ctx.createLinearGradient(0,50,0,95);
+  lingrad2.addColorStop(0.5, '#000');
+  lingrad2.addColorStop(1, 'rgba(0,0,0,0)');
+
+  // assign gradients to fill and stroke styles
+  ctx.fillStyle = lingrad;
+  ctx.strokeStyle = lingrad2;
+
+  // draw shapes
+  ctx.fillRect(10,10,130,130);
+  ctx.strokeRect(50,50,50,50);
+
+}
+
+ +

 

+ + + +

Der erste Teil beschreibt den Hintergrundverlauf. Wie Sie sehen, haben wir an der gleichen Position zwei unterschiedliche Farben definiert. Das dient zur Erzeugung sprunghafter Farbänderungen - hier von weiss nach grün. Üblicherweise spielt es keine Rolle in welcher Reihenfolge die Farbstufen definiert werden, in diesem speziellen Fall ist die Reihenfolge aber wichtig. Nehmen Sie die Zuordnungen am besten in der tatsächlichen Reihenfolge vor, so beugen Sie möglichen Problemen vor.

+ +

Beim zweiten Verlauf haben wir an der Startposition (0,0) keinen Farbwert definiert, was auch nicht unbedingt nötig ist, weil dann automatisch die Farbe der nächsten Farbstufe verwendet wird. Daher bewirkt die Zuordnung der Farbstufe „schwarz“ an der Position 0.5 automatisch eine einheitliche Schwarzfärbung vom Start Null bis zur Position 0.5.

+ +

{{EmbedLiveSample("A_createLinearGradient_example", "180", "180", "https://mdn.mozillademos.org/files/235/Canvas_lineargradient.png")}}

+ +

Beispiel für createRadialGradient

+ +

In diesem Beispiel werden wir vier kreisförmige Verläufe definieren. Weil wir volle Kontrolle über die Start- und Zielpunkte der Verläufe haben, können wir komplexere Verläufe erzeugen als z.B. die einfacheren Radialverläufe in Photoshop, die uns nur Verläufe mit einem gemeinsamen Mittelpunkt und radial gleichmäßig verteilten Farben erlauben würden.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Create gradients
+  var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);
+  radgrad.addColorStop(0, '#A7D30C');
+  radgrad.addColorStop(0.9, '#019F62');
+  radgrad.addColorStop(1, 'rgba(1,159,98,0)');
+
+  var radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50);
+  radgrad2.addColorStop(0, '#FF5F98');
+  radgrad2.addColorStop(0.75, '#FF0188');
+  radgrad2.addColorStop(1, 'rgba(255,1,136,0)');
+
+  var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40);
+  radgrad3.addColorStop(0, '#00C9FF');
+  radgrad3.addColorStop(0.8, '#00B5E2');
+  radgrad3.addColorStop(1, 'rgba(0,201,255,0)');
+
+  var radgrad4 = ctx.createRadialGradient(0,150,50,0,140,90);
+  radgrad4.addColorStop(0, '#F4F201');
+  radgrad4.addColorStop(0.8, '#E4C700');
+  radgrad4.addColorStop(1, 'rgba(228,199,0,0)');
+
+  // draw shapes
+  ctx.fillStyle = radgrad4;
+  ctx.fillRect(0,0,150,150);
+  ctx.fillStyle = radgrad3;
+  ctx.fillRect(0,0,150,150);
+  ctx.fillStyle = radgrad2;
+  ctx.fillRect(0,0,150,150);
+  ctx.fillStyle = radgrad;
+  ctx.fillRect(0,0,150,150);
+}
+
+ + + +

Wir haben die Mittelpunkte von Start- und Zielkreis etwas gegeneinander versetzt, was einen kugelförmigen 3D-Effekt erzeugt. Man sollte vermeiden, dass sich die Begrenzungen des inneren und des äußeren Kreises schneiden, weil das unberechenbare Effekte erzeugen kann.

+ +

Die letzte Farbstufe in jedem der vier Verläufe verwendet eine völlig transparente Farbe. Für einen gelungenen Übergang zur vorhergehenden Stufe sollten die Farbwerte identisch sein. Das ist im obigen Programm nicht gleich erkennbar, weil unterscheidliche Schreibweisen für die Farbwerte verwendet wurden. Berücksichtigen Sie, dass z.B. beim ersten Farbverlauf #019F62 auch als rgba(1,159,98,1) geschrieben werden könnte.

+ +

{{EmbedLiveSample("A_createRadialGradient_example", "180", "180", "https://mdn.mozillademos.org/files/244/Canvas_radialgradient.png")}}

+ +

Muster

+ +

In einem der Beispiele auf der vorhergehenden Seite haben wir mehrere Schleifen verwendet, um Bilder in Form eines Musters anzuordnen. Allerdings gibt es auch eine weit einfachere Methode: createPattern()

+ +
+
{{domxref("CanvasRenderingContext2D.createPattern", "createPattern(image, type)")}}
+
+

Erzeugt ein neues Muster-Objekt in Canvas und gibt es zurück. image ist eine {{domxref("CanvasImageSource")}} (ein {{domxref("HTMLImageElement")}}, ein weiterer Canvas, ein {{HTMLElement("video")}} Element, oder ähnliches). Der String image gibt an wie das Bild benutzt wird.

+
+
+ +

type enthält eine der folgenden Zeichenketten und bestimmt, wie aus dem Bild ein Muster erzeugt wird.

+ +
+
repeat
+
+

Wiederhole („kachle“) das Bild horizontal und vertikal.

+
+
repeat-x
+
+

Wiederhole das Bild nur horizontal.

+
+
repeat-y
+
+

Nur vertikale Wiederholung.

+
+
no-repeat
+
+

Keine Wiederholung, das Bild wird nur einmal benutzt.

+
+
+ +

 

+ +

Wir verwenden eine Methode ähnlich den vorhergehenden Beispielen zu Verläufen, um ein {{domxref("CanvasPattern")}}-Objekt zu erzeugen. Haben wir das Muster erst erzeugt, können wir es einer fillStyle- oder strokeStyle-Eigenschaft zuordnen.

+ +
var img = new Image();
+img.src = 'someimage.png';
+var ptrn = ctx.createPattern(img,'repeat');
+
+ +

 

+ +
+

Anmerkung: Wie bei drawImage() müssen Sie auch hier sicherstellen, dass das Bild vollständig geladen wurde bevor Sie die Methode aufrufen; andernfalls kann das Muster inkorrekt dargestellt werden.

+
+ +

Beispiel für createPattern

+ +

Im abschließenden Beispiel erzeugen wir ein Muster um es dann dem fillStyle zuzuordnen. Bemerkenswert ist hierbei die Verwendung des onload()-Handlers, um zu gewährleisten, dass die Bilddatei erst dann dem Muster zugeordnet wird, wenn sie vollständig geladen wurde.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // create new image object to use as pattern
+  var img = new Image();
+  img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
+  img.onload = function(){
+
+    // create pattern
+    var ptrn = ctx.createPattern(img,'repeat');
+    ctx.fillStyle = ptrn;
+    ctx.fillRect(0,0,150,150);
+
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_createPattern_example", "180", "180", "https://mdn.mozillademos.org/files/222/Canvas_createpattern.png")}}

+ +

Schatten

+ +

An der Erzeugung von Schatten sind vier Eignschaften beteiligt:

+ +
+
{{domxref("CanvasRenderingContext2D.shadowOffsetX", "shadowOffsetX = float")}}
+
+

Definiert die horizontale Ausdehnung des Schattens vom Objekt weg. Dieser Wert wird nicht durch die Transforamtionsmatrix beeinflusst. Standardwert ist 0.

+
+
{{domxref("CanvasRenderingContext2D.shadowOffsetY", "shadowOffsetY = float")}}
+
+

Wie shadowOffsetY, aber in vertikaler Richtung.

+
+
{{domxref("CanvasRenderingContext2D.shadowBlur", "shadowBlur = float")}}
+
+

Definiert das Ausmaß der Unschärfe; der Wert beschreibt dabei nicht eine bestimmte Anzahl von Pixeln. Er wird nicht durch die Transformationsmatrix beeinflusst. Standardwert ist 0.

+
+
{{domxref("CanvasRenderingContext2D.shadowColor", "shadowColor = color")}}
+
+

Eine CSS Farbdefinition der Schattenfarbe. Standardwert ist voll tranparentes Schwarz.

+
+
+ +

Die Eigenschaften shadowOffsetX und shadowOffsetY bestimmen wie weit weg vom Objekt sich der Schatten in x- und y-Richtung erstrecken soll; diese Werte werden nicht von der aktuellen Tranformationsmatrix beeinflusst. Mit negative Werten verläuft der Schatten nach links und oben, mit positiven nach rechts und unten. Standardwert für beide Parameter ist 0.

+ +

Die Eigenschaft shadowBlur definiert die Ausdehnung der Unschärfezone; der Wert beschreibt dabei keine bestimmte Anzahl von Pixeln und wird durch die Transformationsmatrix nicht verändert. Standardwert: 0.

+ +

Die Eigenschaft shadowColor ist ein regulärer CSS-Farbwert, der die Farbe des Schatteneffektes definiert. Standardwert: voll-transparentes Schwarz.

+ +
+

Note: Schatten werden nur bei source-over compositing operations gezeichnet.

+
+ +

Beispiel für Text mit Schatteneffekt

+ +

Dieses Beispiel zeichnet eine Buchstabenfolge mit einem Schatteneffekt.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  ctx.shadowOffsetX = 2;
+  ctx.shadowOffsetY = 2;
+  ctx.shadowBlur = 2;
+  ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
+
+  ctx.font = "20px Times New Roman";
+  ctx.fillStyle = "Black";
+  ctx.fillText("Sample String", 5, 30);
+}
+
+ + + +

{{EmbedLiveSample("A_shadowed_text_example", "180", "100", "https://mdn.mozillademos.org/files/2505/shadowed-string.png")}}

+ +

Wir werden uns die font-Eigenschaft und die fillText-Methode im nächsten Kapitel über das Zeichnen von Text genauer ansehen.

+ +

Canvas Füllregeln

+ +

Bei der Verwendung von fill ( oder {{domxref("CanvasRenderingContext2D.clip", "clip")}} und {{domxref("CanvasRenderingContext2D.isPointInPath", "isPointinPath")}}) kann man wahlweise eine Füllregel angeben, mit der man über die Berechnung der Windungszahl bestimmt ob ein Punkt innerhalb oder ausserhalb der Strecke liegt und ob die Fläche dementsprechend gefüllt wird oder nicht. Das ist nützlich wenn eine Strecke sich selbst schneidet oder in eine andere eingeschrieben ist.

+ +

Zwei Werte sind möglich:

+ + + +

In diesem  Beispiel verwenden wir die evenodd-Regel.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.beginPath();
+  ctx.arc(50, 50, 30, 0, Math.PI*2, true);
+  ctx.arc(50, 50, 15, 0, Math.PI*2, true);
+  ctx.fill("evenodd");
+}
+ + + +

{{EmbedLiveSample("Canvas_fill_rules", "110", "110", "https://mdn.mozillademos.org/files/9855/fill-rule.png")}}

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_shapes", "Web/API/Canvas_API/Tutorial/Drawing_text")}}

diff --git a/files/de/web/api/canvas_api/tutorial/basic_animations/index.html b/files/de/web/api/canvas_api/tutorial/basic_animations/index.html new file mode 100644 index 0000000000..78ca0ac2fc --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/basic_animations/index.html @@ -0,0 +1,307 @@ +--- +title: Einfache Animationen +slug: Web/Guide/HTML/Canvas_Tutorial/Basic_animations +translation_of: Web/API/Canvas_API/Tutorial/Basic_animations +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}
+ +
+

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.

+
+ +

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.

+ +

Grundlagen der Animation

+ +

Diese Schritte müssen Sie befolgen, um ein neues Frame zu zeichnen:

+ +
    +
  1. Bereinigen Sie die Zeichenfläche (canvas)
    + 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.
  2. +
  3. Sichern Sie den Canvas-Zustand
    + 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 save()-Methode.
  4. +
  5. Zeichnen Sie die animierte Form
    + Hier erzeugen Sie nun endlich die eigentliche Animation.
  6. +
  7. Setzen Sie den Canvas-Zustand zurück.
    + Mit der restore()-Methode können Sie auf den Ursprungszustand zurückwechseln, um ein neues Frame zu erzeugen.
  8. +
+ +

Steuerung einer Animation

+ +

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 for-Schleife zu erzeugen.

+ +

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:

+ +

Updates nach Zeitplan

+ +

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.

+ +
+
{{domxref("WindowTimers.setInterval", "setInterval(function, delay)")}}
+
Ruft die unter function spezifierte Funktion wiederholend nach der in delay (Milisekunden) definierten Zeitspanne auf.
+
{{domxref("WindowTimers.setTimeout", "setTimeout(function, delay)")}}
+
Ruft die spezifizierte Funktion nach der unter delay festgelegten Zeitspanne einmalig auf.
+
{{domxref("Window.requestAnimationFrame()", "requestAnimationFrame(callback)")}}
+
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.
+
+ +

If you don't want any user interaction you can use the setInterval() 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 setTimeout(). By setting {{domxref("EventListener")}}s, we catch any user interaction and execute our animation functions.

+ +
+

In the examples below, we'll use the {{domxref("window.requestAnimationFrame()")}} method to control the animation. The requestAnimationFrame 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 Anatomy of a video game in our Game development zone.

+
+ +

An animated solar system

+ +

This example animates a small model of our solar system.

+ +
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();
+
+ + + +

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

+ +

An animated clock

+ +

This example draws an animated clock, showing your current time.

+ +
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 < 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 < 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 >= 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);
+ + + +

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

+ +

A looping panorama

+ +

In this example, a panorama is scrolled left-to-right. We're using an image of Yosemite National Park we took from Wikipedia, but you could use any image that's larger than the canvas.

+ +
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 > CanvasXSize) { x = CanvasXSize - imgW; } // image larger than canvas
+    if (imgW > CanvasXSize) { clearX = imgW; } // image width larger than canvas
+    else { clearX = CanvasXSize; }
+    if (imgH > 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 <= Canvas Size
+    if (imgW <= CanvasXSize) {
+        // reset, start from beginning
+        if (x > CanvasXSize) { x = -imgW + x; }
+        // draw additional image1
+        if (x > 0) { ctx.drawImage(img, -imgW + x, y, imgW, imgH); }
+        // draw additional image2
+        if (x - imgW > 0) { ctx.drawImage(img, -imgW * 2 + x, y, imgW, imgH); }
+    }
+
+    // if image is > Canvas Size
+    else {
+        // reset, start from beginning
+        if (x > (CanvasXSize)) { x = CanvasXSize - imgW; }
+        // draw aditional image
+        if (x > (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;
+}
+
+ +

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 CanvasXZSize and CanvasYSize variables in the JavaScript code.

+ +
<canvas id="canvas" width="800" height="200"></canvas>
+ +

{{EmbedLiveSample("A_looping_panorama", "830", "230")}}

+ +

Other examples

+ +
+
A basic ray-caster
+
A good example of how to do animations using keyboard controls.
+
Advanced animations
+
We will have a look at some advanced animation techniques and physics in the next chapter.
+
+ +

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

diff --git a/files/de/web/api/canvas_api/tutorial/basic_usage/index.html b/files/de/web/api/canvas_api/tutorial/basic_usage/index.html new file mode 100644 index 0000000000..f89af7fa04 --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/basic_usage/index.html @@ -0,0 +1,154 @@ +--- +title: Grundlagen Canvas +slug: Web/Guide/HTML/Canvas_Tutorial/Grundlagen +tags: + - Canvas + - Graphics + - HTML + - Intermediate + - Tutorial +translation_of: Web/API/Canvas_API/Tutorial/Basic_usage +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial", "Web/API/Canvas_API/Tutorial/Drawing_shapes")}}
+ +
+

Beginnen wir diese Einführung mit einem Blick auf das {{Glossary("HTML")}}-Element {{HTMLElement("canvas")}} an sich. Am Ende wirst du in einem Canvas einen 2D-Context anlegen können und ein erstes Beispiel im Browser gezeichnet haben.

+
+ +

Das <canvas>-Element

+ +

Beginnen wir mit dem {{HTMLElement("canvas")}}-Element:

+ +
<canvas id="tutorial" width="150" height="150"></canvas>
+
+ +

Auf den ersten Blick sieht ein {{HTMLElement("canvas")}} wie ein {{HTMLElement("img")}}-Element aus, mit dem Unterschied, dass es die Attribute src und alt nicht besitzt. Das <canvas>-Element hat nur zwei Attribute - {{htmlattrxref("width", "canvas")}} und {{htmlattrxref("height", "canvas")}}. Diese sind optional und können auch über {{Glossary("DOM")}}-Eigenschaften gesetzt werden. Wenn die Attribute nicht gesetzt sind, bekommt das Element eine Breite von 300px und eine Höhe von 150px. Die Maße des canvas können auch über {{Glossary("CSS")}} gesetzt werden, allerdings wird das Bild dann auf die gesetzte Größe skaliert. Wenn das Verhältnis der CSS-Maße nicht zur ursprünglichen Größe passt, wird das Bild verzerrt.

+ +
+

Hinweis: Wenn das Ergebnis des Renderings verzerrt wirkt, dann setze bitte die Attribute width und height explizit im <canvas> und nicht über CSS.

+
+ +

Das id-Attribut ist eines der globalen Attribute in HTML, welche auf alle HTML-Elemente anwendbar sind (sein sollen). Es empfiehlt sich eine id zu vergeben, dadurch wird der Zugriff mit JavaScript/CSS vereinfacht.

+ +

Auch wenn man nicht mit CSS die Maße des canvas festlegen sollte, kann man jegliche andere CSS-Eigenschaften auf das {{HTMLElement("canvas")}}-Element anwenden (margin, border, background etc). Diese CSS-Regeln haben keinen Effekt auf das eigentliche Zeichnen (anders bei SVG)

+ +
+

Fallback

+ +

Einige ältere Browser unterstützen das {{HTMLElement("canvas")}}-Element nicht, deshalb sollte man einen sogenannten Fallback schreiben, welcher nur den Browsern angezeigt wird, welche das {{HTMLElement("canvas")}}-Element nicht unterstützen. Browser, die das {{HTMLElement("canvas")}}-Element unterstützen zeigen diesen Fallback nicht.

+ +

Beispiele:

+ +
<canvas id="stockGraph" width="150" height="150">
+  aktueller Wechselkurs: $3.15 +0.15
+</canvas>
+
+<canvas id="clock" width="150" height="150">
+  <img src="images/clock.png" width="150" height="150" alt=""/>
+</canvas>
+
+ +

Benötigter </canvas>-Tag

+ +

Im Unterschied zu dem {{HTMLElement("img")}}-Element, benötigt das {{HTMLElement("canvas")}}-Element den Endtag  (</canvas>).

+ +

Wenn kein Fallback definiert wird, reicht ein <canvas id="foo" ...></canvas> völlig aus.

+ +

Der Kontext

+ +

{{HTMLElement("canvas")}} stellt mehrere Kontexte zum Zeichnen auf dem canvas zur Verfügung. Der Standardkontext ist der 2D-Kontext. Es gibt noch WebGL (3D context) basierend auf OpenGL ES.

+ +

Zuerst ist das canvas leer. Mithilfe von JavaScript definiert man den Kontext und zeichnet mit diesem. Das {{HTMLElement("canvas")}}-Element hat eine Methode getContext(), mit der der Kontext definiert wird. getContext() benötigt nur einen String als Argument, den Typ des Kontextes. Für 2D-Grafiken ist dieser String "2d".

+ +
var canvas = document.getElementById("tutorial");
+var ctx = canvas.getContext("2d");
+
+ +

Die erste Zeile speichert in der Variablen canvas den DOM-Knoten unseres canvas mithilfe der {{domxref("document.getElementById()")}}-Methode. Danach wird die getContext()-Methode aufgerufen, um den Kontext in der Variablen ctx zu speichern.

+ +
+

Browserkompatibilität prüfen

+ +

Nicht nur der Fallback kann die Browserkompatibilität prüfen. Auch mit JavaScript ist dies möglich, in dem die Existenz der getContext()-Methode überprüft wird. Beispiel:

+ +
var canvas = document.getElementById('tutorial');
+
+if (canvas.getContext) {
+  var ctx = canvas.getContext('2d');
+  // weiterer Code
+} else {
+  alert("Dein Browser unterstützt das <canvas> Element nicht")
+}
+
+
+
+ +

HTML-Struktur

+ +

Eine einfache HTML-Struktur reicht für unser Tutorial erst einmal völlig aus.

+ +
+

Hinweis: Es gilt als schlechter Stil, Skripte direkt in HTML einzubetten. Wir tun das hier nur aus Gründen der Kompaktheit.

+
+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <title>Canvas tutorial</title>
+    <script type="text/javascript">
+      function draw() {
+        var canvas = document.getElementById('tutorial');
+        if (canvas.getContext) {
+          var ctx = canvas.getContext('2d');
+        }
+      }
+    </script>
+    <style type="text/css">
+      canvas { border: 1px solid black; }
+    </style>
+  </head>
+  <body onload="draw();">
+    <canvas id="tutorial" width="150" height="150"></canvas>
+  </body>
+</html>
+
+ +

Das Skript enthält eine Funktion draw(), die nach dem Laden der Seite ausgeführt wird; dies geschieht durch Hören auf das {{event("load")}}-Ereignis des Dokuments. Diese oder eine ähnliche Funktion könnte auch durch {{domxref("WindowTimers.setTimeout", "window.setTimeout()")}}, {{domxref("WindowTimers.setInterval", "window.setInterval()")}} oder jeden anderen Ereignisbehandler aufgerufen werden, solange die Seite vorher geladen wurde.

+ +

So sieht die Vorlage in Aktion aus. Wie man hier sehen kann, ist sie anfangs leer.

+ +

{{EmbedLiveSample("HTML-Struktur", 160, 160)}}

+ +

Einfaches Beispiel

+ +

Im einfachen Beispiel werden zwei Rechtecke gezeichnet, eins mit Transparenz.

+ +
<!DOCTYPE html>
+<html>
+ <head>
+  <script type="application/javascript">
+    function draw() {
+      var canvas = document.getElementById("canvas");
+      if (canvas.getContext) {
+        var ctx = canvas.getContext("2d");
+
+        ctx.fillStyle = "rgb(200,0,0)";
+        ctx.fillRect(10, 10, 55, 50);
+
+        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
+        ctx.fillRect(30, 30, 55, 50);
+      }
+    }
+  </script>
+ </head>
+ <body onload="draw();">
+   <canvas id="canvas" width="150" height="150"></canvas>
+ </body>
+</html>
+
+ +

Demo:

+ +

{{EmbedLiveSample("Einfaches_Beispiel", 160, 160, "https://mdn.mozillademos.org/files/228/canvas_ex1.png")}}

+ +

{{PreviousNext("Web/Guide/HTML/Canvas_tutorial", "Web/Guide/HTML/Canvas_tutorial/Drawing_shapes")}}

diff --git a/files/de/web/api/canvas_api/tutorial/drawing_shapes/index.html b/files/de/web/api/canvas_api/tutorial/drawing_shapes/index.html new file mode 100644 index 0000000000..f23e7664b5 --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/drawing_shapes/index.html @@ -0,0 +1,453 @@ +--- +title: Formen zeichnen mit Canvas +slug: Web/Guide/HTML/Canvas_Tutorial/Formen_zeichnen +translation_of: Web/API/Canvas_API/Tutorial/Drawing_shapes +--- +

{{CanvasSidebar}}

+ +

Koordinatensystem

+ +

Bevor wir mit dem Zeichnen beginnen können, müssen wir über das canvas grid oder Koordinatensystem sprechen. Unser HTML-Skelett von der vorigen Seite hatte ein canvas-Element mit den Maßen 150 Pixel Höhe und 150 Pixel Breite. Zur Rechten sieht man diesen canvas, über den das Standard-Grid gelegt wurde. Normalerweise entspricht eine Einheit einem Pixel auf dem canvas. Der Ursprung dieses Rasters befindet sich in der oberen linken Ecke, im Punkt (0,0). Alle Elemente werden relativ zum Ursprung positioniert. Die Position des blauen Quadrates ist also x Pixel vom linken Rand und y Pixel vom oberen Rand entfernt, am Punkt (x,y). Später in diesem Tutorial werden wir sehen, wie wir den Ursprung an eine andere Position verschieben, das Koordinatensystem rotieren und sogar skalieren können, aber für's Erste behalten wir die Standardeinstellungen bei.

+ +

Rechtecke zeichnen

+ +

Nicht wie in SVG, unterstützt {{HTMLElement("canvas")}} nur eine einfache Form: das Rechteck. Andere Formen werden mithilfe von Pfaden gezeichnet, dazu später mehr.

+ +
+

Es gibt drei Funktionen, welche auf verschiedenste Art Rechtecke zeichnen:

+ +
+
fillRect(x, y, breite, höhe)
+
Zeichnet ein gefülltes Rechteck
+
strokeRect(x, y, breite, höhe)
+
Zeichnet den Rahmen eines Rechteckes
+
clearRect(x, y, breite, höhe)
+
Der Bereich des Rechteckes wird transparent
+
+ +

Alle drei Funktionen benötigen die selben drei Argumente. x und y beschreibt die Position (siehe Koordinatensystem). breite und höhe beschreiben die Größe des Rechteckes.

+ +

Beispiel mit Rechtecken

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    ctx.fillRect(25, 25, 100, 100);
+    ctx.clearRect(45, 45, 60, 60);
+    ctx.strokeRect(50, 50, 50, 50);
+  }
+}
+ +

Demo:

+ +

{{EmbedLiveSample("Beispiel_mit_Rechtecken", 160, 160, "https://mdn.mozillademos.org/files/245/Canvas_rect.png")}}

+ +

Die fillRect()-Methode zeichnet ein 100px großes, schwarzes Quadrat. Die clearRect()-Methode löscht danach ein 60px großes Quadrat in der Mitte des schwarzen Quadrates. Anschließend zeichnet die strokeRect()-Methode einen 50px großen schwarzen Rahmen in der Mitte.

+ +

Später werden wir zwei alternative Methoden für clearRect() behandeln und sehen, wie man die Füll- und Konturfarbe ändern kann.

+ +

Anders als die Pfadmethoden zeichnen diese drei Rechteckmethoden sofort auf den canvas.

+ +

Pfade zeichnen

+ +

Um andere Formen mithilfe von Pfaden zu zeichnen, benötigt man einige weitere Schritte. Zuerst muss man einen Pfad beginnen. Danach kommen die Pfadbefehle. Zuletzt wird dieser gezeichnet oder gefüllt. Diese Methoden werden hierfür genutzt:

+ +
+
beginPath()
+
Erstellt einen Pfad und beendet ggf. einen älteren.
+
closePath()
+
Beendet den Pfad und verbindet den Startpunkt mit dem Endpunkt.
+
stroke()
+
Zeichnet die Kontur des Pfades.
+
fill()
+
Zeichnet die Füllung des Pfades.
+
+ +

Zuerst wird also die beginPath()-Methode ausgeführt. Danach kommen weitere Pfadanweisung wie Linien oder Kurven. Ein weiterer Aufruf der Methode beginPath() oder ein Aufruf der Methode closePath() löscht die Pfadanweisungen. Optional kann nun closePath() ausgeführt werden.

+ +
Hinweis: Wenn fill() ausgeführt wird, wird automatisch der Pfad beendet, sodass closePath() nicht mehr ausgeführt werden muss. Da ist nicht der Fall wenn stroke() ausgeführt wird.
+ +

Ein Dreieck zeichnen

+ +

So sähe zum Beispiel der Code aus, um ein Dreieck zu zeichnen:

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    ctx.beginPath();
+    ctx.moveTo(75, 50);
+    ctx.lineTo(100, 75);
+    ctx.lineTo(100, 25);
+    ctx.fill();
+  }
+}
+ +

Demo:

+ +

{{EmbedLiveSample("Ein_Dreieck_zeichnen", 160, 160)}}

+ +

Stift bewegen

+ +

Eine sehr sinnvolle Methode ist moveTo(). Sie zeichnet zwar nichts, verändert allerdings die Position des Stiftes, sodass spätere Methoden nicht beim Punkt (0, 0) anfangen.

+ +
+
moveTo(x, y)
+
Bewegt den Stift zu der Koordinate (x , y).
+
+ +

Meist wird direkt nach dem Aufruf von beginPath() moveTo() ausgeführt. Außerdem kann man moveTo() für nichtverbundene Pfade benutzen. Beispiel (moveTo()-Aufrufe sind rote Linien):

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+     var ctx = canvas.getContext('2d');
+
+     ctx.beginPath();
+     ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // Outer circle
+     ctx.moveTo(110, 75);
+     ctx.arc(75, 75, 35, 0, Math.PI, false);    // Mund
+     ctx.moveTo(65, 65);
+     ctx.arc(60, 65, 5, 0, Math.PI * 2, true);  // Linkes Auge
+     ctx.moveTo(95, 65);
+     ctx.arc(90, 65, 5, 0, Math.PI * 2, true);  // Rechtes Auge
+     ctx.stroke();
+  }
+}
+ +

Demo:

+ +

{{EmbedLiveSample("Stift_bewegen", 160, 160, "https://mdn.mozillademos.org/files/252/Canvas_smiley.png")}}

+ +
+

Hinweis: arc() zeichnet einen Kreisbogen. Mehr dazu: {{anch("Kreisbögen")}}.

+
+ +

Linien

+ +

Für Linien verwendet man die lineTo()-Methode:

+ +
+
lineTo(x, y)
+
Zeichnet eine Linie von der aktuellen Stiftposition zu dem Punkt (x, y).
+
+ +

Diese Methode erwartet wie moveTo() zwei Argumente: x und y, welche die Koordinate des Linienendes ist. Der Startpunkt wurde Mithilfe anderer Methoden schon festgelegt. Anschließend ist das Linienende der neue Startpunkt.

+ +

Beispiel mit zwei Dreiecken:

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    // Filled triangle
+    ctx.beginPath();
+    ctx.moveTo(25, 25);
+    ctx.lineTo(105, 25);
+    ctx.lineTo(25, 105);
+    ctx.fill();
+
+    // Stroked triangle
+    ctx.beginPath();
+    ctx.moveTo(125, 125);
+    ctx.lineTo(125, 45);
+    ctx.lineTo(45, 125);
+    ctx.closePath();
+    ctx.stroke();
+  }
+}
+ +

Es beginnt mit der Ausführung von beginPath() um eine neue Form zu beginnen. Danach wird mit moveTo() der Startpunkt festgelegt. Danach werden die Linien gezeichnet.

+ +

{{EmbedLiveSample("Linien", 160, 160, "https://mdn.mozillademos.org/files/238/Canvas_lineTo.png")}}

+ +

Kreisbögen

+ +

Um Kreise oder Kreisbögen zu zeichnen, benutzt man die arc()-Methode.

+ +
+
arc(x, y, radius, startWinkel, endWinkel, uhrzeigersinn)
+
Zeichnet einen Kreisbogen.
+
+ +

Diese Methode benötigt sechs Parameter: x und y sind die Koordinaten des Mittelpunktes des Kreisbogens. radius ist der Radius des Kreisbogens. startWinkel und endWinkel definieren die Punkte auf dem Kreis in rad. Der uhrzeigersinn-Parameter ist true, wenn der Kreisbogen gegen den Uhrzeigersinn und false wenn er im Uhrzeigersinn gezeichnet werden soll.

+ +
+

Hinweis: Die Winkelzahlen werden in rad angegeben, nicht in deg. Die Umrechnungsformel lautet: rad = (Math.PI / 180) * deg.

+
+Dieses Beispiel zeigt Kreisbügen mit den unterschiedlichsten Parametern:
+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    for (var i = 0; i < 4; i++) {
+      for (var j = 0; j < 3; j++) {
+        ctx.beginPath();
+        var x = 25 + j * 50; // x coordinate
+        var y = 25 + i * 50; // y coordinate
+        var radius = 20; // Arc radius
+        var startAngle = 0; // Starting point on circle
+        var endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle
+        var anticlockwise = i % 2 == 0 ? false : true; // clockwise or anticlockwise
+
+        ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
+
+        if (i > 1) {
+          ctx.fill();
+        } else {
+          ctx.stroke();
+        }
+      }
+    }
+  }
+}
+{{EmbedLiveSample("Kreisbögen", 160, 210, "https://mdn.mozillademos.org/files/204/Canvas_arc.png")}} + +

Bezier und quadratische Kurven

+ +

Bézierkurven sind in kubischer und quadratischer Form enthalten. Damit kann man ziemlich komplexe Strukturen zeichnen.

+ +
+
quadraticCurveTo(cp1x, cp1y, x, y)
+
Zeichnet eine quadratische Bézierkurve von der aktuellen Stiftposition zu x und y, mithilfe des Kontrollpunktes mit den Koordinaten (cp1x, cp1y).
+
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
+
Zeichnet eine quadratische Bézierkurve von der aktuellen Stiftposition zu x und y, mithilfe der Kontrollpunkte mit den Koordinaten (cp1x, cp1y) und (cp2x, cp2y).
+
+ +

Den Unterschied zwischen den beiden Funktionen lässt sich am Besten durch die beiden Bilder rechts erklären: Oben die quadratische und unten die kubische.

+ +

Die Kontrollpunkte sind hier rot, die Start- und Endpunkte blau gekennzeichnet.

+ +

Quadratische Bézierkurven

+ +

Dieses Beispiel zeichnet Mithilfe von Bézierkurven eine Sprechblase:

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    // Quadratric curves example
+    ctx.beginPath();
+    ctx.moveTo(75, 25);
+    ctx.quadraticCurveTo(25, 25, 25, 62.5);
+    ctx.quadraticCurveTo(25, 100, 50, 100);
+    ctx.quadraticCurveTo(50, 120, 30, 125);
+    ctx.quadraticCurveTo(60, 120, 65, 100);
+    ctx.quadraticCurveTo(125, 100, 125, 62.5);
+    ctx.quadraticCurveTo(125, 25, 75, 25);
+    ctx.stroke();
+  }
+}
+ +

{{EmbedLiveSample("Quadratische_Bézierkurven", 160, 160, "https://mdn.mozillademos.org/files/243/Canvas_quadratic.png")}}

+ +

Kubische Bézierkurven

+ +

Dieses Beispiel zeichnet ein Herz Mithilfe von kubischen Bézierkurven.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    // Cubic curves example
+    ctx.beginPath();
+    ctx.moveTo(75, 40);
+    ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
+    ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
+    ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
+    ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
+    ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
+    ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
+    ctx.fill();
+  }
+}
+ +

{{EmbedLiveSample("Kubische_Bézierkurven", 160, 160, "https://mdn.mozillademos.org/files/207/Canvas_bezier.png")}}

+ +

Rechtecke

+ +

Es gibt auch eine Rechtecksmethode für Pfade:

+ +
+
rect(x, y, width, height)
+
Zeichnet ein Rechteck.
+
+ +

Nachdem diese Methode ausgeführt wurde, wird automatisch moveTo(0, 0) ausgeführt.

+ +

Kombinationen

+ +

Mit Kombinationen all dieser Pfadmethoden können die komplexesten Formen gezeichnet werden. In diesem letzten Beispiel wird ein Spielcharakter gezeichnet:

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    roundedRect(ctx, 12, 12, 150, 150, 15);
+    roundedRect(ctx, 19, 19, 150, 150, 9);
+    roundedRect(ctx, 53, 53, 49, 33, 10);
+    roundedRect(ctx, 53, 119, 49, 16, 6);
+    roundedRect(ctx, 135, 53, 49, 33, 10);
+    roundedRect(ctx, 135, 119, 25, 49, 10);
+
+    ctx.beginPath();
+    ctx.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7, false);
+    ctx.lineTo(31, 37);
+    ctx.fill();
+
+    for (var i = 0; i < 8; i++) {
+      ctx.fillRect(51 + i * 16, 35, 4, 4);
+    }
+
+    for (i = 0; i < 6; i++) {
+      ctx.fillRect(115, 51 + i * 16, 4, 4);
+    }
+
+    for (i = 0; i < 8; i++) {
+      ctx.fillRect(51 + i * 16, 99, 4, 4);
+    }
+
+    ctx.beginPath();
+    ctx.moveTo(83, 116);
+    ctx.lineTo(83, 102);
+    ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
+    ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
+    ctx.lineTo(111, 116);
+    ctx.lineTo(106.333, 111.333);
+    ctx.lineTo(101.666, 116);
+    ctx.lineTo(97, 111.333);
+    ctx.lineTo(92.333, 116);
+    ctx.lineTo(87.666, 111.333);
+    ctx.lineTo(83, 116);
+    ctx.fill();
+
+    ctx.fillStyle = 'white';
+    ctx.beginPath();
+    ctx.moveTo(91, 96);
+    ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
+    ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
+    ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
+    ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
+    ctx.moveTo(103, 96);
+    ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
+    ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
+    ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
+    ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
+    ctx.fill();
+
+    ctx.fillStyle = 'black';
+    ctx.beginPath();
+    ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
+    ctx.fill();
+
+    ctx.beginPath();
+    ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
+    ctx.fill();
+  }
+}
+
+// A utility function to draw a rectangle with rounded corners.
+
+function roundedRect(ctx, x, y, width, height, radius) {
+  ctx.beginPath();
+  ctx.moveTo(x, y + radius);
+  ctx.lineTo(x, y + height - radius);
+  ctx.arcTo(x, y + height, x + radius, y + height, radius);
+  ctx.lineTo(x + width - radius, y + height);
+  ctx.arcTo(x + width, y + height, x + width, y + height-radius, radius);
+  ctx.lineTo(x + width, y + radius);
+  ctx.arcTo(x + width, y, x + width - radius, y, radius);
+  ctx.lineTo(x + radius, y);
+  ctx.arcTo(x, y, x, y + radius, radius);
+  ctx.stroke();
+}
+ +
+

Demo:

+ +

{{EmbedLiveSample("Kombinationen", 160, 160)}}

+ +

{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Basic_usage", "Web/Guide/HTML/Canvas_tutorial/Using_images")}}

+
+
diff --git a/files/de/web/api/canvas_api/tutorial/drawing_text/index.html b/files/de/web/api/canvas_api/tutorial/drawing_text/index.html new file mode 100644 index 0000000000..1cd3f0bfc6 --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/drawing_text/index.html @@ -0,0 +1,165 @@ +--- +title: Text zeichnen +slug: Web/Guide/HTML/Canvas_Tutorial/Drawing_text +tags: + - Canvas + - Fortgeschritten + - Grafik + - Tutorial +translation_of: Web/API/Canvas_API/Tutorial/Drawing_text +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Applying_styles_and_colors", "Web/API/Canvas_API/Tutorial/Using_images")}}
+ +
+

Nachdem wir im vorigen Kapitel gesehen haben, wie man Gestaltung und Farben anwendet , werden wir nun einen Blick darauf werfen, wie man Text auf ein canvas zeichnet.

+
+ +

Text zeichnen

+ +

Der Rendering-Kontext hält zwei Methoden zum zeichnen von Text bereit:

+ +
+
{{domxref("CanvasRenderingContext2D.fillText", "fillText(text, x, y [, maxWidth])")}}
+
Füllt einen gegebenen Text an den gegebenen (x,y)-Koordinaten. Optional mit einer maximalen Breite, die zu zeichnen ist.
+
{{domxref("CanvasRenderingContext2D.strokeText", "strokeText(text, x, y [, maxWidth])")}}
+
Umrandet einen gegebenen Text an den gegebenen (x,y)-Koordinaten. Optional mit einer maximalen Breite, die zu zeichnen ist.
+
+ +

ein fillText-Beispiel

+ +

Der Text wird mit dem aktuellen fillStyle gefüllt.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.font = '48px serif';
+  ctx.fillText('Hello world', 10, 50);
+}
+ + + +

{{EmbedLiveSample("A_fillText_example", 310, 110)}}

+ +

ein strokeText-Beispiel

+ +

Der Text wird mit dem aktuellen strokeStyle umrandet.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.font = '48px serif';
+  ctx.strokeText('Hello world', 10, 50);
+}
+ + + +

{{EmbedLiveSample("A_strokeText_example", 310, 110)}}

+ +

Text gestalten

+ +

In den obigen Beispielen nutzen wir bereits die font-Eigentschaft, um den Text ein wenig größer als standardmäßig zu machen. Es gibt ein paar mehr Eigenschaften, die es erlauben, das Zeichnen von Text auf dem canvas zu beeinflussen:

+ +
+
{{domxref("CanvasRenderingContext2D.font", "font = value")}}
+
Der aktuell genutzte Text-Stil, der zum Zeichnen genutzt wird. Dieser String nutzt die selbe Syntax wie die CSS {{cssxref("font")}}-Eigenschaft. Die Standard-Schriftart ist 10px sans-serif.
+
{{domxref("CanvasRenderingContext2D.textAlign", "textAlign = value")}}
+
Einstellung der Text-Ausrichtung. Mögliche Werte: start, end, left, right oder center. Der Standard-Wert ist start.
+
{{domxref("CanvasRenderingContext2D.textBaseline", "textBaseline = value")}}
+
Baseline alignment setting. Possible values: top, hanging, middle, alphabetic, ideographic, bottom. The default value is alphabetic.
+
{{domxref("CanvasRenderingContext2D.direction", "direction = value")}}
+
Directionality. Possible values: ltr, rtl, inherit. The default value is inherit.
+
+ +

These properties might be familiar to you, if you have worked with CSS before.

+ +

The following diagram from the WHATWG demonstrates the various baselines supported by the textBaseline property.The top of the em square is
+roughly at the top of the glyphs in a font, the hanging baseline is
+where some glyphs like आ are anchored, the middle is half-way
+between the top of the em square and the bottom of the em square,
+the alphabetic baseline is where characters like Á, ÿ,
+f, and Ω are anchored, the ideographic baseline is
+where glyphs like 私 and 達 are anchored, and the bottom
+of the em square is roughly at the bottom of the glyphs in a
+font. The top and bottom of the bounding box can be far from these
+baselines, due to glyphs extending far outside the em square.

+ +

A textBaseline example

+ +

Edit the code below and see your changes update live in the canvas:

+ +
ctx.font = '48px serif';
+ctx.textBaseline = 'hanging';
+ctx.strokeText('Hello world', 0, 100);
+
+ + + +

{{ EmbedLiveSample('Playable_code', 700, 360) }}

+ +

Advanced text measurements

+ +

In the case you need to obtain more details about the text, the following method allows you to measure it.

+ +
+
{{domxref("CanvasRenderingContext2D.measureText", "measureText()")}}
+
Returns a {{domxref("TextMetrics")}} object containing the width, in pixels, that the specified text will be when drawn in the current text style.
+
+ +

The following code snippet shows how you can measure a text and get its width.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var text = ctx.measureText('foo'); // TextMetrics object
+  text.width; // 16;
+}
+
+ +

Gecko-specific notes

+ +

In Gecko (the rendering engine of Firefox, Firefox OS and other Mozilla based applications), some prefixed APIs were implemented in earlier versions to draw text on a canvas. These are now deprecated and removed, and are no longer guaranteed to work.

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Applying_styles_and_colors", "Web/API/Canvas_API/Tutorial/Using_images")}}

diff --git a/files/de/web/api/canvas_api/tutorial/index.html b/files/de/web/api/canvas_api/tutorial/index.html new file mode 100644 index 0000000000..487f5b7984 --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/index.html @@ -0,0 +1,51 @@ +--- +title: Canvas Tutorial +slug: Web/Guide/HTML/Canvas_Tutorial +translation_of: Web/API/Canvas_API/Tutorial +--- +

+ +

<canvas> ("Leinwand") ist ein HTML Element, auf das man mit Hilfe von Skripten (normalerweise JavaScript) Animationen, Grafiken oder Bilder projiziert. Die Bilder rechts zeigen Beispiele, die sich mit dem <canvas>-Element erstellen lassen.

+ +

Das <canvas>-Element wurde zuerst von Apple für das Mac OS X Dashboard vorgestellt und später in Safari und Google Chrome implementiert. Gecko 1.8-basierte Browser wie Firefox 1.5 und jünger unterstützen dieses Element ebenfalls. Das <canvas>-Element ist Teil der WhatWG Web applications 1.0 Spezifikation (HTML5).

+ +

Dieses Tutorial beschreibt die Grundlagen des Zeichnens von 2d-Grafiken mit dem <canvas>-Element. Die Beispiele sollen die Möglichkeiten des Canvas aufzeigen. Der zugehörige Code dient als Einführung und kann als Vorlage für eigenen Content dienen.

+ +

Vorbereitung

+ +

Das <canvas>-Element ("Leinwand") sinnvoll zu nutzen ist nicht schwierig, setzt aber ein einfaches Verständnis von HTML und JavaScript voraus. Einige ältere Browser unterstützen das <canvas>-Element nicht. Die Standardgröße des Canvas beträgt 300 x 150 Pixel (Breite x Höhe). Selbstverständlich lassen diese sich über die Attribute height und width oder mit Hilfe von CSS ändern. Um auf dem <canvas>-Element zu zeichnen, nutzen Entwickler meist die JavaScript-Canvas-API "on the fly".

+ +

In diesem Tutorial

+ + + +

Siehe auch

+ + + +
{{ Next("Web/Guide/HTML/Canvas_tutorial/Basic_usage") }}
diff --git a/files/de/web/api/canvas_api/tutorial/optimizing_canvas/index.html b/files/de/web/api/canvas_api/tutorial/optimizing_canvas/index.html new file mode 100644 index 0000000000..fc1678c71a --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/optimizing_canvas/index.html @@ -0,0 +1,118 @@ +--- +title: Canvas optimieren +slug: Web/Guide/HTML/Canvas_Tutorial/Canvas_optimieren +tags: + - Canvas + - Fortgeschritten + - Grafik + - HTML + - HTML5 + - Tutorial +translation_of: Web/API/Canvas_API/Tutorial/Optimizing_canvas +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility", "Web/API/Canvas_API/Tutorial/Finale")}}
+ +
+

Das {{HTMLElement("canvas")}}-Element ist einer der am meisten verbreiteten Standards, um 2D-Grafiken im Web zu erzeugen. Es wird oft für Spiele und komplexe Visualisierungen eingesetzt. Reizen Webseiten und Apps das Canvas jedoch zu sehr aus, lässt die Performance nach. Dieser Artikel soll Hilfestellung für die Optimierung der Nutzung des Canvas-Elements geben, um sicherzustellen, dass Ihre Webseite oder App performant ist.

+
+ +

Tipps zur Performance

+ +

Dies ist eine Sammlung von Tipps, die Helfen, die Performance zu verbessern.

+ +

Vorrendern von ähnlichen oder sich wiederholenden Objekten auf einem Offscreen-Canvas

+ +

Falls Sie komplexe Zeichenoperationen in jedem Frame ausführen, ziehen Sie in Betracht, ein Offscreen-Canvas zu erzeugen. Damit können Sie Objekte einmal (oder wann immer Änderungen stattfinden) auf dem Offscreen-Canvas zeichnen und in jedem Frame das Offscreen-Canvas zeichnen.

+ +
myEntity.offscreenCanvas = document.createElement('canvas');
+myEntity.offscreenCanvas.width = myEntity.width;
+myEntity.offscreenCanvas.height = myEntity.height;
+myEntity.offscreenContext = myEntity.offscreenCanvas.getContext('2d');
+
+myEntity.render(myEntity.offscreenContext);
+
+ +

Vermeiden Sie Gleitkomma-Koordinaten

+ +

Falls Sie Objekte auf dem Canvas mit Gleitkommazahlen als Koordinaten zeichnen, müssen Subpixel gerendert werden.

+ +
ctx.drawImage(myImage, 0.3, 0.5);
+
+ +

Dadurch muss der Browser zusätzliche Berechnungen durchführen, um eine Kantenglättung zu erzielen. Um dies zu verhindern, stellen Sie sicher, dass Sie alle Koordinaten in Aufrufen von {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}} runden, zum Beispiel mit Hilfe von {{jsxref("Math.floor()")}}.

+ +

Skalieren Sie keine Bilder in drawImage

+ +

Laden Sie mehrere Größen Ihrer Bilder auf ein Offscreen-Canvas, anstatt sie andauernd in {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}} zu skalieren.

+ +

Benutzen Sie mehrere Canvas-Ebenen für komplexe Szenen

+ +

Möglicherweise haben Sie einige Elemente, die sich oft ändern oder bewegen, während andere Dinge (wie zum Beispiel die UI) sich nie ändern. Diese Situation können Sie optimieren, indem Sie durch die Erzeugung mehrerer Canvas-Elemente Ebenen erzeugen.

+ +

Zum Beispiel können Sie eine UI-Ebene erzeugen, die über allen anderen Ebenen liegt und nur während Benutzereingaben gezeichnet wird. Zusätzlich kann es eine Spiel-Ebene geben, die alle oft veränderten Objekte enthält, sowie eine Hintergrund-Ebene, deren Objekte sich selten ändern.

+ +
<div id="stage">
+  <canvas id="ui-layer" width="480" height="320"></canvas>
+  <canvas id="game-layer" width="480" height="320"></canvas>
+  <canvas id="background-layer" width="480" height="320"></canvas>
+</div>
+
+<style>
+  #stage {
+    width: 480px;
+    height: 320px;
+    position: relative;
+    border: 2px solid black
+  }
+  canvas { position: absolute; }
+  #ui-layer { z-index: 3 }
+  #game-layer { z-index: 2 }
+  #background-layer { z-index: 1 }
+</style>
+
+ +

Nutzen Sie CSS für große Hintergrundbilder

+ +

Falls Sie wie die meisten Spiele ein statisches Hintergrundbild haben, nutzen Sie ein simples {{HTMLElement("div")}}-Element mit der CSS-Eigenschaft {{cssxref("background")}} und positionieren Sie es unter dem Canvas. Dadurch verhindern Sie ein permanentes Neuzeichnen des Bildes in jedem Frame.

+ +

Skalieren Sie das Canvas mit CSS-Transformationen

+ +

CSS-Transformationen sind schneller, da sie die Grafikkarte nutzen. Im besten Fall skalieren Sie das Canvas nicht, oder haben ein kleineres Canvas, das Sie hochskalieren, als dass Sie ein großes Canvas herunterskalieren. Für Firefox OS ist die Zielgröße 480 x 320 px.

+ +
var scaleX = window.innerWidth / canvas.width;
+var scaleY = window.innerHeight / canvas.height;
+
+var scaleToFit = Math.min(scaleX, scaleY);
+var scaleToCover = Math.max(scaleX, scaleY);
+
+stage.style.transformOrigin = '0 0'; //scale from top left
+stage.style.transform = 'scale(' + scaleToFit + ')';
+
+ +

Nutzen Sie das moz-opaque Attribut (nur Gecko)

+ +

Falls Ihr Spiel ein Canvas nutzt, das nicht transparent sein muss, setzen Sie das moz-opaque Attribut im Canvas-Tag. Diese Information kann intern genutzt werden, um das Zeichnen zu optimieren.

+ +
<canvas id="myCanvas" moz-opaque></canvas>
+ +

Weitere Tipps

+ + + +

Siehe auch

+ + + +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility", "Web/API/Canvas_API/Tutorial/Finale")}}

diff --git a/files/de/web/api/canvas_api/tutorial/using_images/index.html b/files/de/web/api/canvas_api/tutorial/using_images/index.html new file mode 100644 index 0000000000..b636807f97 --- /dev/null +++ b/files/de/web/api/canvas_api/tutorial/using_images/index.html @@ -0,0 +1,324 @@ +--- +title: Bilder +slug: Web/Guide/HTML/Canvas_Tutorial/Bilder +translation_of: Web/API/Canvas_API/Tutorial/Using_images +--- +

{{CanvasSidebar}}

+ +

Natürlich können auch Bilder gezeichnet werden. Diese können die unterschiedlichsten Formate haben: PNG, GIF, JPEG oder bestimmte HTML Elemente.

+ +

Um Bilder zu importieren bedarf es bloß zwei Schritte:

+ +
    +
  1. Ein {{domxref("HTMLImageElement")}} Objekt erzeugen oder das HTML Element selektieren.
  2. +
  3. Das Bild mithilfe der drawImage() Funktion zeichnen.
  4. +
+ +

Bilder importieren

+ +

Die canvas API unterstützt folgende Typen als Bilder:

+ +
+
{{domxref("HTMLImageElement")}}
+
Diese Bilder können mithilfe des Image() Konstruktor oder mithilfe eines {{HTMLElement("img")}} Element erstellt werden.
+
{{domxref("HTMLVideoElement")}}
+
Der aktuelle Frame des {{HTMLElement("video")}} Element wird als Bild genutzt.
+
{{domxref("HTMLCanvasElement")}}
+
Ein anderes {{HTMLElement("canvas")}} Element kann ebenfalls als Bild dienen.
+
{{domxref("ImageBitmap")}}
+
Eine hochleistungsfähige Bitmap, welche mit niedriger Verzögerung gerendert werden kann. Sie lässt sich aus allen der oben genannten Quellen, sowie aus mehreren weitern erstellen.
+
+ +

These sources are collectively referred to by the type {{domxref("CanvasImageSource")}}.

+ +

There are several ways to get images for use on a canvas.

+ +

Bilder von derselben Seite

+ +

Um Bilder von derselben Seite zu bekommen, können diese Methoden genutzt werden:

+ + + +

Bilder von anderen Seiten nutzen

+ +

Mithilfe des {{htmlattrxref("crossorigin", "img")}} Attributs eines {{domxref("HTMLImageElement")}} ist es möglich die Erlaubnis zur Benutzung eines Bildes von einer anderen Domain zu nutzen. Wenn die Domain jenen Zugriff gestattet, kann das Image genutzt werden und das Bild wird wie gewollt angezeigt; andernfalls entsteht ein "getaintes Canvas".

+ +

Andere Canvas Elemente benutzen

+ +

Genau wie normalen Bildern auch, können wir ein anderes Canvas durch {{domxref("document.getElementsByTagName()")}} oder {{domxref("document.getElementById()")}} ansprechen.

+ +

Sei dabei sicher, dass du auf deinem Canvas etwas gezeichnet hast, bevor du es im Zielcanvas verwendest.

+ +

Eine sinnvoller Einsatz ist zum Beispiel das zweite Canvas als Vorschaubild (Thumbnail) des ersten zu verwenden.

+ +

Ein Bild von Grund auf

+ +

Eine andere Option ist ein neues {{domxref("HTMLImageElement")}} in JavaScript zu erstellen. Um das zu tun, können wir den Image()-Konstruktor verwenden:

+ +
var img = new Image(); // Erstelle neues Image-Objekt
+img.src = 'myImage.png'; // Setze den Pfad zum Bild
+ +

Wird dieses Skript ausgeführt, fängt das Bild an zu laden.

+ +
+

Achtung: Wenn drawImage() vor dem Laden des Bildes ausgeführt wird, wird nichts passieren (In älteren Browsern kann es eine Fehlermeldung geben). Um jenen Fehler zu vermeiden, muss also sichergestellt werden, dass das load-Event benutzt wird.

+
+ +
var img = new Image();   // Erstelle neues Image-Objekt
+img.addEventListener("load", function() {
+  // füge hier den drawImage()-Befehl ein
+}, false);
+img.src = 'myImage.png'; // Setze den Pfad zum Bild
+
+ +

Wenn nur ein externes Bild geladen werden muss, ist das eine gute Möglichkeit. Wenn jedoch mehrere Bilder benötigt werden, sollte es besser anders gelöst werden. Es ist nicht das Ziel deises Tutorials auf das Vorladen von Bildern einzugehen, aber für eine komplette Lösung kannst du dir JavaScript Image Preloader angucken (ist leder auf Englisch).

+ +

Embedding an image via data: URL

+ +

Another possible way to include images is via the data: url. Data URLs allow you to completely define an image as a Base64 encoded string of characters directly in your code.

+ +
var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
+
+ +

One advantage of data URLs is that the resulting image is available immediately without another round trip to the server. Another potential advantage is that it is also possible to encapsulate in one file all of your CSS, JavaScript, HTML, and images, making it more portable to other locations.

+ +

Some disadvantages of this method are that your image is not cached, and for larger images the encoded url can become quite long.

+ +

Using frames from a video

+ +

You can also use frames from a video being presented by a {{HTMLElement("video")}} element (even if the video is not visible). For example, if you have a {{HTMLElement("video")}} element with the ID "myvideo", you can do this:

+ +
function getMyVideo() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    return document.getElementById('myvideo');
+  }
+}
+
+ +

This returns the {{domxref("HTMLVideoElement")}} object for the video, which, as covered earlier, is one of the objects that can be used as a CanvasImageSource.

+ +

Drawing images

+ +

Once we have a reference to our source image object we can use the drawImage() method to render it to the canvas. As we will see later the drawImage() method is overloaded and has several variants. In its most basic form it looks like this:

+ +
+
drawImage(image, x, y)
+
Draws the CanvasImageSource specified by the image parameter at the coordinates (x, y).
+
+ +

Example: A simple line graph

+ +

In the following example, we will use an external image as the backdrop for a small line graph. Using backdrops can make your script considerably smaller because we can avoid the need for code to generate the background. In this example, we're only using one image, so I use the image object's load event handler to execute the drawing statements. The drawImage() method places the backdrop at the coordinate (0, 0), which is the top-left corner of the canvas.

+ + + +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var img = new Image();
+  img.onload = function(){
+    ctx.drawImage(img,0,0);
+    ctx.beginPath();
+    ctx.moveTo(30,96);
+    ctx.lineTo(70,66);
+    ctx.lineTo(103,76);
+    ctx.lineTo(170,15);
+    ctx.stroke();
+  };
+  img.src = 'https://mdn.mozillademos.org/files/5395/backdrop.png';
+}
+ +

The resulting graph looks like this:

+ +

{{EmbedLiveSample("Example_A_simple_line_graph", 220, 160, "https://mdn.mozillademos.org/files/206/Canvas_backdrop.png")}}

+ +

Scaling

+ +

The second variant of the drawImage() method adds two new parameters and lets us place scaled images on the canvas.

+ +
+
drawImage(image, x, y, width, height)
+
This adds the width and height parameters, which indicate the size to which to scale the image when drawing it onto the canvas.
+
+ +

Example: Tiling an image

+ +

In this example, we'll use an image as a wallpaper and repeat it several times on the canvas. This is done simply by looping and placing the scaled images at different positions. In the code below, the first for loop iterates over the rows. The second for loop iterates over the columns. The image is scaled to one third of its original size, which is 50x38 pixels.

+ +
+

Note: Images can become blurry when scaling up or grainy if they're scaled down too much. Scaling is probably best not done if you've got some text in it which needs to remain legible.

+
+ + + +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var img = new Image();
+  img.onload = function(){
+    for (var i=0;i<4;i++){
+      for (var j=0;j<3;j++){
+        ctx.drawImage(img,j*50,i*38,50,38);
+      }
+    }
+  };
+  img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+}
+ +

The resulting canvas looks like this:

+ +

{{EmbedLiveSample("Example_Tiling_an_image", 160, 160, "https://mdn.mozillademos.org/files/251/Canvas_scale_image.png")}}

+ +

Slicing

+ +

The third and last variant of the drawImage() method has eight parameters. It lets us cut out a section of the source image, then scale and draw it on our canvas.

+ +
+
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
+
Given an image, this function takes the area of the source image specified by the rectangle whose top-left corner is (sx, sy) and whose width and height are sWidth and sHeight and draws it into the canvas, placing it on the canvas at (dx, dy) and scaling it to the size specified by dWidth and dHeight.
+
+ +

To really understand what this does, it may help to look at the image to the right. The first four parameters define the location and size of the slice on the source image. The last four parameters define the rectangle into which to draw the image on the destination canvas.

+ +

Slicing can be a useful tool when you want to make compositions. You could have all elements in a single image file and use this method to composite a complete drawing. For instance, if you want to make a chart you could have a PNG image containing all the necessary text in a single file and depending on your data could change the scale of your chart fairly easily. Another advantage is that you don't need to load every image individually, which can improve load performance.

+ +

Example: Framing an image

+ +

In this example, we'll use the same rhino as in the previous example, but we'll slice out its head and composite it into a picture frame. The picture frame image is a 24-bit PNG which includes a drop shadow. Because 24-bit PNG images include a full 8-bit alpha channel, unlike GIF and 8-bit PNG images, it can be placed onto any background without worrying about a matte color.

+ +
<html>
+ <body onload="draw();">
+   <canvas id="canvas" width="150" height="150"></canvas>
+   <div style="display:none;">
+     <img id="source" src="https://mdn.mozillademos.org/files/5397/rhino.jpg" width="300" height="227">
+     <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150">
+   </div>
+ </body>
+</html>
+
+ +
function draw() {
+  var canvas = document.getElementById('canvas');
+  var ctx = canvas.getContext('2d');
+
+  // Draw slice
+  ctx.drawImage(document.getElementById('source'),
+                33, 71, 104, 124, 21, 20, 87, 104);
+
+  // Draw frame
+  ctx.drawImage(document.getElementById('frame'),0,0);
+}
+ +

We took a different approach to loading the images this time. Instead of loading them by creating new {{domxref("HTMLImageElement")}} objects, we included them as {{HTMLElement("img")}} tags directly in our HTML source and retrieved the images from those. The images are hidden from output by setting the CSS property {{cssxref("display")}} to none for those images.

+ +

{{EmbedLiveSample("Example_Framing_an_image", 160, 160, "https://mdn.mozillademos.org/files/226/Canvas_drawimage2.jpg")}}

+ +

The script itself is very simple. Each {{HTMLElement("img")}} is assigned an ID attribute, which makes them easy to select using {{domxref("document.getElementById()")}}. We then simply use drawImage() to slice the rhino out of the first image and scale him onto the canvas, then draw the frame on top using a second drawImage() call.

+ + + +

In the final example of this chapter, we'll build a little art gallery. The gallery consists of a table containing several images. When the page is loaded, a {{HTMLElement("canvas")}}  element is inserted for each image and a frame is drawn arround it.

+ +

In this case, every image has a fixed width and height, as does the frame that's drawn around them. You could enhance the script so that it uses the image's width and height to make the frame fit perfectly around it.

+ +

The code below should be self-explanatory. We loop through the {{domxref("document.images")}} container and add new canvas elements accordingly. Probably the only thing to note, for those not so familiar with the DOM, is the use of the {{domxref("Node.insertBefore")}} method. insertBefore() is a method of the parent node (a table cell) of the element (the image) before which we want to insert our new node (the canvas element).

+ +
<html>
+ <body onload="draw();">
+     <table>
+      <tr>
+        <td><img src="https://mdn.mozillademos.org/files/5399/gallery_1.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5401/gallery_2.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5403/gallery_3.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5405/gallery_4.jpg"></td>
+      </tr>
+      <tr>
+        <td><img src="https://mdn.mozillademos.org/files/5407/gallery_5.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5409/gallery_6.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5411/gallery_7.jpg"></td>
+        <td><img src="https://mdn.mozillademos.org/files/5413/gallery_8.jpg"></td>
+      </tr>
+     </table>
+     <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150">
+ </body>
+</html>
+
+ +

And here's some CSS to make things look nice:

+ +
body {
+  background: 0 -100px repeat-x url(https://mdn.mozillademos.org/files/5415/bg_gallery.png) #4F191A;
+  margin: 10px;
+}
+
+img {
+  display: none;
+}
+
+table {
+  margin: 0 auto;
+}
+
+td {
+  padding: 15px;
+}
+
+ +

Tying it all together is the JavaScript to draw our framed images:

+ +
function draw() {
+
+  // Loop through all images
+  for (var i=0;i<document.images.length;i++){
+
+    // Don't add a canvas for the frame image
+    if (document.images[i].getAttribute('id')!='frame'){
+
+      // Create canvas element
+      canvas = document.createElement('canvas');
+      canvas.setAttribute('width',132);
+      canvas.setAttribute('height',150);
+
+      // Insert before the image
+      document.images[i].parentNode.insertBefore(canvas,document.images[i]);
+
+      ctx = canvas.getContext('2d');
+
+      // Draw image to canvas
+      ctx.drawImage(document.images[i],15,20);
+
+      // Add frame
+      ctx.drawImage(document.getElementById('frame'),0,0);
+    }
+  }
+}
+ +

{{EmbedLiveSample("Art_gallery_example", 725, 400, "https://mdn.mozillademos.org/files/205/Canvas_art_gallery.jpg")}}

+ +

Controlling image scaling behavior

+ +

As mentioned previously, scaling images can result in fuzzy or blocky artifacts due to the scaling process. You can use the drawing context's imageSmoothingEnabled property to control the use of image smoothing algorithms when scaling images within your context. By default, this is true, meaning images will be smoothed when scaled. You can disable this feature like this:

+ +
ctx.mozImageSmoothingEnabled = false;
+
+ +

{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Drawing_shapes", "Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors")}}

diff --git a/files/de/web/api/document/readystatechange_event/index.html b/files/de/web/api/document/readystatechange_event/index.html new file mode 100644 index 0000000000..9fba4b5dee --- /dev/null +++ b/files/de/web/api/document/readystatechange_event/index.html @@ -0,0 +1,86 @@ +--- +title: readystatechange +slug: Web/Events/readystatechange +tags: + - Referenz + - XMLHttpRequest +translation_of: Web/API/Document/readystatechange_event +--- +

{{ApiRef}}

+ +

Das Ereignis readystatechange wird ausgelöst, wenn sich die Eigenschaft readyState eines Dokumentes verändert hat.

+ +

Allgemeine Information

+ +
+
Spezifikation
+
HTML5
+
Schnittstelle
+
Event
+
Aufsteigend
+
Nein
+
Abbrechbar
+
Nein
+
Ziel
+
Dokument
+
Standardaktion
+
Keine
+
+ +

Eigenschaften

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}{{domxref("EventTarget")}}The event target (the topmost target in the DOM tree).
type {{readonlyInline}}{{domxref("DOMString")}}The type of event.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Whether the event normally bubbles or not.
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Whether the event is cancellable or not.
+ +

Beispiel

+ +
// als Alternative zu DOMContentLoaded
+document.onreadystatechange = function () {
+    if (document.readyState == "interactive") {
+        initApplication();
+    }
+}
+
+ +

Browser-Kompatibilität

+ +

Dieses Ereignis wird vom Internet Explorer schon sehr lange unterstützt und kann daher als Alternative zum DOMContentLoaded-Ereignis genutzt werden (vgl. Anmerkung [2] im Abschnitt Browserkompatibilität).

+ +

Verwandte Ereignisse

+ + diff --git a/files/de/web/api/document_object_model/index.html b/files/de/web/api/document_object_model/index.html new file mode 100644 index 0000000000..a16a254129 --- /dev/null +++ b/files/de/web/api/document_object_model/index.html @@ -0,0 +1,66 @@ +--- +title: DOM +slug: DOM +tags: + - DOM +translation_of: Web/API/Document_Object_Model +translation_of_original: DOM +--- +
Verwendung des W3C DOM Level 1
+Einführung in das W3C DOM.
+ +

Das Document Object Model (DOM) ist eine Programmierschnittstelle für HTML- und XML-Dokumente. Sie bildet die strukturelle Abbildung des Dokuments und ermöglicht Skripten die Veränderung des Inhalts und dessen Präsentation.

+ + + + + + + + +
+

Dokumentation

+ +
+
Gecko DOM Referenz
+
Die Gecko Document Object Model Referenz.
+
Über das Document Object Model
+
Eine kleine Einführung ins DOM.
+
Das DOM und JavaScript
+
Was ist das DOM? Was ist JavaScript? Wie kann ich diese Techniken zusammen benutzen? Dieses Dokument beantwortet diese und weitere Fragen.
+
Verwendung von dynamischen Styles
+
Wie man Style-Informationen mittels DOM erhalten und verändern kann.
+
Das XUL-Interface dynamisch verändern
+
Die Grundlagen zum Manipulieren des XUL-UI mit DOM Methoden.
+
Größe von Elementen bestimmen
+
Dieser Artikel beschreibt, wie man den richtigen Größen von Elementen finden kann.
+
Das Document Object Model in Mozilla (engl.)
+
Eine ältere Dokumentation über das DOM, auf mozilla.org.
+
+ +

Alle zeigen...

+
+

Community

+ +
    +
  • Mozillas DOM Foren:{{ DiscussionList("dev-tech-dom", "mozilla.dev.tech.dom") }}
  • +
+ +

Tools

+ + + + + + +
+ +

 

diff --git a/files/de/web/api/file/typ/index.html b/files/de/web/api/file/typ/index.html deleted file mode 100644 index 28c57a4cb1..0000000000 --- a/files/de/web/api/file/typ/index.html +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: File.type -slug: Web/API/File/Typ -translation_of: Web/API/File/type ---- -
{{APIRef("File API")}}
- -

Gibt den Internet Media Typ (MIME) einer Datei zurück, welche durch ein {{domxref("File")}} Objekt dargestellt wird.

- -

Syntax

- -
var name = file.type;
- -

Wert

- -

Eine Zeichenkette, welche den Internet Media Typ (MIME) enthält und den Typ der Datei angibt, zum Beispiel "image/png" für ein PNG Bild

- -

Beispiel

- -
<input type="file" multiple onchange="showType(this)">
-
- -
function showType(fileInput) {
-  var files = fileInput.files;
-
-  for (var i = 0; i < files.length; i++) {
-    var name = files[i].name;
-    var type = files[i].type;
-    alert("Filename: " + name + " , Type: " + type);
-  }
-}
- -

Hinweis: Basierend auf der aktuellen Implementierung, lesen Browser nicht wirklich den Bytestrom einer Datei, um ihren Medientyp zu bestimmen. Es wird aufgrund der Dateiendung angenommen; eine PNG-Bilddatei, die in .txt umbenannt wird, würde "text/plain" und nicht "image/png" ergeben. Darüber hinaus ist file.type im Allgemeinen nur für gängige Dateitypen wie Bilder, HTML-Dokumente, Audio und Video zuverlässig. Seltene Dateierweiterungen würden eine leere Zeichenkette zurückgeben. Die Client-Konfiguration (z.B. die Windows-Registrierung) kann auch bei gängigen Typen zu unerwarteten Werten führen. Entwicklern wird empfohlen, sich nicht auf diese Eigenschaft als einziges Validierungsschema zu verlassen.

- -

Spezifikation

- - - - - - - - - - - - - - - - -
SpezifikationStatusKommentar
{{SpecName('File API', '#dfn-type', 'type')}}{{Spec2('File API')}}Initiale Definition.
- -

Browser-Kompatibilität

- - - -

{{Compat("api.File.type")}}

- -

Siehe auch

- - diff --git a/files/de/web/api/file/type/index.html b/files/de/web/api/file/type/index.html new file mode 100644 index 0000000000..28c57a4cb1 --- /dev/null +++ b/files/de/web/api/file/type/index.html @@ -0,0 +1,65 @@ +--- +title: File.type +slug: Web/API/File/Typ +translation_of: Web/API/File/type +--- +
{{APIRef("File API")}}
+ +

Gibt den Internet Media Typ (MIME) einer Datei zurück, welche durch ein {{domxref("File")}} Objekt dargestellt wird.

+ +

Syntax

+ +
var name = file.type;
+ +

Wert

+ +

Eine Zeichenkette, welche den Internet Media Typ (MIME) enthält und den Typ der Datei angibt, zum Beispiel "image/png" für ein PNG Bild

+ +

Beispiel

+ +
<input type="file" multiple onchange="showType(this)">
+
+ +
function showType(fileInput) {
+  var files = fileInput.files;
+
+  for (var i = 0; i < files.length; i++) {
+    var name = files[i].name;
+    var type = files[i].type;
+    alert("Filename: " + name + " , Type: " + type);
+  }
+}
+ +

Hinweis: Basierend auf der aktuellen Implementierung, lesen Browser nicht wirklich den Bytestrom einer Datei, um ihren Medientyp zu bestimmen. Es wird aufgrund der Dateiendung angenommen; eine PNG-Bilddatei, die in .txt umbenannt wird, würde "text/plain" und nicht "image/png" ergeben. Darüber hinaus ist file.type im Allgemeinen nur für gängige Dateitypen wie Bilder, HTML-Dokumente, Audio und Video zuverlässig. Seltene Dateierweiterungen würden eine leere Zeichenkette zurückgeben. Die Client-Konfiguration (z.B. die Windows-Registrierung) kann auch bei gängigen Typen zu unerwarteten Werten führen. Entwicklern wird empfohlen, sich nicht auf diese Eigenschaft als einziges Validierungsschema zu verlassen.

+ +

Spezifikation

+ + + + + + + + + + + + + + + + +
SpezifikationStatusKommentar
{{SpecName('File API', '#dfn-type', 'type')}}{{Spec2('File API')}}Initiale Definition.
+ +

Browser-Kompatibilität

+ + + +

{{Compat("api.File.type")}}

+ +

Siehe auch

+ + diff --git a/files/de/web/api/file/using_files_from_web_applications/index.html b/files/de/web/api/file/using_files_from_web_applications/index.html new file mode 100644 index 0000000000..c44ac4b9df --- /dev/null +++ b/files/de/web/api/file/using_files_from_web_applications/index.html @@ -0,0 +1,512 @@ +--- +title: Zugriff auf Dateien von Webapplikationen +slug: Web/API/File/Zugriff_auf_Dateien_von_Webapplikationen +translation_of: Web/API/File/Using_files_from_web_applications +--- +

Mithilfe der File API, welche mit HTML5 zum DOM hinzugefügt wurde, ist es nun für Webinhalte möglich den Benutzer lokale Dateien auswählen zu lassen und den Inhalt dieser Dateien auszulesen. Die Auswahl kann entweder durch das HTML Element {{ HTMLElement("input") }} oder durch Drag and Drop erfolgen.

+ +

Es ist auch möglich die DOM File API von Erweiterungen oder anderem Chrome Code zu benutzen. In diesem Fall müssen einige zusätzliche Dinge beachtet werden, die im Abschnitt Using the DOM File API in chrome code näher erläutert werden.

+ +

Auf ausgewählte Dateien zugreifen

+ +

Gehen wir von folgendem HTML-Code aus:

+ +
<input type="file" id="input">
+ +

Die File API erlaubt den Zugriff auf eine {{ domxref("FileList") }} mit {{ domxref("File") }} Objekten, die die vom Benutzer ausgewählten Dateien repräsentieren.

+ +

Wenn der Benutzer nur eine Datei auswählt, dann muss nur die erste Datei in der Liste betrachtet werden.

+ +

Eine ausgewählte Datei erhält man über den üblichen DOM Selektor:

+ +
var selectedFile = document.getElementById('input').files[0];
+ +

Oder mit einem jQuery Selektor:

+ +
var selectedFile = $('#input').get(0).files[0];
+
+var selectedFile = $('#input')[0].files[0];
+ +
+

Tritt der Fehler "files is undefined" auf:
+ Es wurde das falsche HTML element ausgewählt. Es ist zu beachten, dass ein jQuery Selektor eine Liste von zutreffenden DOM Elementen zurückliefert. Das richtige DOM Element muss ausgewählt werden, um "files" darauf anzuwenden.

+
+ +

Zugriff auf ausgewählte Dateien über den Change Event

+ +

Es ist auch möglich (aber nicht unbedingt erforderlich), auf die {{ domxref("FileList") }} über das change event zuzugreifen:

+ +
<input type="file" id="input" onchange="handleFiles(this.files)">
+ +

Wenn der Benutzer eine Datei auswählt, wird die Funktion handleFiles() aufgerufen. Als Parameter wird die {{ domxref("FileList") }} übergeben. Sie enthält die {{ domxref("File") }} Objekte, die die vom Benutzer ausgewählten Dateien repräsentieren.

+ +

Soll der Benutzer mehrere Dateien auswählen können, dann kann einfach das Attribut multiple auf das input Element angewendet werden:

+ +
<input type="file" id="input" multiple onchange="handleFiles(this.files)">
+ +

In diesem Fall enthält die Dateiliste, die an die handleFiles() Funktion übergeben wird, ein {{ domxref("File") }} für jede Datei, die der Benutzer ausgewählt hat.

+ +

Dynamisch einen Change Listener hinzufügen

+ +

Wurde das input Feld mit einer JavaScript Bibliothek wie jQuery erzeugt, dann muss der event Listener mit {{ domxref("element.addEventListener()") }} hinzugefügt werden:

+ +
var inputElement = document.getElementById("input");
+inputElement.addEventListener("change", handleFiles, false);
+function handleFiles() {
+  var fileList = this.files; /* Jetzt kann die Dateiliste verarbeitet werden */
+}
+ +

In diesem Fall ist die handleFiles() Funktion selbst der Event Handler, anders als in den vorigen Beispielen, wo sie von einem Event Handler aufgerufen wurde.

+ +

Informationen über die ausgewählte(n) Datei(en)

+ +

Das {{ domxref("FileList") }} Objekt, das vom DOM geliefert wurde, enthält eine Liste aller Dateien, die vom Benutzer ausgewählt wurden. Jede der Dateien wird durch ein {{ domxref("File") }} Objekt repräsentiert. Die Anzahl der ausgewälten Dateien kann über das length Attribut der Liste ermittelt werden:

+ +
var numFiles = files.length;
+ +

Die einzelnen {{ domxref("File") }} Objekte erhält man, indem das {{ domxref("FileList") }} Objekt einfach wie ein Array behandelt wird:

+ +
for (var i = 0, numFiles = files.length; i < numFiles; i++) {
+  var file = files[i];
+  ..
+}
+
+ +

Diese Schleife iteriert über alle Dateien in der Liste.

+ +

Im {{ domxref("File") }} Objekt liefern drei Attribute hilfreiche Informationen über die Datei:

+ +
+
name
+
Der Dateiname als read-only String. Es ist nur der Dateiname enthalten, keine Pfadinformationen.
+
size
+
Die Dateigröße in Bytes als read-only 64-Bit Integer.
+
type
+
Der MIME type der Datei als read-only String oder "", wenn der Typ nicht ermittelt werden konnte.
+
+ +

Beispiel: Dateigröße anzeigen

+ +

Das folgende Beispiel zeigt eine mögliche Verwendung des size Attributs:

+ +
<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>File(s) size</title>
+<script>
+function updateSize() {
+  var nBytes = 0,
+      oFiles = document.getElementById("uploadInput").files,
+      nFiles = oFiles.length;
+  for (var nFileId = 0; nFileId < nFiles; nFileId++) {
+    nBytes += oFiles[nFileId].size;
+  }
+  var sOutput = nBytes + " bytes";
+  // optional code for multiples approximation
+  for (var aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
+    sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
+  }
+  // end of optional code
+  document.getElementById("fileNum").innerHTML = nFiles;
+  document.getElementById("fileSize").innerHTML = sOutput;
+}
+</script>
+</head>
+
+<body onload="updateSize();">
+<form name="uploadForm">
+<p><input id="uploadInput" type="file" name="myFiles" onchange="updateSize();" multiple> selected files: <span id="fileNum">0</span>; total size: <span id="fileSize">0</span></p>
+<p><input type="submit" value="Send file"></p>
+</form>
+</body>
+</html>
+
+ +

Verstecktes input Element mit click() Methode

+ +

Ab Gecko 2.0 {{ geckoRelease("2.0") }}, kann das zugegebenermaßen hässliche Datei-{{ HTMLElement("input") }} Element versteckt und durch eigene Oberflächenelemente zum Zugriff auf den Datei-Öffnen-Dialog ersetzt werden. Dazu wird das input Element mit dem CSS Stil display:none versehen und mit der Methode {{ domxref("element.click","click()") }} aufgerufen.

+ +

Gehen wir von folgendem HTML-Code aus:

+ +
<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
+<a href="#" id="fileSelect">Bitte Dateien auswählen</a>
+
+
+ +

Der Code, der auf den click Event reagiert, kann wie folgt aussehen:

+ +
var fileSelect = document.getElementById("fileSelect"),
+  fileElem = document.getElementById("fileElem");
+
+fileSelect.addEventListener("click", function (e) {
+  if (fileElem) {
+    fileElem.click();
+  }
+  e.preventDefault(); // prevent navigation to "#"
+}, false);
+
+ +

Das neue Oberflächenelement zum Zugriff auf den Datei-Öffnen-Dialog kann nun beliebig gestaltet werden.

+ +

label Element zum Auslösen eines versteckten input Elements

+ +

Wird statt eines Links ({{ HTMLElement("a") }} Element) ein {{ HTMLElement("label") }} Element verwendet, dann kann das versteckte input Element auch ohne JavaScript ausgelöst werden. Dabei darf das input-Element aber weder mit display: none noch mit visibility: hidden versteckt werden, weil es in diesen Fällen nicht mit der Tastatur bedienbar wäre. Es darf nur visuell versteckt werden.

+ +

Gehen wir von folgendem HTML-Code aus:

+ +
<input type="file" id="fileElem" multiple accept="image/*" class="visually-hidden">
+<label for="fileElem">Bitte Dateien auswählen</label>
+
+ +

und folgendem CSS:

+ +
.visually-hidden {
+  position: absolute !important;
+  height: 1px;
+  width: 1px;
+  overflow: hidden;
+  clip: rect(1px, 1px, 1px, 1px);
+}
+
+input.visually-hidden:focus + label {
+  outline: thin dotted;
+}
+
+ +

Bei einem Klick auf das label Element wird der Datei-Öffnen-Dialog angezeigt.

+ +

Das label Element kann per CSS beliebig gestaltet werden. Man muss aber das Label des versteckten Eingabefelds hervorheben, wenn dieses den Fokus hat, d.h. per Tab-Taste angewählt wurde – sei es durch outline wie im obigen Beispiel oder background-color oder box-shadow. (Gegenwärtig stellt Firefox das Label von fokussierten <input type="file">-Elementen nicht hervorgehoben dar.)

+ +

Dateien per Drag and Drop auswählen

+ +

Es ist auch möglich, per Drag and Drop Dateien an die Webanwendung zu übergeben.

+ +

Zuerst muss eine Drop Zone eingerichtet werden. Welcher Teil der Oberfläche Drops entgegen nimmt, ist vom Design der Anwendung abhängig. Generell ist das Empfangen von Drop Events aber einfach:

+ +
var dropbox;
+
+dropbox = document.getElementById("dropbox");
+dropbox.addEventListener("dragenter", dragenter, false);
+dropbox.addEventListener("dragover", dragover, false);
+dropbox.addEventListener("drop", drop, false);
+
+ +

In diesem Beispiel wird das Element mit der ID dropbox als Drop Zone verwendet. Das wird durch Registrieren der Listener für {{event('dragenter')}}, {{event('dragover')}} und {{event('drop')}} Events erreicht.

+ +

dragenter und dragover benötigen wir in unserem Fall eigentlich nicht. Wir verhindern lediglich eine weitere Behandlung der Events durch Aufruf von e.stopPropagation() und e.preventDefault():

+ +
function dragenter(e) {
+  e.stopPropagation();
+  e.preventDefault();
+}
+
+function dragover(e) {
+  e.stopPropagation();
+  e.preventDefault();
+}
+
+ +

Das Wesentliche geschieht in der Behandlung des drop Events:

+ +
function drop(e) {
+  e.stopPropagation();
+  e.preventDefault();
+
+  var dt = e.dataTransfer;
+  var files = dt.files;
+
+  handleFiles(files);
+}
+
+ +

Hier ermitteln wir das dataTransfer Feld aus dem Event, entnehmen ihm die Dateiliste und übergeben diese an handleFiles(). Von da an ist die Weiterverarbeitung die gleiche wie bei Verwendung des input Elements.

+ +

Beispiel: Thumbnails von ausgewählten Bildern anzeigen

+ +

Gehen wir davon aus, dass auf einer Foto-Webseite mit HTML5 eine Thumbnail Vorschau von Bildern angezeigt werden soll, bevor sie hochgeladen werden. Das input Element oder die Drop Zone kann wie oben beschrieben eingerichtet werden. Diese rufen die folgende handleFiles() Funktion auf:

+ +
function handleFiles(files) {
+  for (var i = 0; i < files.length; i++) {
+    var file = files[i];
+    var imageType = /^image\//;
+
+    if (!imageType.test(file.type)) {
+      continue;
+    }
+
+    var img = document.createElement("img");
+    img.classList.add("obj");
+    img.file = file;
+    preview.appendChild(img); // Gehen wird davon aus, dass "preview" das div-Element ist, in dem der Inhalt angezeigt wird.
+
+    var reader = new FileReader();
+    reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
+    reader.readAsDataURL(file);
+  }
+}
+
+ +

Mit der Schleife wird über die ausgewählten Dateien iteriert. Bei jeder Datei wird mit Hilfe des type Attributs (indem der Reguläre Ausdruck "^image\/" darauf angewendet wird) geprüft, ob es sich um eine Bild Datei handelt. Liegt eine Bild Datei vor, dann wird ein neues img Element erzeugt. Mit CSS können z.B. hübsche Ränder oder Schatten erzeugt und die Größe des Bildes festgelegt werden, das muss also nicht hier im Code erfolgen.

+ +

Jedem Bild wird die CSS Klasse obj zugewiesen, so dass es einfach ist, es im DOM Baum zu finden. Außerdem wird jedem Bild im file Attribut das {{ domxref("File") }} Objekt für das Bild zugewiesen; das benötigen wir später für den tatsächlichen Upload der Datei. Mit {{ domxref("Node.appendChild()") }} wird das neue Thumbnail dem preview Bereich unserer Anwendung hinzugefügt.

+ +

Dann erstellen wir einen {{ domxref("FileReader") }}, um das Bild asynchron zu laden und es dem img Element hinzuzufügen. Nach dem Erstellen des FileReader Objektes definieren wir die onload Funktion und rufen readAsDataURL() auf, um die Leseoperation im Hintergrund zu starten. Ist der komplette Inhalt der Bilddatei geladen, dann wird er in eine data: URL umgewandelt, die an den onload Callback übergeben wird. Unsere Implementation dieser Routine setzt das src Attribut des img Elements auf das geladene Bild, was dazu führt, dass das Bild im Thumbnail auf dem Bildschirm des Benutzers erscheint.

+ +

Verwendung von Objekt URLs

+ +

Mit Gecko 2.0 {{ geckoRelease("2.0") }} wurde die Unterstützung für die DOM Methoden {{ domxref("window.URL.createObjectURL()") }} und {{ domxref("window.URL.revokeObjectURL()") }} eingeführt. Mit ihnen lassen sich einfache URL Strings erzeugen, die beliebige Daten referenzieren, auf die per DOM {{ domxref("File") }} Objekte zugegriffen werden kann; einschließlich lokale Dateien auf dem Computer des Anwenders.

+ +

Wenn man aus HTML mit einer URL auf ein {{ domxref("File") }} Objekt verweisen möchte, dann kann man dafür eine Objekt URL erzeugen:

+ +
var objectURL = window.URL.createObjectURL(fileObj);
+ +

Die Objekt URL ist ein String, der das {{ domxref("File") }} Objekt identifiziert. Jedes Mal, wenn {{ domxref("window.URL.createObjectURL()") }} aufgerufen wird, wird eine eindeutige Objekt URL erzeugt, auch wenn vorher bereits eine Objekt URL für diese Datei erzeugt wurde. Jede von ihnen muss wieder freigegeben werden. Sie werden automatisch freigegeben, wenn das Dokument entladen wird. Wenn ihre Anwendung die Objekt URLs dynamisch verwendet, dann sollten Sie sie auch explizit durch Aufruf von {{ domxref("window.URL.revokeObjectURL()") }} freigeben:

+ +
window.URL.revokeObjectURL(objectURL);
+ +

Beispiel: Mit Objekt URLs Bilder anzeigen

+ +

Dieses Beispiel verwendet Objekt URLs, um Thumbnails von Bildern anzuzeigen. Darüber hinaus werden weitere Dateiinformationen einschließlich Name und Größe angezeigt. Live Ansicht des Beispiels.

+ +

Das HTML für die Oberfläche sieht folgendermaßen aus:

+ +
<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
+<a href="#" id="fileSelect">Bitte Dateien auswählen</a>
+<div id="fileList">
+  <p>Keine Dateien ausgewählt!</p>
+</div>
+
+ +

Das erstellt ein Datei {{ HTMLElement("input") }} Element, zusammen mit einem Link, der den Datei-Öffnen-Dialog anzeigt (so kann das Datei input Element versteckt werden, da es nicht so attraktiv aussieht). Das wird oben im Abschnitt {{ anch("Verstecktes input Element mit click() Methode") }} näher beschrieben.

+ +

Die handleFiles() Methode sieht folgendermaßen aus:

+ +
window.URL = window.URL || window.webkitURL;
+
+var fileSelect = document.getElementById("fileSelect"),
+    fileElem = document.getElementById("fileElem"),
+    fileList = document.getElementById("fileList");
+
+fileSelect.addEventListener("click", function (e) {
+  if (fileElem) {
+    fileElem.click();
+  }
+  e.preventDefault(); // prevent navigation to "#"
+}, false);
+
+function handleFiles(files) {
+  if (!files.length) {
+    fileList.innerHTML = "<p>No files selected!</p>";
+  } else {
+    var list = document.createElement("ul");
+    for (var i = 0; i < files.length; i++) {
+      var li = document.createElement("li");
+      list.appendChild(li);
+
+      var img = document.createElement("img");
+      img.src = window.URL.createObjectURL(files[i]);
+      img.height = 60;
+      img.onload = function() {
+        window.URL.revokeObjectURL(this.src);
+      }
+      li.appendChild(img);
+      var info = document.createElement("span");
+      info.innerHTML = files[i].name + ": " + files[i].size + " bytes";
+      li.appendChild(info);
+    }
+  }
+}
+
+ +

Das {{ HTMLElement("div") }} Element mit der ID fileList wird ermittelt. Das ist der Block, in den wir später unsere Dateiliste einschließlich Thumbnails einfügen.

+ +

Ist das {{ domxref("FileList") }} Objekt, das an handleFiles() übergeben wird, null, dann wird einfach "No files selected!" ausgegeben. Ansonsten bauen wir unsere Dateiliste wie folgt:

+ +
    +
  1. Ein neues ({{ HTMLElement("ul") }}) Element wird erzeugt.
  2. +
  3. Das neue Listenelement wird dem {{ HTMLElement("div") }} Block hinzugefügt durch Aufruf der {{ domxref("element.appendChild()") }} Methode.
  4. +
  5. Für jedes {{ domxref("File") }} in der {{ domxref("FileList") }}, die durch files repräsentiert wird: +
      +
    1. Erzeuge ein neues Listenelement ({{ HTMLElement("li") }}) und füge es der Liste hinzu.
    2. +
    3. Erzeuge ein neues Bildelement ({{ HTMLElement("img") }}).
    4. +
    5. Setze das src Attribut des Bildes auf die neue Objekt URL, die die Datei repräsentiert, wobei die Objekt URL mit {{ domxref("window.URL.createObjectURL()") }} erzeugt wird.
    6. +
    7. Setze die Bildgröße auf 60 Pixel.
    8. +
    9. Richte den onload Event Handler ein, um die Objekt URL wieder freizugeben, da sie nach dem Laden des Bildes nicht mehr benötigt wird. Das wird durch Aufruf der Methode {{ domxref("window.URL.revokeObjectURL()") }} und Übergabe des Objekt URL Strings aus img.src gemacht.
    10. +
    11. Füge das neue Bildelement dem Listenelement hinzu.
    12. +
    +
  6. +
+ +

Example: Uploading a user-selected file

+ +

Another thing you might want to do is let the user upload the selected file or files (such as the images selected using the previous example) to a server. This can be done asynchronously very easily.

+ +

Creating the upload tasks

+ +

Continuing with the code that built the thumbnails in the previous example, recall that every thumbnail image is in the CSS class obj with the corresponding {{ domxref("File") }} attached in a file attribute. This allows us to select all of the images the user has chosen for uploading using {{ domxref("Document.querySelectorAll()") }}, like this:

+ +
function sendFiles() {
+  var imgs = document.querySelectorAll(".obj");
+
+  for (var i = 0; i < imgs.length; i++) {
+    new FileUpload(imgs[i], imgs[i].file);
+  }
+}
+
+ +

Line 2 fetches a {{ domxref("NodeList") }}, called imgs, of all the elements in the document with the CSS class obj. In our case, these will be all of the image thumbnails. Once we have that list, it's trivial to go through it and create a new FileUpload instance for each. Each of these handles uploading the corresponding file.

+ +

Handling the upload process for a file

+ +

The FileUpload function accepts two inputs: an image element and a file from which to read the image data.

+ +
function FileUpload(img, file) {
+  var reader = new FileReader();
+  this.ctrl = createThrobber(img);
+  var xhr = new XMLHttpRequest();
+  this.xhr = xhr;
+
+  var self = this;
+  this.xhr.upload.addEventListener("progress", function(e) {
+        if (e.lengthComputable) {
+          var percentage = Math.round((e.loaded * 100) / e.total);
+          self.ctrl.update(percentage);
+        }
+      }, false);
+
+  xhr.upload.addEventListener("load", function(e){
+          self.ctrl.update(100);
+          var canvas = self.ctrl.ctx.canvas;
+          canvas.parentNode.removeChild(canvas);
+      }, false);
+  xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
+  xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
+  reader.onload = function(evt) {
+    xhr.sendAsBinary(evt.target.result);
+  };
+  reader.readAsBinaryString(file);
+}
+
+ +

The FileUpload() function shown above creates a throbber, which is used to display progress information, and then creates an {{ domxref("XMLHttpRequest") }} to handle uploading the data.

+ +

Before actually transferring the data, several preparatory steps are taken:

+ +
    +
  1. The XMLHttpRequest's upload progress listener is set to update the throbber with new percentage information so that as the upload progresses the throbber will be updated based on the latest information.
  2. +
  3. The XMLHttpRequest's upload load event handler is set to update the throbber progress information to 100% to ensure the progress indicator actually reaches 100% (in case of granularity quirks during the process). It then removes the throbber since it's no longer needed. This causes the throbber to disappear once the upload is complete.
  4. +
  5. The request to upload the image file is opened by calling XMLHttpRequest's open() method to start generating a POST request.
  6. +
  7. The MIME type for the upload is set by calling the XMLHttpRequest function overrideMimeType(). In this case, we're using a generic MIME type; you may or may not need to set the MIME type at all depending on your use case.
  8. +
  9. The FileReader object is used to convert the file to a binary string.
  10. +
  11. Finally, when the content is loaded the XMLHttpRequest function sendAsBinary() is called to upload the file's content.
  12. +
+ +
Note: The non-standard sendAsBinary method in the example above is considered deprecated as of Gecko 31 {{ geckoRelease(31) }} and will be removed soon. The standard send(Blob data) method can be used instead.
+ +

Handling the upload process for a file, asynchronously

+ +
<?php
+if (isset($_FILES['myFile'])) {
+    // Example:
+    move_uploaded_file($_FILES['myFile']['tmp_name'], "uploads/" . $_FILES['myFile']['name']);
+    exit;
+}
+?><!DOCTYPE html>
+<html>
+<head>
+    <title>dnd binary upload</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <script type="text/javascript">
+        function sendFile(file) {
+            var uri = "/index.php";
+            var xhr = new XMLHttpRequest();
+            var fd = new FormData();
+
+            xhr.open("POST", uri, true);
+            xhr.onreadystatechange = function() {
+                if (xhr.readyState == 4 && xhr.status == 200) {
+                    // Handle response.
+                    alert(xhr.responseText); // handle response.
+                }
+            };
+            fd.append('myFile', file);
+            // Initiate a multipart/form-data upload
+            xhr.send(fd);
+        }
+
+        window.onload = function() {
+            var dropzone = document.getElementById("dropzone");
+            dropzone.ondragover = dropzone.ondragenter = function(event) {
+                event.stopPropagation();
+                event.preventDefault();
+            }
+
+            dropzone.ondrop = function(event) {
+                event.stopPropagation();
+                event.preventDefault();
+
+                var filesArray = event.dataTransfer.files;
+                for (var i=0; i<filesArray.length; i++) {
+                    sendFile(filesArray[i]);
+                }
+            }
+        }
+    </script>
+</head>
+<body>
+    <div>
+        <div id="dropzone" style="margin:30px; width:500px; height:300px; border:1px dotted grey;">Drag & drop your file here...</div>
+    </div>
+</body>
+</html>
+
+ +

Example: Using object URLs to display PDF

+ +

Object URLs can be used for other things than just images! They can be used to display embedded PDF files or any other resources that can be displayed by the browser.

+ +

In Firefox, to have the PDF appear embedded in the iframe (rather than proposed as a downloaded file), the preference pdfjs.disabled must be set to false {{non-standard_inline()}}.

+ +
<iframe id="viewer">
+
+ +

And here is the change of the src attribute:

+ +
var obj_url = window.URL.createObjectURL(blob);
+var iframe = document.getElementById('viewer');
+iframe.setAttribute('src', obj_url);
+window.URL.revokeObjectURL(obj_url);
+ +

Example: Using object URLs with other file types

+ +

You can manipulate files of other formats the same way. Here is how to preview uploaded video:

+ +
var video = document.getElementById('video');
+var obj_url = window.URL.createObjectURL(blob);
+video.src = obj_url;
+video.play()
+window.URL.revokeObjectURL(obj_url);
+ +

Specifications

+ + + +

See also

+ + + +

{{ HTML5ArticleTOC() }}

+ +

{{ languages( { "zh-cn": "zh-cn/Using_files_from_web_applications", "ja": "ja/Using_files_from_web_applications" } ) }}

diff --git a/files/de/web/api/file/zugriff_auf_dateien_von_webapplikationen/index.html b/files/de/web/api/file/zugriff_auf_dateien_von_webapplikationen/index.html deleted file mode 100644 index c44ac4b9df..0000000000 --- a/files/de/web/api/file/zugriff_auf_dateien_von_webapplikationen/index.html +++ /dev/null @@ -1,512 +0,0 @@ ---- -title: Zugriff auf Dateien von Webapplikationen -slug: Web/API/File/Zugriff_auf_Dateien_von_Webapplikationen -translation_of: Web/API/File/Using_files_from_web_applications ---- -

Mithilfe der File API, welche mit HTML5 zum DOM hinzugefügt wurde, ist es nun für Webinhalte möglich den Benutzer lokale Dateien auswählen zu lassen und den Inhalt dieser Dateien auszulesen. Die Auswahl kann entweder durch das HTML Element {{ HTMLElement("input") }} oder durch Drag and Drop erfolgen.

- -

Es ist auch möglich die DOM File API von Erweiterungen oder anderem Chrome Code zu benutzen. In diesem Fall müssen einige zusätzliche Dinge beachtet werden, die im Abschnitt Using the DOM File API in chrome code näher erläutert werden.

- -

Auf ausgewählte Dateien zugreifen

- -

Gehen wir von folgendem HTML-Code aus:

- -
<input type="file" id="input">
- -

Die File API erlaubt den Zugriff auf eine {{ domxref("FileList") }} mit {{ domxref("File") }} Objekten, die die vom Benutzer ausgewählten Dateien repräsentieren.

- -

Wenn der Benutzer nur eine Datei auswählt, dann muss nur die erste Datei in der Liste betrachtet werden.

- -

Eine ausgewählte Datei erhält man über den üblichen DOM Selektor:

- -
var selectedFile = document.getElementById('input').files[0];
- -

Oder mit einem jQuery Selektor:

- -
var selectedFile = $('#input').get(0).files[0];
-
-var selectedFile = $('#input')[0].files[0];
- -
-

Tritt der Fehler "files is undefined" auf:
- Es wurde das falsche HTML element ausgewählt. Es ist zu beachten, dass ein jQuery Selektor eine Liste von zutreffenden DOM Elementen zurückliefert. Das richtige DOM Element muss ausgewählt werden, um "files" darauf anzuwenden.

-
- -

Zugriff auf ausgewählte Dateien über den Change Event

- -

Es ist auch möglich (aber nicht unbedingt erforderlich), auf die {{ domxref("FileList") }} über das change event zuzugreifen:

- -
<input type="file" id="input" onchange="handleFiles(this.files)">
- -

Wenn der Benutzer eine Datei auswählt, wird die Funktion handleFiles() aufgerufen. Als Parameter wird die {{ domxref("FileList") }} übergeben. Sie enthält die {{ domxref("File") }} Objekte, die die vom Benutzer ausgewählten Dateien repräsentieren.

- -

Soll der Benutzer mehrere Dateien auswählen können, dann kann einfach das Attribut multiple auf das input Element angewendet werden:

- -
<input type="file" id="input" multiple onchange="handleFiles(this.files)">
- -

In diesem Fall enthält die Dateiliste, die an die handleFiles() Funktion übergeben wird, ein {{ domxref("File") }} für jede Datei, die der Benutzer ausgewählt hat.

- -

Dynamisch einen Change Listener hinzufügen

- -

Wurde das input Feld mit einer JavaScript Bibliothek wie jQuery erzeugt, dann muss der event Listener mit {{ domxref("element.addEventListener()") }} hinzugefügt werden:

- -
var inputElement = document.getElementById("input");
-inputElement.addEventListener("change", handleFiles, false);
-function handleFiles() {
-  var fileList = this.files; /* Jetzt kann die Dateiliste verarbeitet werden */
-}
- -

In diesem Fall ist die handleFiles() Funktion selbst der Event Handler, anders als in den vorigen Beispielen, wo sie von einem Event Handler aufgerufen wurde.

- -

Informationen über die ausgewählte(n) Datei(en)

- -

Das {{ domxref("FileList") }} Objekt, das vom DOM geliefert wurde, enthält eine Liste aller Dateien, die vom Benutzer ausgewählt wurden. Jede der Dateien wird durch ein {{ domxref("File") }} Objekt repräsentiert. Die Anzahl der ausgewälten Dateien kann über das length Attribut der Liste ermittelt werden:

- -
var numFiles = files.length;
- -

Die einzelnen {{ domxref("File") }} Objekte erhält man, indem das {{ domxref("FileList") }} Objekt einfach wie ein Array behandelt wird:

- -
for (var i = 0, numFiles = files.length; i < numFiles; i++) {
-  var file = files[i];
-  ..
-}
-
- -

Diese Schleife iteriert über alle Dateien in der Liste.

- -

Im {{ domxref("File") }} Objekt liefern drei Attribute hilfreiche Informationen über die Datei:

- -
-
name
-
Der Dateiname als read-only String. Es ist nur der Dateiname enthalten, keine Pfadinformationen.
-
size
-
Die Dateigröße in Bytes als read-only 64-Bit Integer.
-
type
-
Der MIME type der Datei als read-only String oder "", wenn der Typ nicht ermittelt werden konnte.
-
- -

Beispiel: Dateigröße anzeigen

- -

Das folgende Beispiel zeigt eine mögliche Verwendung des size Attributs:

- -
<!DOCTYPE html>
-<html>
-<head>
-<meta charset="UTF-8">
-<title>File(s) size</title>
-<script>
-function updateSize() {
-  var nBytes = 0,
-      oFiles = document.getElementById("uploadInput").files,
-      nFiles = oFiles.length;
-  for (var nFileId = 0; nFileId < nFiles; nFileId++) {
-    nBytes += oFiles[nFileId].size;
-  }
-  var sOutput = nBytes + " bytes";
-  // optional code for multiples approximation
-  for (var aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
-    sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
-  }
-  // end of optional code
-  document.getElementById("fileNum").innerHTML = nFiles;
-  document.getElementById("fileSize").innerHTML = sOutput;
-}
-</script>
-</head>
-
-<body onload="updateSize();">
-<form name="uploadForm">
-<p><input id="uploadInput" type="file" name="myFiles" onchange="updateSize();" multiple> selected files: <span id="fileNum">0</span>; total size: <span id="fileSize">0</span></p>
-<p><input type="submit" value="Send file"></p>
-</form>
-</body>
-</html>
-
- -

Verstecktes input Element mit click() Methode

- -

Ab Gecko 2.0 {{ geckoRelease("2.0") }}, kann das zugegebenermaßen hässliche Datei-{{ HTMLElement("input") }} Element versteckt und durch eigene Oberflächenelemente zum Zugriff auf den Datei-Öffnen-Dialog ersetzt werden. Dazu wird das input Element mit dem CSS Stil display:none versehen und mit der Methode {{ domxref("element.click","click()") }} aufgerufen.

- -

Gehen wir von folgendem HTML-Code aus:

- -
<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
-<a href="#" id="fileSelect">Bitte Dateien auswählen</a>
-
-
- -

Der Code, der auf den click Event reagiert, kann wie folgt aussehen:

- -
var fileSelect = document.getElementById("fileSelect"),
-  fileElem = document.getElementById("fileElem");
-
-fileSelect.addEventListener("click", function (e) {
-  if (fileElem) {
-    fileElem.click();
-  }
-  e.preventDefault(); // prevent navigation to "#"
-}, false);
-
- -

Das neue Oberflächenelement zum Zugriff auf den Datei-Öffnen-Dialog kann nun beliebig gestaltet werden.

- -

label Element zum Auslösen eines versteckten input Elements

- -

Wird statt eines Links ({{ HTMLElement("a") }} Element) ein {{ HTMLElement("label") }} Element verwendet, dann kann das versteckte input Element auch ohne JavaScript ausgelöst werden. Dabei darf das input-Element aber weder mit display: none noch mit visibility: hidden versteckt werden, weil es in diesen Fällen nicht mit der Tastatur bedienbar wäre. Es darf nur visuell versteckt werden.

- -

Gehen wir von folgendem HTML-Code aus:

- -
<input type="file" id="fileElem" multiple accept="image/*" class="visually-hidden">
-<label for="fileElem">Bitte Dateien auswählen</label>
-
- -

und folgendem CSS:

- -
.visually-hidden {
-  position: absolute !important;
-  height: 1px;
-  width: 1px;
-  overflow: hidden;
-  clip: rect(1px, 1px, 1px, 1px);
-}
-
-input.visually-hidden:focus + label {
-  outline: thin dotted;
-}
-
- -

Bei einem Klick auf das label Element wird der Datei-Öffnen-Dialog angezeigt.

- -

Das label Element kann per CSS beliebig gestaltet werden. Man muss aber das Label des versteckten Eingabefelds hervorheben, wenn dieses den Fokus hat, d.h. per Tab-Taste angewählt wurde – sei es durch outline wie im obigen Beispiel oder background-color oder box-shadow. (Gegenwärtig stellt Firefox das Label von fokussierten <input type="file">-Elementen nicht hervorgehoben dar.)

- -

Dateien per Drag and Drop auswählen

- -

Es ist auch möglich, per Drag and Drop Dateien an die Webanwendung zu übergeben.

- -

Zuerst muss eine Drop Zone eingerichtet werden. Welcher Teil der Oberfläche Drops entgegen nimmt, ist vom Design der Anwendung abhängig. Generell ist das Empfangen von Drop Events aber einfach:

- -
var dropbox;
-
-dropbox = document.getElementById("dropbox");
-dropbox.addEventListener("dragenter", dragenter, false);
-dropbox.addEventListener("dragover", dragover, false);
-dropbox.addEventListener("drop", drop, false);
-
- -

In diesem Beispiel wird das Element mit der ID dropbox als Drop Zone verwendet. Das wird durch Registrieren der Listener für {{event('dragenter')}}, {{event('dragover')}} und {{event('drop')}} Events erreicht.

- -

dragenter und dragover benötigen wir in unserem Fall eigentlich nicht. Wir verhindern lediglich eine weitere Behandlung der Events durch Aufruf von e.stopPropagation() und e.preventDefault():

- -
function dragenter(e) {
-  e.stopPropagation();
-  e.preventDefault();
-}
-
-function dragover(e) {
-  e.stopPropagation();
-  e.preventDefault();
-}
-
- -

Das Wesentliche geschieht in der Behandlung des drop Events:

- -
function drop(e) {
-  e.stopPropagation();
-  e.preventDefault();
-
-  var dt = e.dataTransfer;
-  var files = dt.files;
-
-  handleFiles(files);
-}
-
- -

Hier ermitteln wir das dataTransfer Feld aus dem Event, entnehmen ihm die Dateiliste und übergeben diese an handleFiles(). Von da an ist die Weiterverarbeitung die gleiche wie bei Verwendung des input Elements.

- -

Beispiel: Thumbnails von ausgewählten Bildern anzeigen

- -

Gehen wir davon aus, dass auf einer Foto-Webseite mit HTML5 eine Thumbnail Vorschau von Bildern angezeigt werden soll, bevor sie hochgeladen werden. Das input Element oder die Drop Zone kann wie oben beschrieben eingerichtet werden. Diese rufen die folgende handleFiles() Funktion auf:

- -
function handleFiles(files) {
-  for (var i = 0; i < files.length; i++) {
-    var file = files[i];
-    var imageType = /^image\//;
-
-    if (!imageType.test(file.type)) {
-      continue;
-    }
-
-    var img = document.createElement("img");
-    img.classList.add("obj");
-    img.file = file;
-    preview.appendChild(img); // Gehen wird davon aus, dass "preview" das div-Element ist, in dem der Inhalt angezeigt wird.
-
-    var reader = new FileReader();
-    reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
-    reader.readAsDataURL(file);
-  }
-}
-
- -

Mit der Schleife wird über die ausgewählten Dateien iteriert. Bei jeder Datei wird mit Hilfe des type Attributs (indem der Reguläre Ausdruck "^image\/" darauf angewendet wird) geprüft, ob es sich um eine Bild Datei handelt. Liegt eine Bild Datei vor, dann wird ein neues img Element erzeugt. Mit CSS können z.B. hübsche Ränder oder Schatten erzeugt und die Größe des Bildes festgelegt werden, das muss also nicht hier im Code erfolgen.

- -

Jedem Bild wird die CSS Klasse obj zugewiesen, so dass es einfach ist, es im DOM Baum zu finden. Außerdem wird jedem Bild im file Attribut das {{ domxref("File") }} Objekt für das Bild zugewiesen; das benötigen wir später für den tatsächlichen Upload der Datei. Mit {{ domxref("Node.appendChild()") }} wird das neue Thumbnail dem preview Bereich unserer Anwendung hinzugefügt.

- -

Dann erstellen wir einen {{ domxref("FileReader") }}, um das Bild asynchron zu laden und es dem img Element hinzuzufügen. Nach dem Erstellen des FileReader Objektes definieren wir die onload Funktion und rufen readAsDataURL() auf, um die Leseoperation im Hintergrund zu starten. Ist der komplette Inhalt der Bilddatei geladen, dann wird er in eine data: URL umgewandelt, die an den onload Callback übergeben wird. Unsere Implementation dieser Routine setzt das src Attribut des img Elements auf das geladene Bild, was dazu führt, dass das Bild im Thumbnail auf dem Bildschirm des Benutzers erscheint.

- -

Verwendung von Objekt URLs

- -

Mit Gecko 2.0 {{ geckoRelease("2.0") }} wurde die Unterstützung für die DOM Methoden {{ domxref("window.URL.createObjectURL()") }} und {{ domxref("window.URL.revokeObjectURL()") }} eingeführt. Mit ihnen lassen sich einfache URL Strings erzeugen, die beliebige Daten referenzieren, auf die per DOM {{ domxref("File") }} Objekte zugegriffen werden kann; einschließlich lokale Dateien auf dem Computer des Anwenders.

- -

Wenn man aus HTML mit einer URL auf ein {{ domxref("File") }} Objekt verweisen möchte, dann kann man dafür eine Objekt URL erzeugen:

- -
var objectURL = window.URL.createObjectURL(fileObj);
- -

Die Objekt URL ist ein String, der das {{ domxref("File") }} Objekt identifiziert. Jedes Mal, wenn {{ domxref("window.URL.createObjectURL()") }} aufgerufen wird, wird eine eindeutige Objekt URL erzeugt, auch wenn vorher bereits eine Objekt URL für diese Datei erzeugt wurde. Jede von ihnen muss wieder freigegeben werden. Sie werden automatisch freigegeben, wenn das Dokument entladen wird. Wenn ihre Anwendung die Objekt URLs dynamisch verwendet, dann sollten Sie sie auch explizit durch Aufruf von {{ domxref("window.URL.revokeObjectURL()") }} freigeben:

- -
window.URL.revokeObjectURL(objectURL);
- -

Beispiel: Mit Objekt URLs Bilder anzeigen

- -

Dieses Beispiel verwendet Objekt URLs, um Thumbnails von Bildern anzuzeigen. Darüber hinaus werden weitere Dateiinformationen einschließlich Name und Größe angezeigt. Live Ansicht des Beispiels.

- -

Das HTML für die Oberfläche sieht folgendermaßen aus:

- -
<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
-<a href="#" id="fileSelect">Bitte Dateien auswählen</a>
-<div id="fileList">
-  <p>Keine Dateien ausgewählt!</p>
-</div>
-
- -

Das erstellt ein Datei {{ HTMLElement("input") }} Element, zusammen mit einem Link, der den Datei-Öffnen-Dialog anzeigt (so kann das Datei input Element versteckt werden, da es nicht so attraktiv aussieht). Das wird oben im Abschnitt {{ anch("Verstecktes input Element mit click() Methode") }} näher beschrieben.

- -

Die handleFiles() Methode sieht folgendermaßen aus:

- -
window.URL = window.URL || window.webkitURL;
-
-var fileSelect = document.getElementById("fileSelect"),
-    fileElem = document.getElementById("fileElem"),
-    fileList = document.getElementById("fileList");
-
-fileSelect.addEventListener("click", function (e) {
-  if (fileElem) {
-    fileElem.click();
-  }
-  e.preventDefault(); // prevent navigation to "#"
-}, false);
-
-function handleFiles(files) {
-  if (!files.length) {
-    fileList.innerHTML = "<p>No files selected!</p>";
-  } else {
-    var list = document.createElement("ul");
-    for (var i = 0; i < files.length; i++) {
-      var li = document.createElement("li");
-      list.appendChild(li);
-
-      var img = document.createElement("img");
-      img.src = window.URL.createObjectURL(files[i]);
-      img.height = 60;
-      img.onload = function() {
-        window.URL.revokeObjectURL(this.src);
-      }
-      li.appendChild(img);
-      var info = document.createElement("span");
-      info.innerHTML = files[i].name + ": " + files[i].size + " bytes";
-      li.appendChild(info);
-    }
-  }
-}
-
- -

Das {{ HTMLElement("div") }} Element mit der ID fileList wird ermittelt. Das ist der Block, in den wir später unsere Dateiliste einschließlich Thumbnails einfügen.

- -

Ist das {{ domxref("FileList") }} Objekt, das an handleFiles() übergeben wird, null, dann wird einfach "No files selected!" ausgegeben. Ansonsten bauen wir unsere Dateiliste wie folgt:

- -
    -
  1. Ein neues ({{ HTMLElement("ul") }}) Element wird erzeugt.
  2. -
  3. Das neue Listenelement wird dem {{ HTMLElement("div") }} Block hinzugefügt durch Aufruf der {{ domxref("element.appendChild()") }} Methode.
  4. -
  5. Für jedes {{ domxref("File") }} in der {{ domxref("FileList") }}, die durch files repräsentiert wird: -
      -
    1. Erzeuge ein neues Listenelement ({{ HTMLElement("li") }}) und füge es der Liste hinzu.
    2. -
    3. Erzeuge ein neues Bildelement ({{ HTMLElement("img") }}).
    4. -
    5. Setze das src Attribut des Bildes auf die neue Objekt URL, die die Datei repräsentiert, wobei die Objekt URL mit {{ domxref("window.URL.createObjectURL()") }} erzeugt wird.
    6. -
    7. Setze die Bildgröße auf 60 Pixel.
    8. -
    9. Richte den onload Event Handler ein, um die Objekt URL wieder freizugeben, da sie nach dem Laden des Bildes nicht mehr benötigt wird. Das wird durch Aufruf der Methode {{ domxref("window.URL.revokeObjectURL()") }} und Übergabe des Objekt URL Strings aus img.src gemacht.
    10. -
    11. Füge das neue Bildelement dem Listenelement hinzu.
    12. -
    -
  6. -
- -

Example: Uploading a user-selected file

- -

Another thing you might want to do is let the user upload the selected file or files (such as the images selected using the previous example) to a server. This can be done asynchronously very easily.

- -

Creating the upload tasks

- -

Continuing with the code that built the thumbnails in the previous example, recall that every thumbnail image is in the CSS class obj with the corresponding {{ domxref("File") }} attached in a file attribute. This allows us to select all of the images the user has chosen for uploading using {{ domxref("Document.querySelectorAll()") }}, like this:

- -
function sendFiles() {
-  var imgs = document.querySelectorAll(".obj");
-
-  for (var i = 0; i < imgs.length; i++) {
-    new FileUpload(imgs[i], imgs[i].file);
-  }
-}
-
- -

Line 2 fetches a {{ domxref("NodeList") }}, called imgs, of all the elements in the document with the CSS class obj. In our case, these will be all of the image thumbnails. Once we have that list, it's trivial to go through it and create a new FileUpload instance for each. Each of these handles uploading the corresponding file.

- -

Handling the upload process for a file

- -

The FileUpload function accepts two inputs: an image element and a file from which to read the image data.

- -
function FileUpload(img, file) {
-  var reader = new FileReader();
-  this.ctrl = createThrobber(img);
-  var xhr = new XMLHttpRequest();
-  this.xhr = xhr;
-
-  var self = this;
-  this.xhr.upload.addEventListener("progress", function(e) {
-        if (e.lengthComputable) {
-          var percentage = Math.round((e.loaded * 100) / e.total);
-          self.ctrl.update(percentage);
-        }
-      }, false);
-
-  xhr.upload.addEventListener("load", function(e){
-          self.ctrl.update(100);
-          var canvas = self.ctrl.ctx.canvas;
-          canvas.parentNode.removeChild(canvas);
-      }, false);
-  xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
-  xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
-  reader.onload = function(evt) {
-    xhr.sendAsBinary(evt.target.result);
-  };
-  reader.readAsBinaryString(file);
-}
-
- -

The FileUpload() function shown above creates a throbber, which is used to display progress information, and then creates an {{ domxref("XMLHttpRequest") }} to handle uploading the data.

- -

Before actually transferring the data, several preparatory steps are taken:

- -
    -
  1. The XMLHttpRequest's upload progress listener is set to update the throbber with new percentage information so that as the upload progresses the throbber will be updated based on the latest information.
  2. -
  3. The XMLHttpRequest's upload load event handler is set to update the throbber progress information to 100% to ensure the progress indicator actually reaches 100% (in case of granularity quirks during the process). It then removes the throbber since it's no longer needed. This causes the throbber to disappear once the upload is complete.
  4. -
  5. The request to upload the image file is opened by calling XMLHttpRequest's open() method to start generating a POST request.
  6. -
  7. The MIME type for the upload is set by calling the XMLHttpRequest function overrideMimeType(). In this case, we're using a generic MIME type; you may or may not need to set the MIME type at all depending on your use case.
  8. -
  9. The FileReader object is used to convert the file to a binary string.
  10. -
  11. Finally, when the content is loaded the XMLHttpRequest function sendAsBinary() is called to upload the file's content.
  12. -
- -
Note: The non-standard sendAsBinary method in the example above is considered deprecated as of Gecko 31 {{ geckoRelease(31) }} and will be removed soon. The standard send(Blob data) method can be used instead.
- -

Handling the upload process for a file, asynchronously

- -
<?php
-if (isset($_FILES['myFile'])) {
-    // Example:
-    move_uploaded_file($_FILES['myFile']['tmp_name'], "uploads/" . $_FILES['myFile']['name']);
-    exit;
-}
-?><!DOCTYPE html>
-<html>
-<head>
-    <title>dnd binary upload</title>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <script type="text/javascript">
-        function sendFile(file) {
-            var uri = "/index.php";
-            var xhr = new XMLHttpRequest();
-            var fd = new FormData();
-
-            xhr.open("POST", uri, true);
-            xhr.onreadystatechange = function() {
-                if (xhr.readyState == 4 && xhr.status == 200) {
-                    // Handle response.
-                    alert(xhr.responseText); // handle response.
-                }
-            };
-            fd.append('myFile', file);
-            // Initiate a multipart/form-data upload
-            xhr.send(fd);
-        }
-
-        window.onload = function() {
-            var dropzone = document.getElementById("dropzone");
-            dropzone.ondragover = dropzone.ondragenter = function(event) {
-                event.stopPropagation();
-                event.preventDefault();
-            }
-
-            dropzone.ondrop = function(event) {
-                event.stopPropagation();
-                event.preventDefault();
-
-                var filesArray = event.dataTransfer.files;
-                for (var i=0; i<filesArray.length; i++) {
-                    sendFile(filesArray[i]);
-                }
-            }
-        }
-    </script>
-</head>
-<body>
-    <div>
-        <div id="dropzone" style="margin:30px; width:500px; height:300px; border:1px dotted grey;">Drag & drop your file here...</div>
-    </div>
-</body>
-</html>
-
- -

Example: Using object URLs to display PDF

- -

Object URLs can be used for other things than just images! They can be used to display embedded PDF files or any other resources that can be displayed by the browser.

- -

In Firefox, to have the PDF appear embedded in the iframe (rather than proposed as a downloaded file), the preference pdfjs.disabled must be set to false {{non-standard_inline()}}.

- -
<iframe id="viewer">
-
- -

And here is the change of the src attribute:

- -
var obj_url = window.URL.createObjectURL(blob);
-var iframe = document.getElementById('viewer');
-iframe.setAttribute('src', obj_url);
-window.URL.revokeObjectURL(obj_url);
- -

Example: Using object URLs with other file types

- -

You can manipulate files of other formats the same way. Here is how to preview uploaded video:

- -
var video = document.getElementById('video');
-var obj_url = window.URL.createObjectURL(blob);
-video.src = obj_url;
-video.play()
-window.URL.revokeObjectURL(obj_url);
- -

Specifications

- - - -

See also

- - - -

{{ HTML5ArticleTOC() }}

- -

{{ languages( { "zh-cn": "zh-cn/Using_files_from_web_applications", "ja": "ja/Using_files_from_web_applications" } ) }}

diff --git a/files/de/web/api/fullscreen_api/index.html b/files/de/web/api/fullscreen_api/index.html new file mode 100644 index 0000000000..1ddce4c572 --- /dev/null +++ b/files/de/web/api/fullscreen_api/index.html @@ -0,0 +1,305 @@ +--- +title: Vollbild API +slug: Web/API/Vollbild_API +tags: + - API + - DOM + - JS + - Tutorial + - Vollbild API +translation_of: Web/API/Fullscreen_API +--- +

{{DefaultAPISidebar("Fullscreen API")}}

+ +

Die Vollbild-API bietet einen einfachen Weg, um Webinhalte auf dem gesamten Bildschirm des Nutzers anzuzeigen. Der Browser kann einfach angewiesen werden, Elemente und, falls vorhanden, deren Kinder den gesamten Bildschirm einnehmen zu lassen, wodurch jegliche andere Inhalte vorübergehend ausgeblendet werden.

+ +
+

Momentan benutzen nicht alle Browser die Version der API ohne Präfixe. Siehe Vendor Präfixe für Unterschiede zwischen Präfixen und Namen. 

+ +

Für eine universelle Lösung des Problems siehe Fscreen.

+
+ +

Aktivieren des Vollbildmodus

+ +

Wenn sie ein Element gefunden haben, welches Sie im Vollbildmodus anzeigen möchten (etwa ein {{ HTMLElement("video") }}), wir der Vollbildmodus einfach durch den Aufruf der {{ domxref("Element.requestFullscreen()") }}-Methode gestartet.

+ +

Nehmen wir dieses {{ HTMLElement("video") }}-Element als Beispiel:

+ +
<video controls id="myvideo">
+  <source src="somevideo.webm"></source>
+  <source src="somevideo.mp4"></source>
+</video>
+
+ +

Wir können dieses Video mit folgendem Skript in den Vollbildmodus versetzen:

+ +
var elem = document.getElementById("myvideo");
+if (elem.requestFullscreen) {
+  elem.requestFullscreen();
+}
+
+ +

Anzeige-Schwierigkeiten

+ +

An dieser Stelle lohnt es sich, einen wichtigen Unterschied zwischen Gecko und WebKit aufzuzeigen: Gecko fügt automatisch CSS-Regeln zum betroffenen Element hinzu, damit es den ganzen Bildschirm einnimmt: "width: 100%; height: 100%". WebKit tut dies allerdings nicht; stattdessen wird das Vollbild-Element in der selben Größe zentriert vor einem schwarzen Hintergrund dargestellt. Um das gleich Vollbild-Verhalten in WebKit zu erhalten, müssen Sie selbst eine "width: 100%; height: 100%;"-CSS-Regel zum entsprechenden Element hinzufügen:

+ +
#myvideo:-webkit-full-screen {
+  width: 100%;
+  height: 100%;
+}
+
+ +

Andererseits, wenn Sie stattdessen versuchen, das WebKit-Verhalten auf Gecko zu erreichen, müssen sie das Element, das präsentiert werden soll, in einem anderen Element platzieren, welches sie stattdessen in den Vollbild-Modus versetzen. Dann können sie CSS-Regeln verwenden, um das innere Element so zu layouten, wie Sie wünschen.

+ +

Benachrichtigung

+ +

Wenn der Vollbild-Modus erfolgreich gestartet wird, erhält das document, welches das entsprechende Element enthält ein {{ event("fullscreenchange") }}-Event. Wenn der Vollbild-Modus wieder verlassen wird, erhält das document wiederum ein {{ event("fullscreenchange") }}-Event. Beachten Sie jedoch, dass das {{ event("fullscreenchange") }}-Event selbst keine Informationen darüber bereitstellt, ob das document in oder aus den Vollbild-Modus wechselt. Stattdessen muss überprüft werden, ob das Attribut {{ domxref("document.fullscreenElement", "fullscreenElement") }}, des document nicht null ist. In diesem Fall befindet sich der Browser im Vollbild-Modus.

+ +

Wenn die Vollbild-Anfrage scheitert

+ +

Es ist nicht garantiert, dass der Wechsel in den Vollbild-Modus möglich ist. {{ HTMLElement("iframe") }}-Elements etwa haben das {{ HTMLAttrXRef("allowfullscreen", "iframe") }}-Attribut, um ihrem Inhalt zu erlauben, in den Vollbild-Modus zu wechseln. Zusätzlich gibt es bestimmte Arten von Inhalten, wie etwa windowed plug-ins, welche nicht im Vollbild-Modus angezeigt werden können. Wenn versucht wird, ein Element, welches nicht im Vollbild-Modus dargestellt werden kann (oder ein Eltern- bzw. Kind-Knoten eines solchen Elements), wird dies nicht funktionieren. Stattdessen wird das Element, welches den Vollbild-Modus angefragt hat, ein mozfullscreenerror-Event erhalten. Wenn eine Vollbild-Anfrage scheitert, wird Firefox eine Fehlermeldung auf der Web-Konsole ausgeben, welche erklärt, warum die Anfrage fehlgeschlagen ist. In Chrome und neueren Versionen von Opera werden allerdings keine solchen Warnungen produziert.

+ +
+

Hinweis: Vollbild-Anfragen müssen aus einem Event-Handler heraus gestellt werden oder die Anfrage wird abgelehnt. 

+
+ +

Verlassen des Vollbildmodus

+ +

Der Nutzer hat immer die Möglichkeit, selbst den Vollbildmodus zu verlassen; siehe {{ anch("Things your users want to know") }}. Der Vollbildmodus kann aber auch programmatisch mithilfe der  {{domxref("Document.exitFullscreen()")}}-Methode beendet werden.

+ +

Weitere Informationen

+ +

Das {{ domxref("document") }} hält weitere Informationen bereit, welche bei der Entwicklung von Vollbild-Web-Apps hilfreich sein können:

+ +
+
{{ domxref("document.fullscreenElement", "fullscreenElement") }}
+
Das fullscreenElement-Attribut gibt das {{ domxref("element") }} an, welches aktuell im Vollbild-Modus angezeigt wird. Wenn dies nicht null ist, befindet sich das document im Vollbildmodus. Sonst, wenn dieses Attribut null ist, befindet sich das Dokument nicht im Vollbildmodus.
+
{{ domxref("document.fullscreenEnabled", "fullscreenEnabled") }}
+
Das fullscreenEnabled-Attribut gibt an, ob das document aktuell in einem Zustand ist, in welchem der Vollbild-Modus erlaubt ist.
+
+ +

Was die Nutzer wissen wollen

+ +

Sie sollten die Nutzer wissen lassen, dass sie den Vollbildmodus jederzeit mit ESC (oder F11) verlassen können.

+ +

Zusätzlich beendet das Navigieren zu einer anderen Seite oder das Wechseln von Tabs oder Programmen (etwa mit  Alt+Tab) auch den Vollbildmodus.

+ +

Beispiel

+ +

In diesem Beispiel wird ein Video auf einer Webseite dargestellt. Durch drücken von Return oder Enter kann der Nutzer den Vollbildmodus des Videos umschalten.

+ +

Live-Beispiel ansehen

+ +

Watching for the Enter key

+ +

When the page is loaded, this code is run to set up an event listener to watch for the Enter key.

+ +
document.addEventListener("keydown", function(e) {
+  if (e.keyCode == 13) {
+    toggleFullScreen();
+  }
+}, false);
+
+ +

Toggling fullscreen mode

+ +

This code is called when the user hits the Enter key, as seen above.

+ +
function toggleFullScreen() {
+  if (!document.fullscreenElement) {
+      document.documentElement.requestFullscreen();
+  } else {
+    if (document.exitFullscreen) {
+      document.exitFullscreen();
+    }
+  }
+}
+
+ +

This starts by looking at the value of the fullscreenElement attribute on the {{ domxref("document") }} (checking it prefixed with both moz, ms, or webkit). If it's null, the document is currently in windowed mode, so we need to switch to fullscreen mode. Switching to fullscreen mode is done by calling {{ domxref("element.requestFullscreen()") }}.

+ +

If fullscreen mode is already active (fullscreenElement is non-null), we call {{ domxref("document.exitFullscreen()") }}.

+ +

Vendor Präfixe

+ +

For the moment not all browsers are implementing the unprefixed version of the API (for vendor agnostic access to the Fullscreen API you can use Fscreen). Here is the table summarizing the prefixes and name differences between them:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StandardBlink (Chrome & Opera)Gecko (Firefox)Internet Explorer 11EdgeSafari (WebKit)
{{domxref("Document.fullscreen")}}webkitIsFullScreenmozFullScreen-webkitIsFullScreenwebkitIsFullScreen
{{domxref("Document.fullscreenEnabled")}}webkitFullscreenEnabledmozFullScreenEnabledmsFullscreenEnabledwebkitFullscreenEnabledwebkitFullscreenEnabled
{{domxref("Document.fullscreenElement")}}webkitFullscreenElementmozFullScreenElementmsFullscreenElementwebkitFullscreenElementwebkitFullscreenElement
{{domxref("Document.onfullscreenchange")}}onwebkitfullscreenchangeonmozfullscreenchangeMSFullscreenChangeonwebkitfullscreenchangeonwebkitfullscreenchange
{{domxref("Document.onfullscreenerror")}}onwebkitfullscreenerroronmozfullscreenerrorMSFullscreenErroronwebkitfullscreenerroronwebkitfullscreenerror
{{domxref("Document.exitFullscreen()")}}webkitExitFullscreen()mozCancelFullScreen()msExitFullscreen()webkitExitFullscreen()webkitExitFullscreen()
{{domxref("Element.requestFullscreen()")}}webkitRequestFullscreen()mozRequestFullScreen()msRequestFullscreen()webkitRequestFullscreen()webkitRequestFullscreen()
+ +

Spezifikationen

+ + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName("Fullscreen")}}{{Spec2("Fullscreen")}}Initial version.
+ +

Browser Kompatibilität

+ +

All browsers implement this APIs. Nevertheless some implement it with prefixed names with slightly different spelling; e.g., instead of requestFullscreen(), there is MozRequestFullScreen().

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support15 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoDesktop("9.0") }} {{ property_prefix("-moz") }}
+ {{CompatGeckoDesktop("47")}} (behind full-screen-api.unprefix.enabled)
11 {{ property_prefix("-ms") }}12.105.0 {{ property_prefix("-webkit") }}
fullscreenEnabled20 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoDesktop("10.0") }} {{ property_prefix("-moz") }}
+ {{CompatGeckoDesktop("47")}} (behind full-screen-api.unprefix.enabled)
11 {{ property_prefix("-ms") }}12.105.1 {{ property_prefix("-webkit") }}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidChromeEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{ CompatUnknown() }}28 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoMobile("9.0") }}{{ property_prefix("-moz") }}
+ {{CompatGeckoMobile("47")}} (behind full-screen-api.unprefix.enabled)
{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
fullscreenEnabled{{ CompatUnknown() }}28 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoMobile("10.0") }} {{ property_prefix("moz") }}
+ {{CompatGeckoMobile("47")}} (behind full-screen-api.unprefix.enabled)
{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+ +

Siehe auch

+ + diff --git a/files/de/web/api/geolocation_api/index.html b/files/de/web/api/geolocation_api/index.html new file mode 100644 index 0000000000..65be452a5a --- /dev/null +++ b/files/de/web/api/geolocation_api/index.html @@ -0,0 +1,291 @@ +--- +title: Verwenden von geolocation +slug: Web/WebAPI/verwenden_von_geolocation +translation_of: Web/API/Geolocation_API +--- +

Die "geolocation"-API ermöglicht es Nutzern, einer Web-Applikation die eigene Position mitzuteilen. Um die Privatsphäre des Nutzers zu schützen, wird dieser vorher um Erlaubnis gebeten und muss der Übermittlung zustimmen.

+ +

Das geolocation-Objekt

+ +

Die geolocation-API wird durch das {{domxref("window.navigator.geolocation","navigator.geolocation")}}-Objekt offengelegt.

+ +

Wenn das Objekt existiert, sind die geolocation-Services vorhanden und nutzbar. Sie können die Funktionstüchtigkeit daher wie folgt testen:

+ +
if ("geolocation" in navigator) {
+  /* geolocation funktioniert */
+} else {
+  /* geolocation funktioniert NICHT */
+}
+
+ +
+

Notiz: In Firefox 24 und älteren Versionen hat "geolocation" in navigator immer true zurückgegeben, auch wenn die API ausgeschaltet war. Dies wurde mit Firefox 25 gelöst um mit dem Standard kompatibel zu sein. ({{ bug(884921) }})

+
+ +

Die derzeitige Position abfragen

+ +

Um die derzeitige Position des Nutzers zu erhalten, können Sie die Methode {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}} aufrufen. Dies startet eine asynchrone Anfrage für den Abruf der Position zu initiieren. Wenn die Position erhalten wurde, wird die übergebene Callback-Funktion ausgeführt. Sie können optional auch eine zweite Callback-Funktion übergeben, welche im Falle eines Fehlers ausgeführt wird. Ein dritter, optionaler Parameter ist ein Konfigurationsobjekt, in dem Sie das maximale Alter der zurückgegeben Position, die maximale Wartezeit für die Anfrage und den Wunsch für eine hohe Genauigkeit festlegegen können.

+ +
+

Notiz: Standardmäßig versucht {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}} so schnell wie möglich mit einer geringen Genauigkeit zu antworten. Es ist nützlich wenn Sie eine schnelle Antwort benötigen, unabhängig von der Genauigkeit. Beispielsweise Geräte mit einem GPS können eine Minute oder länger benötigen um eine neue Position zu erhalten, also kann es möglich sein, dass weniger genaue Daten (Ort der IP oder von WLANs) zurückgegeben werden.

+
+ +
navigator.geolocation.getCurrentPosition(function(position) {
+  do_something(position.coords.latitude, position.coords.longitude);
+});
+ +

Das obige Beispiel wird die Funktion do_something() ausführen, wenn eine Position erhalten wurde.

+ +

Die derzeitige Position überwachen

+ +

Wenn sich die Positionsdaten ändern (entweder über die Bewegung des Gerätes oder den Erhalt genauere Geolokationsdaten), können Sie eine Callback-Funktion erstellen, die mit der erneuerten Positionsinformation arbeitet. Dies ist über die Funktion {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}} möglich, welche die gleichen Eingabeparameter wie {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}}. besitzt. Die Callback-Funktion wird mehrere Male ausgeführt, dies erlaubt dem Browser die Position aufgrund von Bewegungen oder genaueren Positionsdaten, durch das Verwenden von anderen (langsameren) Methoden, zu erneuern. Die Fehler-Callback-Funktion, welche wie auch in {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}},  hier optional ist, kann auch mehrfach aufgerufen werden.

+ +
+

Notiz: Sie können  {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}} auch ohne ein vorangestellten Aufruf von {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}} nutzen.

+
+ +
var watchID = navigator.geolocation.watchPosition(function(position) {
+  do_something(position.coords.latitude, position.coords.longitude);
+});
+ +

Die Methode {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}} gibt eine numerische ID zurück, die für die eindeutige Identifikation des Positionsüberwachers verwendet werden kann; diese können Sie in der Methode {{domxref("window.navigator.geolocation.clearWatch()","clearWatch()")}} nutzen, um die Positionsüberwachung zu beenden.

+ +
navigator.geolocation.clearWatch(watchID);
+
+ +

Anpassen der Antwort

+ +

Sowohl {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}} als auch {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}} nehmen einen Erfolgs-Callback an, einen optionalen Fehler-Callback und ein optionales PositionOptions-Objekt.

+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","PositionOptions")}}

+ +

Ein Aufruf von {{domxref("window.navigator.geolocation.watchPosition()","watchPosition")}} könnte wie folgt aussehen:

+ +
function geo_success(position) {
+  do_something(position.coords.latitude, position.coords.longitude);
+}
+
+function geo_error() {
+  alert("Entschuldigung, keine Positionsinformationen sind verfügbar.");
+}
+
+var geo_options = {
+  enableHighAccuracy: true,
+  maximumAge        : 30000,
+  timeout           : 27000
+};
+
+var wpid = navigator.geolocation.watchPosition(geo_success, geo_error, geo_options);
+ +

Ein (englisches) Beispiel von watchPosition in Aktion: http://www.thedotproduct.org/experiments/geo/
+ 

+ +

Darstellung einer Position

+ +

Die Position des Nutzers wird durch ein Position-Objekt dargestellt, welches ein Coordinates-Objekt referenziert.

+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","Position")}}

+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","Coordinates")}}

+ +

Fehlerbehandlung

+ +

Die Fehler-Callback-Funktion, wenn im Aufruf von getCurrentPosition() oder watchPosition() vorhanden, nimmt ein PositionError-Objekt als ersten Parameter an.

+ +
function errorCallback(error) {
+  alert('ERROR(' + error.code + '): ' + error.message);
+};
+
+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","PositionError")}}

+ +

Geolokations-Live-Beispiel

+ + + +

HTML Content

+ +
<p><button onclick="geoFindMe()">Zeige meine Position an</button></p>
+<div id="out"></div>
+
+ +

JavaScript Content

+ +
function geoFindMe() {
+  var output = document.getElementById("out");
+
+  if (!navigator.geolocation){
+    output.innerHTML = "<p>Geolokation wird von ihrem Browser nicht unterstützt</p>";
+    return;
+  }
+
+  function success(position) {
+    var latitude  = position.coords.latitude;
+    var longitude = position.coords.longitude;
+
+    output.innerHTML = '<p>Die Latitude ist ' + latitude + '° <br>Die Longitude ist ' + longitude + '°</p>';
+
+    var img = new Image();
+    img.src = "http://maps.googleapis.com/maps/api/staticmap?center=" + latitude + "," + longitude + "&zoom=13&size=300x300&sensor=false";
+
+    output.appendChild(img);
+  };
+
+  function error() {
+    output.innerHTML = "Es war nicht möglich Sie zu lokalisieren";
+  };
+
+  output.innerHTML = "<p>Lokalisieren…</p>";
+
+  navigator.geolocation.getCurrentPosition(success, error);
+}
+
+ +

Demo:

+ +

{{ EmbedLiveSample('Geolokations-Live-Beispiel',350,410) }}

+ +

Für die Erlaubnis fragen

+ +

Jedes Add-On von addons.mozilla.org, welches dieses Feature nutzt, muss explizit, ähnlich der automatischen Frage von Websites, nach Erlaubnis fragen. Die Antwort des Nutzers wird in einer Konfiguration gespeichert, welche von pref-Parameter bestimmt wird, wenn möglich. Die Funktion, die als callback-Parameter verwendet wird mit einem boolischen Wert aufgerufen, welcher die Antwort des Nutzers anzeigt. Wenn dieser true ist, kann das Add-On Geolokationsdaten nutzen.

+ +
function prompt(window, pref, message, callback) {
+    let branch = Components.classes["@mozilla.org/preferences-service;1"]
+                           .getService(Components.interfaces.nsIPrefBranch);
+
+    if (branch.getPrefType(pref) === branch.PREF_STRING) {
+        switch (branch.getCharPref(pref)) {
+        case "always":
+            return callback(true);
+        case "never":
+            return callback(false);
+        }
+    }
+
+    let done = false;
+
+    function remember(value, result) {
+        return function() {
+            done = true;
+            branch.setCharPref(pref, value);
+            callback(result);
+        }
+    }
+
+    let self = window.PopupNotifications.show(
+        window.gBrowser.selectedBrowser,
+        "geolocation",
+        message,
+        "geo-notification-icon",
+        {
+            label: "Ort teilen",
+            accessKey: "S",
+            callback: function(notification) {
+                done = true;
+                callback(true);
+            }
+        }, [
+            {
+                label: "Immer teilen",
+                accessKey: "A",
+                callback: remember("always", true)
+            },
+            {
+                label: "Niemals teilen",
+                accessKey: "N",
+                callback: remember("never", false)
+            }
+        ], {
+            eventCallback: function(event) {
+                if (event === "dismissed") {
+                    if (!done) callback(false);
+                    done = true;
+                    window.PopupNotifications.remove(self);
+                }
+            },
+            persistWhileVisible: true
+        });
+}
+
+prompt(window,
+       "extensions.foo-addon.allowGeolocation",
+       "Foo Add-on möchte deinen Ort abrufen.",
+       function callback(allowed) { alert(allowed); });
+
+ +

Browser compatibility

+ +
{{ CompatibilityTable() }}
+ +
 
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Grundsätzlicher Support5{{CompatGeckoDesktop("1.9.1")}}910.60
+ Removed in 15.0
+ Reintroduced in 16.0
5
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Grundsätzlicher Support2.111{{CompatGeckoMobile("4")}}1010.603.2
+
+ +

Gecko-Notizen

+ +

Firefox besitzt Support für das Lokalisieren über WLAN-Informationen durch die "Google Location"-Services. In einer Transaktion zwischen Firefox und Google werden die Daten übetragen, inklusive Daten des WLAN-Zugriffspunktes, einem Zugriffstoken (ähnlich wie ein zweiwöchiger Cookie), und der IP-Adresse des Nutzers. Für mehr Informationen sollten Sie Mozillas und Googles Datenschutzbestimmungen lesen, welche den Rahmen der Datennutzung angeben.

+ +

Siehe auch

+ + diff --git a/files/de/web/api/history_api/index.html b/files/de/web/api/history_api/index.html new file mode 100644 index 0000000000..6f59cbbdc3 --- /dev/null +++ b/files/de/web/api/history_api/index.html @@ -0,0 +1,227 @@ +--- +title: Manipulieren des Browser-Verlaufes +slug: Web/Guide/DOM/Manipulating_the_browser_history +tags: + - Verlauf +translation_of: Web/API/History_API +--- +

Das DOM {{ domxref("window") }} Objekt stellt Zugriffsmöglichkeiten auf den Browser-Verlauf über das {{ domxref("window.history", "history") }} Objekt bereit. Es bietet nützliche Methoden und Einstellungen, die es ermöglichen, den Zurück- und Vorwärts-Button und den Browser-Verlauf zu steuern und – seit HTML5 – diesen auch zu manipulieren

+ +

Durch den Verlauf gehen

+ +

Um durch den Verlauf des Benutzers zu gehen gibt es die Methoden back(), forward() und go().

+ +

Vor und zurück bewegen

+ +

Um im Verlauf zurück zu navigieren, nutze:

+ +
window.history.back();
+
+ +

Dies hat denselben Effekt, wie wenn der Benutzer den Zurück-Knopf drückt.

+ +

Auf ähnliche Weise kann so vorwärts navigiert werden: 

+ +
window.history.forward();
+
+ +

An einen bestimmten Punkt im Verlauf springen

+ +

Mit der Methode go() ist es möglich, eine spezifische Seite aus dem Verlauf zu laden, welche relativ zur momentanen Seite im Verlauf steht. Die aktuelle Seite hat hierbei natürlich Index 0.

+ +

Um eine Seite rückwärts zu gehen (äquivalent zu back()):

+ +
window.history.go(-1);
+
+ +

Um eine Seite vorwärts zu gehen (äquivalent zu forward()):

+ +
window.history.go(1);
+
+ +

Gleichermassen ist es möglich, 2 Seiten vorwärts mit go(2) zu gehen, 3 rückwärts mit go(-3) und so weiter.

+ +

Die Anzahl der Seiten im history-Stapel kann mithilfe der Eigenschaft length bestimmt werden:

+ +
var numberOfEntries = window.history.length;
+
+ +
Anmerkung: Der Internet Explorer unterstützt das Übergeben von String URLs als Parameter für go(); dies ist nicht standardisiertes Verhalten und wird von Gecko nicht unterstützt.
+ +

Hinzufügen und Ändern von Verlaufseinträgen

+ +

{{ gecko_minversion_header("2") }}

+ +

Mit HTML5 wurden die Methoden history.pushState() und history.replaceState() eingeführt, welche jeweils das Hinzufügen und die Manipulation von Verlaufseinträgen ermöglichen. Diese stehen in Verbindung mit dem {{ domxref("window.onpopstate") }}-Event.

+ +

Wenn history.pushState() benutzt wird, ändert das den Referrer der als HTTP-Header in danach erstellten XMLHttpRequest-Objekten genutzt wird. Der Referrer wird die URL des Dokumentes sein, dessen Fenster this zur Zeit der Erstellung des XMLHttpRequest-Objektes ist.

+ +

Beispiel für die pushState()-Methode

+ +

Angenommen, http://mozilla.org/foo.html führt folgendes JavaScript aus:

+ +
var stateObj = { foo: "bar" };
+history.pushState(stateObj, "seite 2", "bar.html");
+
+ +

Das ändert die angezeigte URL zu http://mozilla.org/bar.html, bringt den Browser aber nicht dazu, bar.html zu laden oder überhaupt dessen Existenz zu überprüfen.

+ +

Man nehme weiter an, der Benutzer navigiert zu http://google.com und klickt dann Zurück. Zu diesem Zeitpunkt wird die URL-Bar http://mozilla.org/bar.html anzeigen, und die Seite erhält ein popstate-Event mit einem Status-Objekt, das eine Kopie von stateObj darstellt. Die Seite selbst wird aussehen wie foo.html, aber während dem popstate-Event kann der Inhalt bearbeitet worden sein.

+ +

Wenn der Benutzer noch einmal Zurück drückt, ändert sich die URL zu http://mozilla.org/foo.html, und die Seite erhält ein weiteres popstate-Event, dieses Mal mit einem null Status-Objekt. Auch hier wird der Inhalt der Seite nicht geändert, wenn das nicht manuell im popstate-Event vollzogen wird.

+ +

Die pushState()-Methode

+ +

pushState() übernimmt drei Parameter: ein state Objekt, einen title (welcher aktuell noch ignoriert wird), und (optional) eine URL. Betrachten wir die einzelnen Parameter genauer:

+ + + +
Anmerkung: In Gecko 2.0 {{ geckoRelease("2.0") }} bis Gecko 5.0 {{ geckoRelease("5.0") }} wird das übergebene Objekt mittels JSON serialisiert. Ab Gecko 6.0 {{ geckoRelease("6.0") }}, übernimmt der structured clone algorithm die Serialisierung des Objekts. Dies erlaubt eine größere Vielfalt an Objekten, die sich sicher serialisieren lassen.
+ +

In gewissem Sinne ist der Aufruf von pushState() ähnlich dem Setzen von window.location = "#foo", insofern beide einen weiteren history-Eintrag sowohl erzeugen als auch aktivieren, der mit dem aktuellen Document verbunden ist. Aber pushState() hat ein paar Vorteile:

+ + + +

Man beachte, dass pushState() niemals das Auslösen eines hashchange-Events verursacht, selbst wenn sich die neue URL von der alten nur durch ihren hash unterscheidet.

+ +

Die replaceState()-Methode

+ +

history.replaceState() arbeitet genauso wie history.pushState(), abgesehen davon, dass replaceState() den aktuellen history-Eintrag verändert, statt einen neuen zu erzeugen.

+ +

replaceState() ist insbesondere nützlich, um das Status-Objekt oder die URL des aktuellen history-Eintrages als Reaktion auf eine Benutzer-Aktion zu aktualisieren.

+ +
Anmerkung: In Gecko 2.0 {{ geckoRelease("2.0") }} bis Gecko 5.0 {{ geckoRelease("5.0") }} wird das übergebene Objekt mittels JSON serialisiert. Ab Gecko 6.0 {{ geckoRelease("6.0") }}, übernimmt der structured clone algorithm die Serialisierung des Objekts. Dies erlaubt eine größere Vielfalt an Objekten, die sich sicher serialisieren lassen.
+ +

Beispiel für die replaceState()-Methode

+ +

Angenommen, http://mozilla.org/foo.html führt folgendes JavaScript aus:

+ +

 

+ +
var stateObj = { foo: "bar" };
+history.pushState(stateObj, "page 2", "bar.html");
+ +

Die Erläuterung dieser beiden Zeilen findet sich in unter "Beispiel für die pushState()-Metode". Weiterhin nehme man an, http://mozilla.org/bar.html würde dieses JavaScript ausführen:

+ +
history.replaceState(stateObj, "page 3", "bar2.html");
+ +

Dies lässt den Browser http://mozilla.org/bar2.html in der Adresszeile anzeigen, aber nicht bar2.html laden oder auch nur prüfen, ob bar2.html existiert.

+ +

Nehmen wir nun an, dass der Nutzer zu http://www.microsoft.com navigiert und dann auf den Zurück-Button des Browsers drückt. Dann wird die Adresszeile http://mozilla.org/bar2.html anzeigen. Drückt der Benutzer den Zurück-Button nun nochmals, zeigt die Adresszeile http://mozilla.org/foo.html und umgeht bar.html vollständig.

+ +

 

+ +

The popstate event

+ +

popstate event is dispatched to the window every time the active history entry changes. If the history entry being activated was created by a call to pushState or affected by a call to replaceState, the popstate event's state property contains a copy of the history entry's state object.

+ +

See {{ domxref("window.onpopstate") }} for sample usage.

+ +

Reading the current state

+ +

When your page loads, it might have a non-null state object.  This can happen, for example, if the page sets a state object (using pushState() or replaceState()) and then the user restarts her browser.  When your page reloads, the page will receive an onload event, but no popstate event.  However, if you read the history.state property, you'll get back the state object you would have gotten if a popstate had fired.

+ +

You can read the state of the current history entry without waiting for a popstate event using the history.state property like this:

+ +
var currentState = history.state;
+
+ +

Examples

+ +

For a complete example of AJAX web site, please see: Ajax navigation example.

+ +

Browser compatibility

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
replaceState, pushState5{{ CompatGeckoDesktop("2.0") }}1011.505.0
history.state18{{ CompatGeckoDesktop("2.0") }}1011.506.0
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
replaceState, pushState{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
history.state{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+ +

See also

+ + + +

{{ languages( { "ja": "ja/DOM/Manipulating_the_browser_history"} ) }}

diff --git a/files/de/web/api/html_drag_and_drop_api/index.html b/files/de/web/api/html_drag_and_drop_api/index.html new file mode 100644 index 0000000000..292b860888 --- /dev/null +++ b/files/de/web/api/html_drag_and_drop_api/index.html @@ -0,0 +1,11 @@ +--- +title: DragDrop +slug: DragDrop +tags: + - NeedsTranslation + - TopicStub +translation_of: Web/API/HTML_Drag_and_Drop_API +translation_of_original: DragDrop +--- +

 

+

See https://developer.mozilla.org/en-US/docs/DragDrop/Drag_and_Drop

diff --git a/files/de/web/api/htmlelement/change_event/index.html b/files/de/web/api/htmlelement/change_event/index.html new file mode 100644 index 0000000000..8836b6bc67 --- /dev/null +++ b/files/de/web/api/htmlelement/change_event/index.html @@ -0,0 +1,109 @@ +--- +title: change +slug: Web/Events/change +translation_of: Web/API/HTMLElement/change_event +--- +

Das change Event wird von {{HTMLElement("input")}}, {{HTMLElement("select")}}, und {{HTMLElement("textarea")}} Elementen ausgelöst, wenn der Benutzer den Wert des Elements verändert. Im Gegensatz zum {{event("input")}} Event wird das change Event nicht bei jeder Änderung der value Eigenschaft ausgelöst.

+ +

General info

+ +
+
Specification
+
HTML5
+
Interface
+
{{domxref("Event")}}
+
Bubbles
+
Yes
+
Cancelable
+
No
+
Target
+
Element
+
Default Action
+
undefined
+
+ +

Eigenschaften

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}{{domxref("EventTarget")}}The event target (the topmost target in the DOM tree).
type {{readonlyInline}}{{domxref("DOMString")}}The type of event.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Whether the event normally bubbles or not.
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Whether the event is cancellable or not.
+ +

Beschreibung

+ +

Das change Event wird abhängig vom Form-Element, welches verändert wird, und der Art der Benutzerinteraktion mit dem Element in verschiedenen Situationen ausgelöst:

+ + + +

Verschiedene Webbbrowser unterscheiden sich darin, ob das change Event bei bestimmten Interaktionen ausgelöst werden soll oder nicht. Tastaturnavigation in {{HTMLElement("select")}} Elementen zum Beispiel lösen das change event in Gecko nie aus bis der Benutzer die Enter-Taste drückt oder den Fokus vom <select> (see {{bug("126379")}}) Element nimmt.

+ +

Die HTML Spezifikation listet die <input> Typen, welche das change Event auslösen können.

+ +

Beispiele

+ +

Ein unvollständiges Beispiel auf jsfiddle: http://jsfiddle.net/nfakc/5/, welches möglicherweise nich in allen Webbrowsern funktioniert.

+ +

Beispiel: Change Event auf einem select

+ +

Der folgende Code behandelt das change Event eines select durch den Aufruf der changeEventHandler Funktion im onchange Attribut. Die Funktion liest den Wert des Elements, das das Event auslöste, und gibt ihn in einem Alert aus.

+ +
<html>
+  <head>
+    <title>Example: Change event on a select</title>
+    <script type="text/javascript">
+      function changeEventHandler(event) {
+        alert('You like ' + event.target.value + ' ice cream.');
+      }
+    </script>
+    </head>
+    <body>
+        <label>Choose an ice cream flavor: </label>
+        <select size="1" onchange="changeEventHandler(event);">
+            <option>chocolate</option>
+            <option>strawberry</option>
+            <option>vanilla</option>
+        </select>
+    </body>
+</html>
+
+ +

Siehe auch

+ +

{{domxref("NetworkInformation.connection")}} löst das change Event  aus, wenn sich die Informationen zur Verbindung verändern.

+ +

Webbrowserkompatibilität

+ +

{{ CompatibilityTable() }}

+ +

Laut QuirksMode sind Chrome und Firefox manchmal kompatibel. Aber IE9 und frühere Versionen von IE10 haben nur eine unvollständige Unterstützung.

diff --git a/files/de/web/api/htmlelement/innertext/index.html b/files/de/web/api/htmlelement/innertext/index.html new file mode 100644 index 0000000000..bd1594471a --- /dev/null +++ b/files/de/web/api/htmlelement/innertext/index.html @@ -0,0 +1,90 @@ +--- +title: Node.innerText +slug: Web/API/Node/innerText +tags: + - API DOM Property Reference +translation_of: Web/API/HTMLElement/innerText +--- +
{{APIRef("DOM")}}
+ +

Zusammenfassung

+ +

Node.innerText ist eine Eigenschaft die die "gerenderten" Text Inhalte einer node und ihrer nachfahren representiert. Als getter nähert es sich dem Text an, den ein User erhalten würde wenn sie/er den Inhalt des Elementes mit dem Kursor highlighten und dann zum Clipboard kopieren würde. Dieses Feature wurde ursprünglich von Internet Explorer eingeführt, und wurde förmlich in den HTML standards von 2016 spezifiziert nachdem es von allen Hauptbrowsern übernommen wurde.

+ +

{{domxref("Node.textContent")}} ist eine etwas ähnliche Alternative. Es gibt allerdings wichtige Unterschiede zwischen den beiden.

+ +

Spezifikation

+ + + + + + + + + + + + + + +
SpezifikationStatusKommentar
{{SpecName('HTML WHATWG', 'dom.html#the-innertext-idl-attribute', 'innerText')}}{{Spec2('HTML WHATWG')}} +

Eingeführt, basiert auf dem draft of the innerText specification. Siehe whatwg/html#465 und whatwg/compat#5 für die Historie.

+
+ +

Browser Kompatibilität

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support4{{ CompatGeckoDesktop(45) }}69.6 (probably earlier)3
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support2.3 (probably earlier){{ CompatGeckoMobile(45) }}10 (probably earlier)124.1 (probably earlier)
+
+ +

Siehe auch

+ + diff --git a/files/de/web/api/htmlheadelement/index.html b/files/de/web/api/htmlheadelement/index.html new file mode 100644 index 0000000000..1c71988bda --- /dev/null +++ b/files/de/web/api/htmlheadelement/index.html @@ -0,0 +1,28 @@ +--- +title: head +slug: Web/HTML/Element/head +tags: + - HTML + - HTML Elemente + - HTML Referenz +translation_of: Web/API/HTMLHeadElement +--- +

Das <head> HTML Element legt den Kopf eines Dokuments fest. In diesem werden Informationen für das Dokument angegeben, darunter der Dokumententitel und Meta-Daten.

+ +

Beispiel

+ +
<html>
+  <head>
+    <title>Seitentitel</title>
+  </head>
+</html>
+
+ +

Spezifikationen

+ + + +

{{HTMLRef}}

diff --git a/files/de/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html b/files/de/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html new file mode 100644 index 0000000000..6ae6f33dd7 --- /dev/null +++ b/files/de/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html @@ -0,0 +1,194 @@ +--- +title: Grundkonzepte +slug: Web/API/IndexedDB_API/Grundkonzepte_hinter_IndexedDB +translation_of: Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB +--- +

IndexedDB ermöglicht es Ihnen Daten innerhalb des Browsers eines Benutzers permanent abzulegen. Weil es Sie Webanwendungen mit funktionsreichen Abfragemöglichkeiten erstellen lässt, können diese Anwendungen sowohl online als auch offline funktionieren. IndexedDB ist geeignet für Anwendungen, die eine große Menge an Daten speichern (z.B. ein Katalog von DVDs in einer Videothek) und Anwendungen, die keine durchgehende Internetverbindung benötigen um zu funktionieren (z.B. E-Mail-Clients, To-Do-Listen oder Notizen).

+

Über dieses Dokument

+

Diese Einführung bespricht wesentliche Konzepte und Fachbegriffe in IndexedDB. Sie liefert Ihnen einen Gesamtüberblick und führt Sie in die Schlüsselkonzepte ein. Um mehr über die Begrifflichkeiten von IndexedDB zu erfahren, lesen Sie den Abschnitt Definitionen.

+

Eine Anleitung zur Verwendung der API finden Sie im Artikel Using IndexedDB. Eine Referenzdokumentierung der IndexedDB-API finden Sie im Artikel IndexedDB und dessen Unterseiten, welche die Objekttypen dokumentiert, die von IndexedDB verwendet werden, ebenso wie die Methoden von synchronen wie asynchronen APIs.

+

Überblick über IndexedDB

+

Mit IndexedDB lassen sich indizierte Objekte mit „Schlüsseln“ ablegen und abrufen. Alle Änderungen an der Datenbank geschehen innerhalb von Transaktionen. Wie die meisten Webspeicher-Lösungen folgt IndexedDB einer Same-Origin-Policy. Während also auf Daten, die innerhalb einer Domain gespeichert wurden, zugegriffen werden kann, kann nicht domainübergreifend auf Daten zugegriffen werden.

+

Die API umfasst sowohl eine asynchrone API als auch eine synchrone API. Die asynchrone API kann für die meisten Fälle verwendet werden, auch für WebWorkers, während die synchrone API nur für den Gebrauch durch WebWorkers gedacht ist. Momentan wird die synchrone API von keinem der großen Browser unterstützt. Aber selbst wenn synchrone APIs unterstützt wären, würden Sie eher die asynchrone API verwenden, wenn Sie mit IndexedDB arbeiten.

+

IndexedDB ist eine Alternative zur WebSQL-Datenbank, welche vom W3C am 18. November 2010 als veraltet erklärt wurde. Während sowohl IndexedDB als auch WebSQL Lösungen zur Speicherung von Daten bieten, bieten sie nicht dieselben Funktionalitäten. WebSQL-Datenbank ist ein relationales Datenbankanfragesystem, IndexedDB hingegen ist ein indiziertes Tabellensystem.

+

Wichtige Konzepte

+

Wenn Sie die Arbeit mit anderen Datenbanksystemen gewohnt sind kann die Arbeit mit IndexedDB am Anfang ungewohnt erscheinen. Behalten Sie deshalb folgende wichtige Konzepte im Hinterkopf:

+ +

Definitionen

+

Dieser Abschitt definiert und erklärt Begriffe, die in der IndexedDB-API verwendet werden.

+

Datenbank

+
+
+ Datenbank
+
+ Ein Aufbewahrungsort für Informationen, typischerweise bestehend aus einem oder mehreren Objektspeichern. Jede Datenbank muss folgende Angaben enthalten: +
    +
  • Name. Er identifiziert die Datenbank innerhalb einer konkreten Herkunft und verändert sich nicht innerhalb seiner Lebenszeit. Der Name kann aus einem beliebigen String-Wert bestehen (einschließlich dem leeren String).
  • +
  • +

    Aktuelle Version. Wenn eine Datenbank zum ersten Mal erstellt wird, nimmt ihre Version den integer-Wert 1 an, wenn nichts anderes angegeben wird. Jede Datenbank kann zu einem Zeitpunkt nur eine Version haben.

    +
  • +
+
+
+ Objektspeicher
+
+

Das Instrument, mit welchem Daten in einer Datenbank gespeichert werden. Der Objektspeicher hält Eintragungen aus Schlüssel-Wert-Paaren permanent. Eintragungen innerhalb eines Objektspeichers werden entsprechend der Schlüssel in aufsteigender Reihenfolge sortiert.

+

Jeder Objektspeicher muss einen Namen haben, der innerhalb seiner Datenbank einzigartig ist. Der Objektspeicher kann optional einen Schlüsselerzeuger und einen Schlüsselpfad besitzen. Wenn der Objektspeicher einen Schlüsselpfad hat, verwendet er in-line keys; ansonsten out-of-line keys.

+

Eine Referenzdokumentation zu Objektspeichern finden Sie unter IDBObjectStore oder IDBObjectStoreSync.

+
+
+ Version
+
+ Wenn eine Datenbank zum ersten Mal erstellt wird, ist ihre Versionsnummer die integer-Zahl 1. Jede Datenbank hat zu jedem Zeitpunkt genau eine Versionsnummer; eine Datenbank kann nicht in verschiedenen Versionen gleichzeitig existieren. Die Versionsnummer kann nur geändert werden, indem die Datenbank mit einer größeren Versionsnummer geöffnet wird als mit der aktuellen. Das wird die Transaktion versionchange starten und ein upgradeneeded Ereignis auslösen. Die einzige Stelle, an der das Schema der Datenbank geupdatet werden kann, ist innerhalb des Handlers dieses Ereignisses.
+  
+
+ Anmerkung: Diese Definition beschreibt die aktuellsten Spezifikationen, welche nur in Browsern auf dem neuesten Stand implementiert sind. In alten Browsern ist die mittlerweile veraltete und entfernte Methode IDBDatabase.setVersion() implementiert.
+
+ Datenbankverbindung
+
+ Eine Operation, die beim Öffnen einer Datenbank erstellt wird. Eine vorgegebene Datenbank kann mehrere Verbindungen gleichzeitig haben.
+
+ Transaktion
+
+

Eine nicht teilbare und dauerhafte Menge an Datenzugriffs- und Datenmodifikationsoperationen auf einer bestimmten Datenbank. Durch Transaktionen können Sie auf die Daten einer Datenbank zugreifen. Tatsächlich muss jeder Lese- oder Schreibvorgang von Daten in einer Transaktion stattfinden.
+
+ Eine Datenbankverbindung kann mit mehreren aktiven Transaktionen gleichzeitig verknüpft sein, so lange schreibende Transaktionen keine überlappenden scopes haben. The scope of transactions, which is defined at creation, determines which object stores the transaction can interact with and remains constant for the lifetime of the transaction. So, for example, if a database connection already has a writing transaction with a scope that just covers the flyingMonkey object store, you can start a second transaction with a scope of the unicornCentaur and unicornPegasus object stores. As for reading transactions, you can have several of them—even overlapping ones.

+

Transactions are expected to be short-lived, so the browser can terminate a transaction that takes too long, in order to free up storage resources that the long-running transaction has locked. You can abort the transaction, which rolls back the changes made to the database in the transaction. And you don't even have to wait for the transaction to start or be active to abort it.

+

The three modes of transactions are: readwrite, readonly, and versionchange. The only way to create and delete object stores and indexes is by using a versionchange transaction. To learn more about transaction types, see the reference article for IndexedDB.

+

Because everything happens within a transaction, it is a very important concept in IndexedDB. To learn more about transactions, especially on how they relate to versioning, see IDBTransaction, which also has reference documentation. For the documentation on the synchronous API, see IDBTransactionSync.

+
+
+ Anfrage
+
+ Die Operation, mit der Lese- und Schreibvorgänge auf einer Datenbank ausgeführt werden. Jede Anfrage repräsentiert eine Lese- oder Schreiboperation.
+
+ Index
+
+

Ein Spezialobjektspeicher zum Nachschlagen von Einträgen eines anderen Objektspeichers, bezeichnet als referenzierter Objektspeicher. Der Index ist ein persistenter Schlüssel-Wert-Speicher, wobei der Wert seiner Einträge dem Schlüssel eines Eintrages im referenzierten Objektspeicher entspricht. Die Einträge in einem Index werden automatisch eingepflegt, sobald Einträge im referenzierten Objekt eingefügt, aktualisiert oder entfernt werden. Jeder Eintrag in einem Index kann auf nur einen Eintrag in seinem referenzierten Objektspeicher zeigen, aber mehrere Indizes können auf denselben Objektspeicher verweisen. Wenn der Objektspeicher sich ändert, werden alle Indizes, die auf ihn verweisen, automatisch aktualisiert.

+

Alternativ können Einträge eines Objektspeichers mithilfe eines Schlüssels nachgeschlagen werden.

+

Um mehr über die Verwendung von Indizes zu erfahren, lesen Sie Using IndexedDB. Die Referenzdokumentation zu Indizes finden Sie unter IDBKeyRange.

+
+
+

Schlüssel und Wert

+
+
+ Schlüssel
+
+

Ein Datenwert über welchen abgelegte Werte aus dem Objektspeicher sortiert und ausgelesen werden können. Der Objektspeicher kann den Schlüssel aus einer dieser drei Quellen erlangen: Einem Schlüsselgenerator, einem Schlüsselpfad und einem explizit angegebem Wert. Der Schlüssel muss aus einem Datentyp bestehen, der eine Nummer hat, die größer ist als die des Schlüssel vor ihm. Jeder Eintrag in einem Objektspeicher muss einen innerhalb des gleichen Objektspeichers einzigartigen Schlüssel haben, deshalb können nicht mehrere Einträge mit demselben Schlüssel in einem vorgegebenem Objektspeicher vorliegen.
+
+ Ein Schlüssel kann einen der folgenden Typen haben: string, date, float und array. Bei Arrays kann der Schlüssel zwischen einem leeren Wert und unendlich liegen. Arrays können wiederum Arrays beinhalten. Es gibt keine Vorschrift nur Schlüssel der Typen string oder integer zu verwenden {{ fx_minversion_inline("11") }}.

+

Alternativ können Sie Einträge eines Objektspeichers auch mithilfe eines Index nachschlagen.

+
+
+ Schlüsselgenerator
+
+ Ein Mechanismus um neue Schlüssel in einer angeordneten Reihenfolge zu erzeugen. Wenn ein Objektspeicher über keinen Schlüsselgenerator verfügt, muss die Anwendung Schlüssel für zu speichernde Einträge zur Verfügung stellen. Generatoren werden nicht zwischen Speichern geteilt. Dies ist mehr ein Detail von Browserimplementierungen, da in der Webentwicklung nicht wirklich Schlüsselgeneratoren erzeugt oder auf sie zugegriffen wird.
+
+ in-line key
+
+ A key that is stored as part of the stored value. It is found using a key path. An in-line key can be generated using a generator. After the key has been generated, it can then be stored in the value using the key path or it can also be used as a key.
+
+ out-of-line key
+
+ A key that is stored separately from the value being stored.
+
+ key path
+
+ Defines where the browser should extract the key from a value in the object store or index. A valid key path can include one of the following: an empty string, a JavaScript identifier, or multiple JavaScript identifiers separated by periods. It cannot include spaces.
+
+ value
+
+

Each record has a value, which could include anything that can be expressed in JavaScript, including: boolean, number, string, date, object, array, regexp, undefined, and null.

+

When an object or array is stored, the properties and values in that object or array can also be anything that is a valid value.

+

Blobs and files can be stored, cf. specification {{ fx_minversion_inline("11") }}.

+
+
+

Range and scope

+
+
+ scope
+
+ The set of object stores and indexes to which a transaction applies. The scopes of read-only transactions can overlap and execute at the same time. On the other hand, the scopes of writing transactions cannot overlap. You can still start several transactions with the same scope at the same time, but they just queue up and execute one after another.
+
+ cursor
+
+ A mechanism for iterating over multiple records with a key range. The cursor has a source that indicates which index or object store it is iterating. It has a position within the range, and moves in a direction that is increasing or decreasing in the order of record keys. For the reference documentation on cursors, see IDBCursor or IDBCursorSync.
+
+ key range
+
+

A continuous interval over some data type used for keys. Records can be retrieved from object stores and indexes using keys or a range of keys. You can limit or filter the range using lower and upper bounds. For example, you can iterate over all values of a key between x and y.

+

For the reference documentation on key range, see IDBKeyRange.

+
+
+

Limitations

+

IndexedDB is designed to cover most cases that need client-side storage. However, it is not designed for a few cases like the following:

+ +

In addition, be aware that browsers can wipe out the database, such as in the following conditions:

+ +

The exact circumstances and browser capabilities change over time, but the general philosophy of the browser vendors is to make the best effort to keep the data when possible.

+
+

Warning: At the moment due to bugs or on purpose it's impossible to open an IndexedDB database from a Web App. This needs more investigation and then be documented.

+
+

Next step

+

OK, so, now with these big concepts under our belts, we can get to more concrete stuff. For a tutorial on how to use the API, see Using IndexedDB.

+

See also

+

Specification

+ +

Reference

+ +

Tutorials

+ +

Related article

+ diff --git a/files/de/web/api/indexeddb_api/grundkonzepte_hinter_indexeddb/index.html b/files/de/web/api/indexeddb_api/grundkonzepte_hinter_indexeddb/index.html deleted file mode 100644 index 6ae6f33dd7..0000000000 --- a/files/de/web/api/indexeddb_api/grundkonzepte_hinter_indexeddb/index.html +++ /dev/null @@ -1,194 +0,0 @@ ---- -title: Grundkonzepte -slug: Web/API/IndexedDB_API/Grundkonzepte_hinter_IndexedDB -translation_of: Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB ---- -

IndexedDB ermöglicht es Ihnen Daten innerhalb des Browsers eines Benutzers permanent abzulegen. Weil es Sie Webanwendungen mit funktionsreichen Abfragemöglichkeiten erstellen lässt, können diese Anwendungen sowohl online als auch offline funktionieren. IndexedDB ist geeignet für Anwendungen, die eine große Menge an Daten speichern (z.B. ein Katalog von DVDs in einer Videothek) und Anwendungen, die keine durchgehende Internetverbindung benötigen um zu funktionieren (z.B. E-Mail-Clients, To-Do-Listen oder Notizen).

-

Über dieses Dokument

-

Diese Einführung bespricht wesentliche Konzepte und Fachbegriffe in IndexedDB. Sie liefert Ihnen einen Gesamtüberblick und führt Sie in die Schlüsselkonzepte ein. Um mehr über die Begrifflichkeiten von IndexedDB zu erfahren, lesen Sie den Abschnitt Definitionen.

-

Eine Anleitung zur Verwendung der API finden Sie im Artikel Using IndexedDB. Eine Referenzdokumentierung der IndexedDB-API finden Sie im Artikel IndexedDB und dessen Unterseiten, welche die Objekttypen dokumentiert, die von IndexedDB verwendet werden, ebenso wie die Methoden von synchronen wie asynchronen APIs.

-

Überblick über IndexedDB

-

Mit IndexedDB lassen sich indizierte Objekte mit „Schlüsseln“ ablegen und abrufen. Alle Änderungen an der Datenbank geschehen innerhalb von Transaktionen. Wie die meisten Webspeicher-Lösungen folgt IndexedDB einer Same-Origin-Policy. Während also auf Daten, die innerhalb einer Domain gespeichert wurden, zugegriffen werden kann, kann nicht domainübergreifend auf Daten zugegriffen werden.

-

Die API umfasst sowohl eine asynchrone API als auch eine synchrone API. Die asynchrone API kann für die meisten Fälle verwendet werden, auch für WebWorkers, während die synchrone API nur für den Gebrauch durch WebWorkers gedacht ist. Momentan wird die synchrone API von keinem der großen Browser unterstützt. Aber selbst wenn synchrone APIs unterstützt wären, würden Sie eher die asynchrone API verwenden, wenn Sie mit IndexedDB arbeiten.

-

IndexedDB ist eine Alternative zur WebSQL-Datenbank, welche vom W3C am 18. November 2010 als veraltet erklärt wurde. Während sowohl IndexedDB als auch WebSQL Lösungen zur Speicherung von Daten bieten, bieten sie nicht dieselben Funktionalitäten. WebSQL-Datenbank ist ein relationales Datenbankanfragesystem, IndexedDB hingegen ist ein indiziertes Tabellensystem.

-

Wichtige Konzepte

-

Wenn Sie die Arbeit mit anderen Datenbanksystemen gewohnt sind kann die Arbeit mit IndexedDB am Anfang ungewohnt erscheinen. Behalten Sie deshalb folgende wichtige Konzepte im Hinterkopf:

- -

Definitionen

-

Dieser Abschitt definiert und erklärt Begriffe, die in der IndexedDB-API verwendet werden.

-

Datenbank

-
-
- Datenbank
-
- Ein Aufbewahrungsort für Informationen, typischerweise bestehend aus einem oder mehreren Objektspeichern. Jede Datenbank muss folgende Angaben enthalten: -
    -
  • Name. Er identifiziert die Datenbank innerhalb einer konkreten Herkunft und verändert sich nicht innerhalb seiner Lebenszeit. Der Name kann aus einem beliebigen String-Wert bestehen (einschließlich dem leeren String).
  • -
  • -

    Aktuelle Version. Wenn eine Datenbank zum ersten Mal erstellt wird, nimmt ihre Version den integer-Wert 1 an, wenn nichts anderes angegeben wird. Jede Datenbank kann zu einem Zeitpunkt nur eine Version haben.

    -
  • -
-
-
- Objektspeicher
-
-

Das Instrument, mit welchem Daten in einer Datenbank gespeichert werden. Der Objektspeicher hält Eintragungen aus Schlüssel-Wert-Paaren permanent. Eintragungen innerhalb eines Objektspeichers werden entsprechend der Schlüssel in aufsteigender Reihenfolge sortiert.

-

Jeder Objektspeicher muss einen Namen haben, der innerhalb seiner Datenbank einzigartig ist. Der Objektspeicher kann optional einen Schlüsselerzeuger und einen Schlüsselpfad besitzen. Wenn der Objektspeicher einen Schlüsselpfad hat, verwendet er in-line keys; ansonsten out-of-line keys.

-

Eine Referenzdokumentation zu Objektspeichern finden Sie unter IDBObjectStore oder IDBObjectStoreSync.

-
-
- Version
-
- Wenn eine Datenbank zum ersten Mal erstellt wird, ist ihre Versionsnummer die integer-Zahl 1. Jede Datenbank hat zu jedem Zeitpunkt genau eine Versionsnummer; eine Datenbank kann nicht in verschiedenen Versionen gleichzeitig existieren. Die Versionsnummer kann nur geändert werden, indem die Datenbank mit einer größeren Versionsnummer geöffnet wird als mit der aktuellen. Das wird die Transaktion versionchange starten und ein upgradeneeded Ereignis auslösen. Die einzige Stelle, an der das Schema der Datenbank geupdatet werden kann, ist innerhalb des Handlers dieses Ereignisses.
-  
-
- Anmerkung: Diese Definition beschreibt die aktuellsten Spezifikationen, welche nur in Browsern auf dem neuesten Stand implementiert sind. In alten Browsern ist die mittlerweile veraltete und entfernte Methode IDBDatabase.setVersion() implementiert.
-
- Datenbankverbindung
-
- Eine Operation, die beim Öffnen einer Datenbank erstellt wird. Eine vorgegebene Datenbank kann mehrere Verbindungen gleichzeitig haben.
-
- Transaktion
-
-

Eine nicht teilbare und dauerhafte Menge an Datenzugriffs- und Datenmodifikationsoperationen auf einer bestimmten Datenbank. Durch Transaktionen können Sie auf die Daten einer Datenbank zugreifen. Tatsächlich muss jeder Lese- oder Schreibvorgang von Daten in einer Transaktion stattfinden.
-
- Eine Datenbankverbindung kann mit mehreren aktiven Transaktionen gleichzeitig verknüpft sein, so lange schreibende Transaktionen keine überlappenden scopes haben. The scope of transactions, which is defined at creation, determines which object stores the transaction can interact with and remains constant for the lifetime of the transaction. So, for example, if a database connection already has a writing transaction with a scope that just covers the flyingMonkey object store, you can start a second transaction with a scope of the unicornCentaur and unicornPegasus object stores. As for reading transactions, you can have several of them—even overlapping ones.

-

Transactions are expected to be short-lived, so the browser can terminate a transaction that takes too long, in order to free up storage resources that the long-running transaction has locked. You can abort the transaction, which rolls back the changes made to the database in the transaction. And you don't even have to wait for the transaction to start or be active to abort it.

-

The three modes of transactions are: readwrite, readonly, and versionchange. The only way to create and delete object stores and indexes is by using a versionchange transaction. To learn more about transaction types, see the reference article for IndexedDB.

-

Because everything happens within a transaction, it is a very important concept in IndexedDB. To learn more about transactions, especially on how they relate to versioning, see IDBTransaction, which also has reference documentation. For the documentation on the synchronous API, see IDBTransactionSync.

-
-
- Anfrage
-
- Die Operation, mit der Lese- und Schreibvorgänge auf einer Datenbank ausgeführt werden. Jede Anfrage repräsentiert eine Lese- oder Schreiboperation.
-
- Index
-
-

Ein Spezialobjektspeicher zum Nachschlagen von Einträgen eines anderen Objektspeichers, bezeichnet als referenzierter Objektspeicher. Der Index ist ein persistenter Schlüssel-Wert-Speicher, wobei der Wert seiner Einträge dem Schlüssel eines Eintrages im referenzierten Objektspeicher entspricht. Die Einträge in einem Index werden automatisch eingepflegt, sobald Einträge im referenzierten Objekt eingefügt, aktualisiert oder entfernt werden. Jeder Eintrag in einem Index kann auf nur einen Eintrag in seinem referenzierten Objektspeicher zeigen, aber mehrere Indizes können auf denselben Objektspeicher verweisen. Wenn der Objektspeicher sich ändert, werden alle Indizes, die auf ihn verweisen, automatisch aktualisiert.

-

Alternativ können Einträge eines Objektspeichers mithilfe eines Schlüssels nachgeschlagen werden.

-

Um mehr über die Verwendung von Indizes zu erfahren, lesen Sie Using IndexedDB. Die Referenzdokumentation zu Indizes finden Sie unter IDBKeyRange.

-
-
-

Schlüssel und Wert

-
-
- Schlüssel
-
-

Ein Datenwert über welchen abgelegte Werte aus dem Objektspeicher sortiert und ausgelesen werden können. Der Objektspeicher kann den Schlüssel aus einer dieser drei Quellen erlangen: Einem Schlüsselgenerator, einem Schlüsselpfad und einem explizit angegebem Wert. Der Schlüssel muss aus einem Datentyp bestehen, der eine Nummer hat, die größer ist als die des Schlüssel vor ihm. Jeder Eintrag in einem Objektspeicher muss einen innerhalb des gleichen Objektspeichers einzigartigen Schlüssel haben, deshalb können nicht mehrere Einträge mit demselben Schlüssel in einem vorgegebenem Objektspeicher vorliegen.
-
- Ein Schlüssel kann einen der folgenden Typen haben: string, date, float und array. Bei Arrays kann der Schlüssel zwischen einem leeren Wert und unendlich liegen. Arrays können wiederum Arrays beinhalten. Es gibt keine Vorschrift nur Schlüssel der Typen string oder integer zu verwenden {{ fx_minversion_inline("11") }}.

-

Alternativ können Sie Einträge eines Objektspeichers auch mithilfe eines Index nachschlagen.

-
-
- Schlüsselgenerator
-
- Ein Mechanismus um neue Schlüssel in einer angeordneten Reihenfolge zu erzeugen. Wenn ein Objektspeicher über keinen Schlüsselgenerator verfügt, muss die Anwendung Schlüssel für zu speichernde Einträge zur Verfügung stellen. Generatoren werden nicht zwischen Speichern geteilt. Dies ist mehr ein Detail von Browserimplementierungen, da in der Webentwicklung nicht wirklich Schlüsselgeneratoren erzeugt oder auf sie zugegriffen wird.
-
- in-line key
-
- A key that is stored as part of the stored value. It is found using a key path. An in-line key can be generated using a generator. After the key has been generated, it can then be stored in the value using the key path or it can also be used as a key.
-
- out-of-line key
-
- A key that is stored separately from the value being stored.
-
- key path
-
- Defines where the browser should extract the key from a value in the object store or index. A valid key path can include one of the following: an empty string, a JavaScript identifier, or multiple JavaScript identifiers separated by periods. It cannot include spaces.
-
- value
-
-

Each record has a value, which could include anything that can be expressed in JavaScript, including: boolean, number, string, date, object, array, regexp, undefined, and null.

-

When an object or array is stored, the properties and values in that object or array can also be anything that is a valid value.

-

Blobs and files can be stored, cf. specification {{ fx_minversion_inline("11") }}.

-
-
-

Range and scope

-
-
- scope
-
- The set of object stores and indexes to which a transaction applies. The scopes of read-only transactions can overlap and execute at the same time. On the other hand, the scopes of writing transactions cannot overlap. You can still start several transactions with the same scope at the same time, but they just queue up and execute one after another.
-
- cursor
-
- A mechanism for iterating over multiple records with a key range. The cursor has a source that indicates which index or object store it is iterating. It has a position within the range, and moves in a direction that is increasing or decreasing in the order of record keys. For the reference documentation on cursors, see IDBCursor or IDBCursorSync.
-
- key range
-
-

A continuous interval over some data type used for keys. Records can be retrieved from object stores and indexes using keys or a range of keys. You can limit or filter the range using lower and upper bounds. For example, you can iterate over all values of a key between x and y.

-

For the reference documentation on key range, see IDBKeyRange.

-
-
-

Limitations

-

IndexedDB is designed to cover most cases that need client-side storage. However, it is not designed for a few cases like the following:

- -

In addition, be aware that browsers can wipe out the database, such as in the following conditions:

- -

The exact circumstances and browser capabilities change over time, but the general philosophy of the browser vendors is to make the best effort to keep the data when possible.

-
-

Warning: At the moment due to bugs or on purpose it's impossible to open an IndexedDB database from a Web App. This needs more investigation and then be documented.

-
-

Next step

-

OK, so, now with these big concepts under our belts, we can get to more concrete stuff. For a tutorial on how to use the API, see Using IndexedDB.

-

See also

-

Specification

- -

Reference

- -

Tutorials

- -

Related article

- diff --git a/files/de/web/api/indexeddb_api/indexeddb_verwenden/index.html b/files/de/web/api/indexeddb_api/indexeddb_verwenden/index.html deleted file mode 100644 index 3d6d61cf3f..0000000000 --- a/files/de/web/api/indexeddb_api/indexeddb_verwenden/index.html +++ /dev/null @@ -1,1180 +0,0 @@ ---- -title: Verwendung von IndexedDB -slug: Web/API/IndexedDB_API/IndexedDB_verwenden -translation_of: Web/API/IndexedDB_API/Using_IndexedDB ---- -

Mit IndexedDB lassen sich Daten innerhalb des Browsers eines Benutzers permanent abzulegen. Es können so Webanwendungen mit funktionsreichen Abfragemöglichkeiten in Anwendungen erstellt werden, die sowohl online als auch offline funktionieren können, da keine Netzwerkfunktionalitäten benötigt werden.

- -

Über dieses Dokument

- -

Dieses Tutorial bespricht die Verwendung der asynchronen API von IndexedDB. Wenn Sie nicht mit IndexedDB vertraut sind, sollten Sie zuerst den Artikel Grundkonzepte lesen.

- -

Eine Referenzdokumentation zur IndexedDB-API finden Sie im Artikel IndexedDB und dessen Unterseiten, welche die Typen und Objekten dokumentieren, die von IndexedDB verwendet werden, ebenso wie die Methoden von sowohl synchronen als auch asynchronen APIs.

- -

Grundschema

- -

Das von IndexedDB unterstützte Grundschema sieht folgendermaßen aus:

- -
    -
  1. Öffne eine Datenbank und starte eine Transaktion.
  2. -
  3. Erzeuge einen Objektspeicher.
  4. -
  5. Fordere die Ausführung von Datenbankoperationen an, wie das Hinzufügen und Auslesen von Daten.
  6. -
  7. Warte auf die richtige Art von DOM-Ereignis, das auftritt, wenn die Operation beendet ist.
  8. -
  9. Verarbeite die Ergebnisse? (, welche im Anforderungsobjekt gefunden werden können).
  10. -
- -

Mit dem Wissen über diese Grundkonzepte können wir uns nun konkreteren Dingen zuwenden.

- -

Erzeugung und Strukturierung des Speichers

- -

Weil sich die Spezifizierung noch in der Entwicklung befindet, verstecken sich aktuelle Implementierungen von IndexedDB unter Browserpräfixen. Bis sich die Spezifizierung verfestigt, könne Browserhersteller unterschiedliche Implementierungen der Standard-IndexedDB-API haben. Aber sobald Konsens auf dem Standard herrscht, implementieren die Hersteller ihn ohne Markierung durch Präfixe. Tatsächlich ist in manchen Implementierungen der Präfix entfernt: Internet Explorer 10, Firefox 16, Chrome 24. Wenn auf Gecko basierende Browser einen Präfix verwenden, dann verwenden sie den Präfix moz, während auf WebKit basierende Browser den Präfix webkit verwenden.

- -

Verwendung einer experimentellen Version von IndexedDB

- -

Für den Fall, dass Sie Ihren Code in Browsern verwenden wollen, die noch Präfixe verwenden, können Sie folgenden Code benutzen:

- -
// In der folgenden Zeile sollten Sie die Präfixe einfügen, die Sie testen wollen.
-window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
-// Verwenden Sie "var indexedDB = ..." NICHT außerhalb einer Funktion.
-// Ferner benötigen Sie evtl. Referenzen zu einigen window.IDB* Objekten:
-window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
-window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
-// (Mozilla hat diese Objekte nie mit Präfixen versehen, also brauchen wir kein window.mozIDB*)
- -

Beachten Sie, dass Implementierungen, die Präfixe verwenden, fehlerhaft oder unvollständig sein können oder einer alten Version der Spezifizierung folgen können. Deshalb ist es nicht empfohlen, sie im Produktivsystem zu verwenden. In manchen Fällen kann es sinnvoll sein, lieber einen Browser nicht zu unterstützen als zu behaupten, er würde unterstützt, und dann Fehler einzubüßen:

- -
if (!window.indexedDB) {
-    window.alert("Ihr Browser unterstützt keine stabile Version von IndexedDB. Dieses und jenes Feature wird Ihnen nicht zur Verfügung stehen.");
-}
-
- -

Öffnen einer Datenbank

- -

Wir starten den ganzen Prozess folgendermaßen:

- -
// Öffnen unserer Datenbank
-var request = window.indexedDB.open("MeineTestdatenbank", 3);
-
- -

Sie sehen, das Öffnen einer Datenbank funktioniert wie jede andere Operation – Sie müssen sie „anfordern“.

- -

Die Anforderung zum Öffnen öffnet nicht sofort die Datenbank und startet auch die Transaktion nicht gleich. Der Aufruf zur open() Funktion gibt ein IDBOpenDBRequest-Objekt mit Ergebniswert (Erfolg) oder Fehlerwert zurück, die Sie als Ereignis verarbeiten. Die meisten anderen asynchronen Funktionen in IndexedDB machen das gleiche – sie geben ein IDBRequest-Objekt mit Ergebnis oder Fehler zurück. Das Ergebnis für die open-Funktion ist eine Instanz einer IDBDatabase.

- -

Der zweite Parameter der open-Methode ist die Version der Datenbank. Die Version der Datenbank bestimmt das Datenbankschema – den Objektspeichern und ihrer Strukturen. Wenn die angeforderte Version nicht existiert (weil die Datenbank neu ist, oder weil die Version aktualisiert wurde), wird das Ereignis onupgradeneeded ausgelöst, und es lässt sich eine neue Version der Datenbank im Handler für dieses Ereignis erzeugen. Mehr dazu später im Abschnitt Aktualisieren der Version der Datenbank.

- -

Erzeugen von Handlern

- -

Das erste, was Sie mit fast allen Anforderungen machen wollen, die Sie erzeugen, ist das Hinzufügen von Handlern für Erfolge und Fehler:

- -
request.onerror = function(event) {
-  // Machen Sie etwas mit request.errorCode!
-};
-request.onsuccess = function(event) {
-  // Machen Sie etwas mit request.result!
-};
- -

Welche der beiden Funktionen, onsuccess() oder onerror(), wird aufgerufen? Wenn alles fehlerfrei ablief, wird ein Erfolgsereignis (d.h. ein DOM-Ereignis, dessen type Eigenschaft auf "success" gesetzt ist) mit request als target ausgelöst. Sobald es ausgelöst wurde, wird die Funktion onsuccess() auf request ausgelöst mit dem Erfolgsereignis als Argument. Wenn nicht alles fehlerfrei ablief, wird ein Fehlerereignis (d.h. ein DOM-Ereignis, dessen type Eigenschaft auf "error" gesetzt ist) auf request ausgelöst. Dies löst die Funktion onerror() aus mit dem Fehlerereignis als Argument.

- -

Die IndexedDB-API ist so entworfen, dass sie die Notwendigkeit zur Fehlerbehandlung minimiert, also werden Sie wahrscheinlich nicht viele Fehlerereignisse sehen (zumindest nicht nachdem Sie sich an die API gewöhnt haben!). Beim Öffnen von Datenbanken jedoch gibt es ein paar typische Zustände, die Fehlerereignisse erzeugen. Das wahrscheinlichste Problem ist, dass der Benutzer festgelegt hat, den Webapps das Erzeugen von Datenbanken nicht zu erlauben. Eines der Hauptentwurfsziele von IndexedDB ist es, das Speichern von großen Datenmengen zur Offline-Verwendung zu erlauben. (Um mehr darüber zu erfahren, wieviel Speicher jedem Browser zur Verfügung steht, lesen Sie Storage limits).

- -

Offensichtlich wollen Browser nicht irgendeinem Werbenetzwerk oder einer böswilligen Website erlauben, Ihren Computer zu verschmutzen, deshalb warnen Browser den Benutzer, wenn eine Webapp zum ersten Mal versucht einen IndexedDB-Speicher zu öffnen. Der Benutzer kann wählen, ob er den Zugriff erlaubt oder verbietet. Außerdem ist IndexedDB komplett deaktiviert im Privaten Modus der Browser (Privater Modus für Firefox und Incognito Modus für Chrome). Der Hauptzweck vom Surfen im Privaten Modus ist es, keine Spuren zu hinterlassen, daher schlägt der Versuch fehl, eine Datenbank in diesem Modus zu öffnen.

- -

Nun nehmen wir an, dass der Benutzer Ihren Anfragen erlaubt hat, eine Datenbank zu erstellen, und Sie haben ein Erfolgsereignis erhalten, um den Erfolgs-Callback auszulösen; was kommt als nächstes? Die Anfrage wurde hier mit einem Aufruf von indexedDB.open() erzeugt, also ist request.result eine Instanz von IDBDatabase, und Sie wollen diese auf jeden Fall für später speichern. Ihr Code könnte etwa so aussehen:

- -
var db;
-var request = indexedDB.open("MeineTestdatenbank");
-request.onerror = function(event) {
-  alert("Warum haben Sie meiner Webapp nicht erlaubt IndexedDB zu verwenden?!");
-};
-request.onsuccess = function(event) {
-  db = request.result;
-};
-
- -

Fehlerbehandlung

- -

Wie bereits oben erwähnt, werden Error events bei entsprechenden Fehlern ausgelöst und in der Objekthierarachie weiter nach oben gereicht. Solche Fehlerereignisse  werden zunächst in der entsprechende Anfrage ausgelöst, die den Fehler verursacht hat. Anschließend werden sie zur Transaktion weitergereicht und schließlich zum Datenbankobjekt.  Wenn man nicht für jede Anfrage einen Error-Handler schreiben möchte, kann man der Datenbank einen einzigen Error-Handler hinzufügen:

- -
db.onerror = function(event) {
-  // Allgemeine Fehlerbehandlung, die für alle Anfragen an die Datenbank gilt.
-  alert("Datenbankfehler: " + event.target.errorCode);
-};
-
- -

Einer der häufigsten Fehler, die beim Öffnen der Datenbank auftreten, ist  VER_ERR.  Er zeigt an, dass die Versionsnummer der lokal gespeicherten Datenbank  größer  als die Versionsnummer ist,  die man zu öffnen versucht. Ein solcher Fehler muss immer durch eine Fehlerbehandlung berücksichtigt werden.

- -

Erstellen oder Updaten der Datenbank

- -

Wenn eine neue Version der Datenbank erstellt wird, wird das onupgradeneeded Event ausgelöst. In der Handler-Funktion dieses Events musst du für die Erstellung der Datenbankspeicher, welche für diese Version benötigt wird, sorgen:

- -
// Dieses Event ist lediglich in modernen Browsern verfügbar
-request.onupgradeneeded = function(event) {
-  var db = event.target.result;
-
-  // Erstelle ein ObjectStore für diese Datenbank
-  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
-};
- -

The Versionsnummer der Datenbank ist vom Typ unsigned long long, so dass sie eine sehr große Ganzzahl sein kann.

- -
-

Das Bedeutet auch, dass sie nicht vom Typ float sein darf,  ansonsten wird sie zur nächstkleineren Ganzzahl abgerundet, so dass die Transaktion weder starten kann noch ein  upgradeneeded ausgelöst wird. Beispielsweise sollte man nicht 2.4 als Versionsnummer verwenden:

- -
var request = indexedDB.open("MeineTestdatenbank", 2.4); // don't do this, as the version will be rounded to 2
-
- -

Wenn man die Versionsnummer der Datenbank erhöht, wird ein  onupgradeneeded -Ereignis ausgelöst. In diesem Fall übernimmt die neue Datenbank automatisch die Objectstores von der Vorgängerversion der Datenbank, so dass man diese nicht erneut erzeugen muss. Lediglich neue Objectsores müssen angelegt oder nicht mehr gebrauchte der Vorgängerversion gelöscht werden. Wenn man einen bereits existierenden Objectstore ändern will (beispielsweise den keyPath veränden), ist es allerdings notwendig, den alten Objectstore zu löschen und einen neuen anzulegen. (Beachten Sie, dass dies die im Objectstore gespeicherten Informationen löscht, so dass man sie vorher auslesen und an anderer Stelle sichern sollte, bevor man ein Datenbankupgrade durchführt.)

- -

WebKit unterstützt die aktuelle Version dieser Spezifikation, wie sie in Chrome 23+ ausgeliefert wird, ebenso Opera 17+ und IE10+. Andere und ältere Implementierungen unterstützen nicht die aktuelle Version dieser Spezifikation und stellen damit auch noch nicht die Möglichkeit bereit, auf die Methodensignatur indexedDB.open(name, version).onupgradeneeded zugreifen zu könenn. Um ältere Versionen einer Datenbank auf neuer Versionen zu aktualisieren, siehe IDBDatabase reference article.

- -

Strukturierung der Datenbank

- -

Im Folgenden wird gezeigt, wie man eine die Daten  strukturiert. IndexedDB verwendet Objectstores anstatt Tabellen, wobei eine einzelne Datenbank viele verschiedene Objectsores enthalten kann. Jeglicher Wert im Objectstore ist einem bestimmten Schlüssel (key) zugeordnet. Es werden dabei unterschiedliche Arten von Schlüsseln verarbeitet, abhängig davon, ob der Objectstore  einen key path oder einen key generator benutzt.

- -

Die folgende Tabelle gibt eine Übersicht über die bereitgestellten Arten von Schlüssseln.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Key PathKey GeneratorBeschreibung
NeinNeinDieser Objectstore kann Daten beliebigen Typs speichern, inklusive primitiven Typen wie Zahlen und Zeichenketten (Strings). Beim Hinzufügen von Werten muss ein separater Schlüssel angegeben werden.
JaNeinDieser Objectstore kann nur JavaScript-Objekte speichern. Die Objekte müssen ein Attribut haben, dass wie der key path benannt ist.
NeinJaDieser Objectstore kann Daten beliebigen Typs speichern. Beim Hinzufügen von Werten kann ein separater Schlüssel angegeben werden. Fehlt der Schlüssel, so wird er generiert.
JaJaDieser Objectstore kann nur JavaScript-Objekte speichern. In der Regel wird der Schlüssel automatisch generiert und als gleichnamiges Attribut im Objekt gespeichert. Falls es jedoch bereits ein Attribut mit dem Namen gibt, so wird der Schlüssel nicht generiert, sondern der Wert des Attributes wird verwendet.
- -

You can also create indices on any object store, provided the object store holds objects, not primitives. An index lets you look up the values stored in an object store using the value of a property of the stored object, rather than the object's key.

- -

Additionally, indexes have the ability to enforce simple constraints on the stored data. By setting the unique flag when creating the index, the index ensures that no two objects are stored with both having the same value for the index's key path. So, for example, if you have an object store which holds a set of people, and you want to ensure that no two people have the same email address, you can use an index with the unique flag set to enforce this.

- -

That may sound confusing, but this simple example should illustrate the concepts:

- -
// This is what our customer data looks like.
-const customerData = [
-  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
-  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
-];
-const dbName = "the_name";
-
-var request = indexedDB.open(dbName, 2);
-
-request.onerror = function(event) {
-  // Handle errors.
-};
-request.onupgradeneeded = function(event) {
-  var db = event.target.result;
-
-  // Create an objectStore to hold information about our customers. We're
-  // going to use "ssn" as our key path because it's guaranteed to be
-  // unique.
-  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
-
-  // Create an index to search customers by name. We may have duplicates
-  // so we can't use a unique index.
-  objectStore.createIndex("name", "name", { unique: false });
-
-  // Create an index to search customers by email. We want to ensure that
-  // no two customers have the same email, so use a unique index.
-  objectStore.createIndex("email", "email", { unique: true });
-
-  // Store values in the newly created objectStore.
-  for (var i in customerData) {
-    objectStore.add(customerData[i]);
-  }
-};
-
- -

As mentioned previously, onupgradeneeded is the only place where you can alter the structure of the database. In it, you can create and delete object stores and build and remove indices.

- -
Object stores are created with a single call to createObjectStore(). The method takes a name of the store, and a parameter object. Even though the parameter object is optional, it is very important, because it lets you define important optional properties and refine the type of object store you want to create. In our case, we've asked for an object store named "customers" and defined a keyPath that is the property that makes an individual object in the store unique. That property in this example is "ssn" since a social security number is guaranteed to be unique. "ssn" must be present on every object that is stored in the objectStore.
- -

We've also asked for an index named "name" that looks at the name property of the stored objects. As with createObjectStore(), createIndex() takes an optional options object that refines the type of index that you want to create. Adding objects that don't have a name property still succeeds, but the object won't appear in the "name" index.

- -

We can now retrieve the stored customer objects using their ssn from the object store directly, or using their name by using the index. To learn how this is done, see the section on using an index.

- -

Hinzufügen und löschen von Daten

- -

Before you can do anything with your new database, you need to start a transaction. Transactions come from the database object, and you have to specify which object stores you want the transaction to span. Also, you need to decide if you're going to make changes to the database or if you just need to read from it. Although transactions have three modes (read-only, read/write, and versionchange), you're better off using a read-only transaction when you can, because they can run concurrently

- -

Daten zur Datenbank hinzufügen

- -

If you've just created a database, then you probably want to write to it. Here's what that looks like:

- -
var transaction = db.transaction(["customers"], "readwrite");
-// Note: Older experimental implementations use the deprecated constant IDBTransaction.READ_WRITE instead of "readwrite".
-// In case you want to support such an implementation, you can just write:
-// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);
- -

The transaction() function takes two arguments (though one is optional) and returns a transaction object. The first argument is a list of object stores that the transaction will span. You can pass an empty array if you want the transaction to span all object stores, but don't do it because the spec says an empty array should generate an InvalidAccessError. If you don't specify anything for the second argument, you get a read-only transaction. Since you want to write to it here you need to pass the "readwrite" flag.

- -

Now that you have a transaction you need to understand its lifetime. Transactions are tied very closely to the event loop. If you make a transaction and return to the event loop without using it then the transaction will become inactive. The only way to keep the transaction active is to make a request on it. When the request is finished you'll get a DOM event and, assuming that the request succeeded, you'll have another opportunity to extend the transaction during that callback. If you return to the event loop without extending the transaction then it will become inactive, and so on. As long as there are pending requests the transaction remains active. Transaction lifetimes are really very simple but it might take a little time to get used to. A few more examples will help, too. If you start seeing TRANSACTION_INACTIVE_ERR error codes then you've messed something up.

- -

Transactions can receive DOM events of three different types: error, abort, and complete. We've talked about the way that error events bubble, so a transaction receives error events from any requests that are generated from it. A more subtle point here is that the default behavior of an error is to abort the transaction in which it occurred. Unless you handle the error by calling preventDefault() on the error event, the entire transaction is rolled back. This design forces you to think about and handle errors, but you can always add a catchall error handler to the database if fine grained error handling is too cumbersome. If you don't handle an error event or if you call abort() on the transaction, then the transaction is rolled back and an abort event is fired on the transaction. Otherwise, after all pending requests have completed, you'll get a complete event. If you're doing lots of database operations, then tracking the transaction rather than individual requests can certainly aide your sanity.

- -

Now that you have a transaction, you'll need to get the object store from it. Transactions only let you have an object store that you specified when creating the transaction. Then you can add all the data you need.

- -
// Do something when all the data is added to the database.
-transaction.oncomplete = function(event) {
-  alert("All done!");
-};
-
-transaction.onerror = function(event) {
-  // Don't forget to handle errors!
-};
-
-var objectStore = transaction.objectStore("customers");
-for (var i in customerData) {
-  var request = objectStore.add(customerData[i]);
-  request.onsuccess = function(event) {
-    // event.target.result == customerData[i].ssn;
-  };
-}
- -

The result of a request generated from a call to add() is the key of the value that was added. So in this case, it should equal the ssn property of the object that was added, since the object store uses the ssn property for the key path. Note that the add() function requires that no object already be in the database with the same key. If you're trying to modify an existing entry, or you don't care if one exists already, use the put() function.

- -

Daten aus der Datenbank löschen

- -

Löschen von Daten ist recht ähnlich:

- -
var request = db.transaction(["customers"], "readwrite")
-                .objectStore("customers")
-                .delete("444-44-4444");
-request.onsuccess = function(event) {
-  // It's gone!
-};
- -

Daten aus der Datenbank auslesen

- -

Now that the database has some info in it, you can retrieve it in several ways. First, the simple get(). You need to provide the key to retrieve the value, like so:

- -
var transaction = db.transaction(["customers"]);
-var objectStore = transaction.objectStore("customers");
-var request = objectStore.get("444-44-4444");
-request.onerror = function(event) {
-  // Handle errors!
-};
-request.onsuccess = function(event) {
-  // Do something with the request.result!
-  alert("Name for SSN 444-44-4444 is " + request.result.name);
-};
- -

That's a lot of code for a "simple" retrieval. Here's how you can shorten it up a bit, assuming that you handle errors at the database level:

- -
db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
-  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
-};
- -

See how this works? Since there's only one object store, you can avoid passing a list of object stores you need in your transaction and just pass the name as a string. Also, you're only reading from the database, so you don't need a "readwrite" transaction. Calling transaction() with no mode specified gives you a "readonly" transaction. Another subtlety here is that you don't actually save the request object to a variable. Since the DOM event has the request as its target you can use the event to get to the result property. Easy, right?!

- -

Benutzung eines Cursors

- -

Using get() requires that you know which key you want to retrieve. If you want to step through all the values in your object store, then you can use a cursor. Here's what it looks like:

- -
var objectStore = db.transaction("customers").objectStore("customers");
-
-objectStore.openCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
-    cursor.continue();
-  }
-  else {
-    alert("No more entries!");
-  }
-};
- -

The openCursor() function takes several arguments. First, you can limit the range of items that are retrieved by using a key range object that we'll get to in a minute. Second, you can specify the direction that you want to iterate. In the above example, we're iterating over all objects in ascending order. The success callback for cursors is a little special. The cursor object itself is the result of the request (above we're using the shorthand, so it's event.target.result). Then the actual key and value can be found on the key and value properties of the cursor object. If you want to keep going, then you have to call continue() on the cursor. When you've reached the end of the data (or if there were no entries that matched your openCursor() request) you still get a success callback, but the result property is undefined.

- -

One common pattern with cursors is to retrieve all objects in an object store and add them to an array, like this:

- -
var customers = [];
-
-objectStore.openCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    customers.push(cursor.value);
-    cursor.continue();
-  }
-  else {
-    alert("Got all customers: " + customers);
-  }
-};
- -
Warnung: Die folgende Funktion ist nicht Teil des IndexedDB Standards.
- -

Mozilla has also implemented getAll() to handle this case. It isn't part of the IndexedDB standard, so it may disappear in the future. We've included it because we think it's useful. The following code does precisely the same thing as above:

- -
objectStore.getAll().onsuccess = function(event) {
-  alert("Got all customers: " + event.target.result);
-};
- -

There is a performance cost associated with looking at the value property of a cursor, because the object is created lazily. When you use getAll(), Gecko must create all the objects at once. If you're just interested in looking at each of the keys, for instance, it is much more efficient to use a cursor than to use getAll(). If you're trying to get an array of all the objects in an object store, though, use getAll().

- -

Benutzung eines Index

- -

Storing customer data using the SSN as a key is logical since the SSN uniquely identifies an individual. (Whether this is a good idea for privacy is a different question, outside the scope of this article.) If you need to look up a customer by name, however, you'll need to iterate over every SSN in the database until you find the right one. Searching in this fashion would be very slow, so instead you can use an index.

- -
var index = objectStore.index("name");
-index.get("Donna").onsuccess = function(event) {
-  alert("Donna's SSN is " + event.target.result.ssn);
-};
- -

The "name" cursor isn't unique, so there could be more than one entry with the name set to "Donna". In that case you always get the one with the lowest key value.

- -

If you need to access all the entries with a given name you can use a cursor. You can open two different types of cursors on indexes. A normal cursor maps the index property to the object in the object store. A key cursor maps the index property to the key used to store the object in the object store. The differences are illustrated here:

- -
index.openCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // cursor.key is a name, like "Bill", and cursor.value is the whole object.
-    alert("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
-    cursor.continue();
-  }
-};
-
-index.openKeyCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // cursor.key is a name, like "Bill", and cursor.value is the SSN.
-    // No way to directly get the rest of the stored object.
-    alert("Name: " + cursor.key + ", SSN: " + cursor.value);
-    cursor.continue();
-  }
-};
- -

Specifying the range and direction of cursors

- -

If you would like to limit the range of values you see in a cursor, you can use a key range object and pass it as the first argument to openCursor() or openKeyCursor(). You can make a key range that only allows a single key, or one the has a lower or upper bound, or one that has both a lower and upper bound. The bound may be "closed" (i.e., the key range includes the given value) or "open" (i.e., the key range does not include the given value). Here's how it works:

- -
// Only match "Donna"
-var singleKeyRange = IDBKeyRange.only("Donna");
-
-// Match anything past "Bill", including "Bill"
-var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
-
-// Match anything past "Bill", but don't include "Bill"
-var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
-
-// Match anything up to, but not including, "Donna"
-var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
-
-//Match anything between "Bill" and "Donna", but not including "Donna"
-var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
-
-index.openCursor(boundKeyRange).onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // Do something with the matches.
-    cursor.continue();
-  }
-};
- -

Sometimes you may want to iterate in descending order rather than in ascending order (the default direction for all cursors). Switching direction is accomplished by passing prev to the openCursor() function:

- -
objectStore.openCursor(null, IDBCursor.prev).onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // Do something with the entries.
-    cursor.continue();
-  }
-};
- -

Since the "name" index isn't unique, there might be multiple entries where name is the same. Note that such a situation cannot occur with object stores since the key must always be unique. If you wish to filter out duplicates during cursor iteration over indexes, you can pass nextunique (or prevunique if you're going backwards) as the direction parameter. When nextunique or prevunique is used, the entry with the lowest key is always the one returned.

- -
index.openKeyCursor(null, IDBCursor.nextunique).onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // Do something with the entries.
-    cursor.continue();
-  }
-};
- -

Versionsänderung während eine Webapplikation in einem anderen Tab geöffnet ist

- -

When your web app changes in such a way that a version change is required for your database, you need to consider what happens if the user has the old version of your app open in one tab and then loads the new version of your app in another. When you call open() with a greater version than the actual version of the database, all other open databases must explicitly acknowledge the request before you can start making changes to the database. Here's how it works:

- -
var openReq = mozIndexedDB.open("MyTestDatabase", 2);
-
-openReq.onblocked = function(event) {
-  // If some other tab is loaded with the database, then it needs to be closed
-  // before we can proceed.
-  alert("Please close all other tabs with this site open!");
-};
-
-openReq.onupgradeneeded = function(event) {
-  // All other databases have been closed. Set everything up.
-  db.createObjectStore(/* ... */);
-  useDatabase(db);
-}
-
-openReq.onsuccess = function(event) {
-  var db = event.target.result;
-  useDatabase(db);
-  return;
-}
-
-function useDatabase(db) {
-  // Make sure to add a handler to be notified if another page requests a version
-  // change. We must close the database. This allows the other page to upgrade the database.
-  // If you don't do this then the upgrade won't happen until the user close the tab.
-  db.onversionchange = function(event) {
-    db.close();
-    alert("A new version of this page is ready. Please reload!");
-  };
-
-  // Do stuff with the database.
-}
-
- -

Sicherheit

- -

IndexedDB uses the same-origin principle, which means that it ties the store to the origin of the site that creates it (typically, this is the site domain or subdomain), so it cannot be accessed by any other origin.

- -

It's important to note that IndexedDB doesn't work for content loaded into a frame from another site (either {{ HTMLElement("frame") }} or {{ HTMLElement("iframe") }}. This is a security measure. Details as to why this matters are forthcoming. See {{ bug(595307) }}.

- -

Warnung über die Schließung des Browsers

- -

When the browser shuts down (e.g., when the user selects Exit or clicks the Close button), any pending IndexedDB transactions are (silently) aborted -- they will not complete, and they will not trigger the error handler. Since the user can exit the browser at any time, this means that you cannot rely upon any particular transaction to complete or to know that it did not complete. There are several implications of this behavior.

- -

First, you should take care to always leave your database in a consistent state at the end of every transaction. For example, suppose that you are using IndexedDB to store a list of items that you allow the user to edit. You save the list after the edit by clearing the object store and then writing out the new list. If you clear the object store in one transaction and write the new list in another transaction, there is a danger that the browser will close after the clear but before the write, leaving you with an empty database. To avoid this, you should combine the clear and the write into a single transaction.

- -

Second, you should never tie database transactions to unload events. If the unload event is triggered by the browser closing, any transactions created in the unload event handler will never complete. An intuitive approach to maintaining some information across browser sessions is to read it from the database when the browser (or a particular page) is opened, update it as the user interacts with the browser, and then save it to the database when the browser (or page) closes. However, this will not work. The database transactions will be created in the unload event handler, but because they are asynchronous they will be aborted before they can execute.

- -

In fact, there is no way to guarantee that IndexedDB transactions will complete, even with normal browser shutdown. See {{ bug(870645) }}.

- -

Vollständiges IndexedDB Beispiel

- -

HTML Content

- -
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
-
-    <h1>IndexedDB Demo: storing blobs, e-publication example</h1>
-    <div class="note">
-      <p>
-        Works and tested with:
-      </p>
-      <div id="compat">
-      </div>
-    </div>
-
-    <div id="msg">
-    </div>
-
-    <form id="register-form">
-      <table>
-        <tbody>
-          <tr>
-            <td>
-              <label for="pub-title" class="required">
-                Title:
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-title" name="pub-title" />
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="pub-biblioid" class="required">
-                Bibliographic ID:<br/>
-                <span class="note">(ISBN, ISSN, etc.)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-biblioid" name="pub-biblioid"/>
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="pub-year">
-                Year:
-              </label>
-            </td>
-            <td>
-              <input type="number" id="pub-year" name="pub-year" />
-            </td>
-          </tr>
-        </tbody>
-        <tbody>
-          <tr>
-            <td>
-              <label for="pub-file">
-                File image:
-              </label>
-            </td>
-            <td>
-              <input type="file" id="pub-file"/>
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="pub-file-url">
-                Online-file image URL:<br/>
-                <span class="note">(same origin URL)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-file-url" name="pub-file-url"/>
-            </td>
-          </tr>
-        </tbody>
-      </table>
-
-      <div class="button-pane">
-        <input type="button" id="add-button" value="Add Publication" />
-        <input type="reset" id="register-form-reset"/>
-      </div>
-    </form>
-
-    <form id="delete-form">
-      <table>
-        <tbody>
-          <tr>
-            <td>
-              <label for="pub-biblioid-to-delete">
-                Bibliographic ID:<br/>
-                <span class="note">(ISBN, ISSN, etc.)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-biblioid-to-delete"
-                     name="pub-biblioid-to-delete" />
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="key-to-delete">
-                Key:<br/>
-                <span class="note">(for example 1, 2, 3, etc.)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="key-to-delete"
-                     name="key-to-delete" />
-            </td>
-          </tr>
-        </tbody>
-      </table>
-      <div class="button-pane">
-        <input type="button" id="delete-button" value="Delete Publication" />
-        <input type="button" id="clear-store-button"
-               value="Clear the whole store" class="destructive" />
-      </div>
-    </form>
-
-    <form id="search-form">
-      <div class="button-pane">
-        <input type="button" id="search-list-button"
-               value="List database content" />
-      </div>
-    </form>
-
-    <div>
-      <div id="pub-msg">
-      </div>
-      <div id="pub-viewer">
-      </div>
-      <ul id="pub-list">
-      </ul>
-    </div>
-
- -

CSS Content

- -
body {
-  font-size: 0.8em;
-  font-family: Sans-Serif;
-}
-
-form {
-  background-color: #cccccc;
-  border-radius: 0.3em;
-  display: inline-block;
-  margin-bottom: 0.5em;
-  padding: 1em;
-}
-
-table {
-  border-collapse: collapse;
-}
-
-input {
-  padding: 0.3em;
-  border-color: #cccccc;
-  border-radius: 0.3em;
-}
-
-.required:after {
-  content: "*";
-  color: red;
-}
-
-.button-pane {
-  margin-top: 1em;
-}
-
-#pub-viewer {
-  float: right;
-  width: 48%;
-  height: 20em;
-  border: solid #d092ff 0.1em;
-}
-#pub-viewer iframe {
-  width: 100%;
-  height: 100%;
-}
-
-#pub-list {
-  width: 46%;
-  background-color: #eeeeee;
-  border-radius: 0.3em;
-}
-#pub-list li {
-  padding-top: 0.5em;
-  padding-bottom: 0.5em;
-  padding-right: 0.5em;
-}
-
-#msg {
-  margin-bottom: 1em;
-}
-
-.action-success {
-  padding: 0.5em;
-  color: #00d21e;
-  background-color: #eeeeee;
-  border-radius: 0.2em;
-}
-
-.action-failure {
-  padding: 0.5em;
-  color: #ff1408;
-  background-color: #eeeeee;
-  border-radius: 0.2em;
-}
-
-.note {
-  font-size: smaller;
-}
-
-.destructive {
-  background-color: orange;
-}
-.destructive:hover {
-  background-color: #ff8000;
-}
-.destructive:active {
-  background-color: red;
-}
-
- -

 

- -

JavaScript Content

- -
(function () {
-  var COMPAT_ENVS = [
-    ['Firefox', ">= 16.0"],
-    ['Google Chrome',
-     ">= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support"]
-  ];
-  var compat = $('#compat');
-  compat.empty();
-  compat.append('<ul id="compat-list"></ul>');
-  COMPAT_ENVS.forEach(function(val, idx, array) {
-    $('#compat-list').append('<li>' + val[0] + ': ' + val[1] + '</li>');
-  });
-
-  const DB_NAME = 'mdn-demo-indexeddb-epublications';
-  const DB_VERSION = 1; // Use a long long for this value (don't use a float)
-  const DB_STORE_NAME = 'publications';
-
-  var db;
-
-  // Used to keep track of which view is displayed to avoid to uselessly reload it
-  var current_view_pub_key;
-
-  function openDb() {
-    console.log("openDb ...");
-    var req = indexedDB.open(DB_NAME, DB_VERSION);
-    req.onsuccess = function (evt) {
-      // Better use "this" than "req" to get the result to avoid problems with
-      // garbage collection.
-      // db = req.result;
-      db = this.result;
-      console.log("openDb DONE");
-    };
-    req.onerror = function (evt) {
-      console.error("openDb:", evt.target.errorCode);
-    };
-
-    req.onupgradeneeded = function (evt) {
-      console.log("openDb.onupgradeneeded");
-      var store = evt.currentTarget.result.createObjectStore(
-        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
-
-      store.createIndex('biblioid', 'biblioid', { unique: true });
-      store.createIndex('title', 'title', { unique: false });
-      store.createIndex('year', 'year', { unique: false });
-    };
-  }
-
-  /**
-   * @param {string} store_name
-   * @param {string} mode either "readonly" or "readwrite"
-   */
-  function getObjectStore(store_name, mode) {
-    var tx = db.transaction(store_name, mode);
-    return tx.objectStore(store_name);
-  }
-
-  function clearObjectStore(store_name) {
-    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
-    var req = store.clear();
-    req.onsuccess = function(evt) {
-      displayActionSuccess("Store cleared");
-      displayPubList(store);
-    };
-    req.onerror = function (evt) {
-      console.error("clearObjectStore:", evt.target.errorCode);
-      displayActionFailure(this.error);
-    };
-  }
-
-  function getBlob(key, store, success_callback) {
-    var req = store.get(key);
-    req.onsuccess = function(evt) {
-      var value = evt.target.result;
-      if (value)
-        success_callback(value.blob);
-    };
-  }
-
-  /**
-   * @param {IDBObjectStore=} store
-   */
-  function displayPubList(store) {
-    console.log("displayPubList");
-
-    if (typeof store == 'undefined')
-      store = getObjectStore(DB_STORE_NAME, 'readonly');
-
-    var pub_msg = $('#pub-msg');
-    pub_msg.empty();
-    var pub_list = $('#pub-list');
-    pub_list.empty();
-    // Reseting the iframe so that it doesn't display previous content
-    newViewerFrame();
-
-    var req;
-    req = store.count();
-    // Requests are executed in the order in which they were made against the
-    // transaction, and their results are returned in the same order.
-    // Thus the count text below will be displayed before the actual pub list
-    // (not that it is algorithmically important in this case).
-    req.onsuccess = function(evt) {
-      pub_msg.append('<p>There are <strong>' + evt.target.result +
-                     '</strong> record(s) in the object store.</p>');
-    };
-    req.onerror = function(evt) {
-      console.error("add error", this.error);
-      displayActionFailure(this.error);
-    };
-
-    var i = 0;
-    req = store.openCursor();
-    req.onsuccess = function(evt) {
-      var cursor = evt.target.result;
-
-      // If the cursor is pointing at something, ask for the data
-      if (cursor) {
-        console.log("displayPubList cursor:", cursor);
-        req = store.get(cursor.key);
-        req.onsuccess = function (evt) {
-          var value = evt.target.result;
-          var list_item = $('<li>' +
-                            '[' + cursor.key + '] ' +
-                            '(biblioid: ' + value.biblioid + ') ' +
-                            value.title +
-                            '</li>');
-          if (value.year != null)
-            list_item.append(' - ' + value.year);
-
-          if (value.hasOwnProperty('blob') &&
-              typeof value.blob != 'undefined') {
-            var link = $('<a href="' + cursor.key + '">File</a>');
-            link.on('click', function() { return false; });
-            link.on('mouseenter', function(evt) {
-                      setInViewer(evt.target.getAttribute('href')); });
-            list_item.append(' / ');
-            list_item.append(link);
-          } else {
-            list_item.append(" / No attached file");
-          }
-          pub_list.append(list_item);
-        };
-
-        // Move on to the next object in store
-        cursor.continue();
-
-        // This counter serves only to create distinct ids
-        i++;
-      } else {
-        console.log("No more entries");
-      }
-    };
-  }
-
-  function newViewerFrame() {
-    var viewer = $('#pub-viewer');
-    viewer.empty();
-    var iframe = $('<iframe />');
-    viewer.append(iframe);
-    return iframe;
-  }
-
-  function setInViewer(key) {
-    console.log("setInViewer:", arguments);
-    key = Number(key);
-    if (key == current_view_pub_key)
-      return;
-
-    current_view_pub_key = key;
-
-    var store = getObjectStore(DB_STORE_NAME, 'readonly');
-    getBlob(key, store, function(blob) {
-      console.log("setInViewer blob:", blob);
-      var iframe = newViewerFrame();
-
-      // It is not possible to set a direct link to the
-      // blob to provide a mean to directly download it.
-      if (blob.type == 'text/html') {
-        var reader = new FileReader();
-        reader.onload = (function(evt) {
-          var html = evt.target.result;
-          iframe.load(function() {
-            $(this).contents().find('html').html(html);
-          });
-        });
-        reader.readAsText(blob);
-      } else if (blob.type.indexOf('image/') == 0) {
-        iframe.load(function() {
-          var img_id = 'image-' + key;
-          var img = $('<img id="' + img_id + '"/>');
-          $(this).contents().find('body').html(img);
-          var obj_url = window.URL.createObjectURL(blob);
-          $(this).contents().find('#' + img_id).attr('src', obj_url);
-          window.URL.revokeObjectURL(obj_url);
-        });
-      } else if (blob.type == 'application/pdf') {
-        $('*').css('cursor', 'wait');
-        var obj_url = window.URL.createObjectURL(blob);
-        iframe.load(function() {
-          $('*').css('cursor', 'auto');
-        });
-        iframe.attr('src', obj_url);
-        window.URL.revokeObjectURL(obj_url);
-      } else {
-        iframe.load(function() {
-          $(this).contents().find('body').html("No view available");
-        });
-      }
-
-    });
-  }
-
-  /**
-   * @param {string} biblioid
-   * @param {string} title
-   * @param {number} year
-   * @param {string} url the URL of the image to download and store in the local
-   *   IndexedDB database. The resource behind this URL is subjected to the
-   *   "Same origin policy", thus for this method to work, the URL must come from
-   *   the same origin than the web site/app this code is deployed on.
-   */
-  function addPublicationFromUrl(biblioid, title, year, url) {
-    console.log("addPublicationFromUrl:", arguments);
-
-    var xhr = new XMLHttpRequest();
-    xhr.open('GET', url, true);
-    // Setting the wanted responseType to "blob"
-    // http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
-    xhr.responseType = 'blob';
-    xhr.onload = function (evt) {
-                           if (xhr.status == 200) {
-                             console.log("Blob retrieved");
-                             var blob = xhr.response;
-                             console.log("Blob:", blob);
-                             addPublication(biblioid, title, year, blob);
-                           } else {
-                             console.error("addPublicationFromUrl error:",
-                                           xhr.responseText, xhr.status);
-                           }
-                         };
-    xhr.send();
-
-    // We can't use jQuery here because as of jQuery 1.8.3 the new "blob"
-    // responseType is not handled.
-    // http://bugs.jquery.com/ticket/11461
-    // http://bugs.jquery.com/ticket/7248
-    // $.ajax({
-    //   url: url,
-    //   type: 'GET',
-    //   xhrFields: { responseType: 'blob' },
-    //   success: function(data, textStatus, jqXHR) {
-    //     console.log("Blob retrieved");
-    //     console.log("Blob:", data);
-    //     // addPublication(biblioid, title, year, data);
-    //   },
-    //   error: function(jqXHR, textStatus, errorThrown) {
-    //     console.error(errorThrown);
-    //     displayActionFailure("Error during blob retrieval");
-    //   }
-    // });
-  }
-
-  /**
-   * @param {string} biblioid
-   * @param {string} title
-   * @param {number} year
-   * @param {Blob=} blob
-   */
-  function addPublication(biblioid, title, year, blob) {
-    console.log("addPublication arguments:", arguments);
-    var obj = { biblioid: biblioid, title: title, year: year };
-    if (typeof blob != 'undefined')
-      obj.blob = blob;
-
-    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
-    var req;
-    try {
-      req = store.add(obj);
-    } catch (e) {
-      if (e.name == 'DataCloneError')
-        displayActionFailure("This engine doesn't know how to clone a Blob, " +
-                             "use Firefox");
-      throw e;
-    }
-    req.onsuccess = function (evt) {
-      console.log("Insertion in DB successful");
-      displayActionSuccess();
-      displayPubList(store);
-    };
-    req.onerror = function() {
-      console.error("addPublication error", this.error);
-      displayActionFailure(this.error);
-    };
-  }
-
-  /**
-   * @param {string} biblioid
-   */
-  function deletePublicationFromBib(biblioid) {
-    console.log("deletePublication:", arguments);
-    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
-    var req = store.index('biblioid');
-    req.get(biblioid).onsuccess = function(evt) {
-      if (typeof evt.target.result == 'undefined') {
-        displayActionFailure("No matching record found");
-        return;
-      }
-      deletePublication(evt.target.result.id, store);
-    };
-    req.onerror = function (evt) {
-      console.error("deletePublicationFromBib:", evt.target.errorCode);
-    };
-  }
-
-  /**
-   * @param {number} key
-   * @param {IDBObjectStore=} store
-   */
-  function deletePublication(key, store) {
-    console.log("deletePublication:", arguments);
-
-    if (typeof store == 'undefined')
-      store = getObjectStore(DB_STORE_NAME, 'readwrite');
-
-    // As per spec http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
-    // the result of the Object Store Deletion Operation algorithm is
-    // undefined, so it's not possible to know if some records were actually
-    // deleted by looking at the request result.
-    var req = store.get(key);
-    req.onsuccess = function(evt) {
-      var record = evt.target.result;
-      console.log("record:", record);
-      if (typeof record == 'undefined') {
-        displayActionFailure("No matching record found");
-        return;
-      }
-      // Warning: The exact same key used for creation needs to be passed for
-      // the deletion. If the key was a Number for creation, then it needs to
-      // be a Number for deletion.
-      req = store.delete(key);
-      req.onsuccess = function(evt) {
-        console.log("evt:", evt);
-        console.log("evt.target:", evt.target);
-        console.log("evt.target.result:", evt.target.result);
-        console.log("delete successful");
-        displayActionSuccess("Deletion successful");
-        displayPubList(store);
-      };
-      req.onerror = function (evt) {
-        console.error("deletePublication:", evt.target.errorCode);
-      };
-    };
-    req.onerror = function (evt) {
-      console.error("deletePublication:", evt.target.errorCode);
-      };
-  }
-
-  function displayActionSuccess(msg) {
-    msg = typeof msg != 'undefined' ? "Success: " + msg : "Success";
-    $('#msg').html('<span class="action-success">' + msg + '</span>');
-  }
-  function displayActionFailure(msg) {
-    msg = typeof msg != 'undefined' ? "Failure: " + msg : "Failure";
-    $('#msg').html('<span class="action-failure">' + msg + '</span>');
-  }
-  function resetActionStatus() {
-    console.log("resetActionStatus ...");
-    $('#msg').empty();
-    console.log("resetActionStatus DONE");
-  }
-
-  function addEventListeners() {
-    console.log("addEventListeners");
-
-    $('#register-form-reset').click(function(evt) {
-      resetActionStatus();
-    });
-
-    $('#add-button').click(function(evt) {
-      console.log("add ...");
-      var title = $('#pub-title').val();
-      var biblioid = $('#pub-biblioid').val();
-      if (!title || !biblioid) {
-        displayActionFailure("Required field(s) missing");
-        return;
-      }
-      var year = $('#pub-year').val();
-      if (year != '') {
-        // Better use Number.isInteger if the engine has EcmaScript 6
-        if (isNaN(year))  {
-          displayActionFailure("Invalid year");
-          return;
-        }
-        year = Number(year);
-      } else {
-        year = null;
-      }
-
-      var file_input = $('#pub-file');
-      var selected_file = file_input.get(0).files[0];
-      console.log("selected_file:", selected_file);
-      // Keeping a reference on how to reset the file input in the UI once we
-      // have its value, but instead of doing that we rather use a "reset" type
-      // input in the HTML form.
-      //file_input.val(null);
-      var file_url = $('#pub-file-url').val();
-      if (selected_file) {
-        addPublication(biblioid, title, year, selected_file);
-      } else if (file_url) {
-        addPublicationFromUrl(biblioid, title, year, file_url);
-      } else {
-        addPublication(biblioid, title, year);
-      }
-
-    });
-
-    $('#delete-button').click(function(evt) {
-      console.log("delete ...");
-      var biblioid = $('#pub-biblioid-to-delete').val();
-      var key = $('#key-to-delete').val();
-
-      if (biblioid != '') {
-        deletePublicationFromBib(biblioid);
-      } else if (key != '') {
-        // Better use Number.isInteger if the engine has EcmaScript 6
-        if (key == '' || isNaN(key))  {
-          displayActionFailure("Invalid key");
-          return;
-        }
-        key = Number(key);
-        deletePublication(key);
-      }
-    });
-
-    $('#clear-store-button').click(function(evt) {
-      clearObjectStore();
-    });
-
-    var search_button = $('#search-list-button');
-    search_button.click(function(evt) {
-      displayPubList();
-    });
-
-  }
-
-  openDb();
-  addEventListeners();
-
-})(); // Immediately-Invoked Function Expression (IIFE)
-
- -

{{ LiveSampleLink('Full_IndexedDB_example', "Test the online live demo") }}

- -

Nächster Schritt

- -

If you want to start tinkering with the API, jump in to the reference documentation and checking out the different methods.

- -

Siehe auch

- -

Reference

- - - -

Tutorials

- - - -

Related articles

- - - -

Firefox

- - diff --git a/files/de/web/api/indexeddb_api/using_indexeddb/index.html b/files/de/web/api/indexeddb_api/using_indexeddb/index.html new file mode 100644 index 0000000000..3d6d61cf3f --- /dev/null +++ b/files/de/web/api/indexeddb_api/using_indexeddb/index.html @@ -0,0 +1,1180 @@ +--- +title: Verwendung von IndexedDB +slug: Web/API/IndexedDB_API/IndexedDB_verwenden +translation_of: Web/API/IndexedDB_API/Using_IndexedDB +--- +

Mit IndexedDB lassen sich Daten innerhalb des Browsers eines Benutzers permanent abzulegen. Es können so Webanwendungen mit funktionsreichen Abfragemöglichkeiten in Anwendungen erstellt werden, die sowohl online als auch offline funktionieren können, da keine Netzwerkfunktionalitäten benötigt werden.

+ +

Über dieses Dokument

+ +

Dieses Tutorial bespricht die Verwendung der asynchronen API von IndexedDB. Wenn Sie nicht mit IndexedDB vertraut sind, sollten Sie zuerst den Artikel Grundkonzepte lesen.

+ +

Eine Referenzdokumentation zur IndexedDB-API finden Sie im Artikel IndexedDB und dessen Unterseiten, welche die Typen und Objekten dokumentieren, die von IndexedDB verwendet werden, ebenso wie die Methoden von sowohl synchronen als auch asynchronen APIs.

+ +

Grundschema

+ +

Das von IndexedDB unterstützte Grundschema sieht folgendermaßen aus:

+ +
    +
  1. Öffne eine Datenbank und starte eine Transaktion.
  2. +
  3. Erzeuge einen Objektspeicher.
  4. +
  5. Fordere die Ausführung von Datenbankoperationen an, wie das Hinzufügen und Auslesen von Daten.
  6. +
  7. Warte auf die richtige Art von DOM-Ereignis, das auftritt, wenn die Operation beendet ist.
  8. +
  9. Verarbeite die Ergebnisse? (, welche im Anforderungsobjekt gefunden werden können).
  10. +
+ +

Mit dem Wissen über diese Grundkonzepte können wir uns nun konkreteren Dingen zuwenden.

+ +

Erzeugung und Strukturierung des Speichers

+ +

Weil sich die Spezifizierung noch in der Entwicklung befindet, verstecken sich aktuelle Implementierungen von IndexedDB unter Browserpräfixen. Bis sich die Spezifizierung verfestigt, könne Browserhersteller unterschiedliche Implementierungen der Standard-IndexedDB-API haben. Aber sobald Konsens auf dem Standard herrscht, implementieren die Hersteller ihn ohne Markierung durch Präfixe. Tatsächlich ist in manchen Implementierungen der Präfix entfernt: Internet Explorer 10, Firefox 16, Chrome 24. Wenn auf Gecko basierende Browser einen Präfix verwenden, dann verwenden sie den Präfix moz, während auf WebKit basierende Browser den Präfix webkit verwenden.

+ +

Verwendung einer experimentellen Version von IndexedDB

+ +

Für den Fall, dass Sie Ihren Code in Browsern verwenden wollen, die noch Präfixe verwenden, können Sie folgenden Code benutzen:

+ +
// In der folgenden Zeile sollten Sie die Präfixe einfügen, die Sie testen wollen.
+window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+// Verwenden Sie "var indexedDB = ..." NICHT außerhalb einer Funktion.
+// Ferner benötigen Sie evtl. Referenzen zu einigen window.IDB* Objekten:
+window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
+window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
+// (Mozilla hat diese Objekte nie mit Präfixen versehen, also brauchen wir kein window.mozIDB*)
+ +

Beachten Sie, dass Implementierungen, die Präfixe verwenden, fehlerhaft oder unvollständig sein können oder einer alten Version der Spezifizierung folgen können. Deshalb ist es nicht empfohlen, sie im Produktivsystem zu verwenden. In manchen Fällen kann es sinnvoll sein, lieber einen Browser nicht zu unterstützen als zu behaupten, er würde unterstützt, und dann Fehler einzubüßen:

+ +
if (!window.indexedDB) {
+    window.alert("Ihr Browser unterstützt keine stabile Version von IndexedDB. Dieses und jenes Feature wird Ihnen nicht zur Verfügung stehen.");
+}
+
+ +

Öffnen einer Datenbank

+ +

Wir starten den ganzen Prozess folgendermaßen:

+ +
// Öffnen unserer Datenbank
+var request = window.indexedDB.open("MeineTestdatenbank", 3);
+
+ +

Sie sehen, das Öffnen einer Datenbank funktioniert wie jede andere Operation – Sie müssen sie „anfordern“.

+ +

Die Anforderung zum Öffnen öffnet nicht sofort die Datenbank und startet auch die Transaktion nicht gleich. Der Aufruf zur open() Funktion gibt ein IDBOpenDBRequest-Objekt mit Ergebniswert (Erfolg) oder Fehlerwert zurück, die Sie als Ereignis verarbeiten. Die meisten anderen asynchronen Funktionen in IndexedDB machen das gleiche – sie geben ein IDBRequest-Objekt mit Ergebnis oder Fehler zurück. Das Ergebnis für die open-Funktion ist eine Instanz einer IDBDatabase.

+ +

Der zweite Parameter der open-Methode ist die Version der Datenbank. Die Version der Datenbank bestimmt das Datenbankschema – den Objektspeichern und ihrer Strukturen. Wenn die angeforderte Version nicht existiert (weil die Datenbank neu ist, oder weil die Version aktualisiert wurde), wird das Ereignis onupgradeneeded ausgelöst, und es lässt sich eine neue Version der Datenbank im Handler für dieses Ereignis erzeugen. Mehr dazu später im Abschnitt Aktualisieren der Version der Datenbank.

+ +

Erzeugen von Handlern

+ +

Das erste, was Sie mit fast allen Anforderungen machen wollen, die Sie erzeugen, ist das Hinzufügen von Handlern für Erfolge und Fehler:

+ +
request.onerror = function(event) {
+  // Machen Sie etwas mit request.errorCode!
+};
+request.onsuccess = function(event) {
+  // Machen Sie etwas mit request.result!
+};
+ +

Welche der beiden Funktionen, onsuccess() oder onerror(), wird aufgerufen? Wenn alles fehlerfrei ablief, wird ein Erfolgsereignis (d.h. ein DOM-Ereignis, dessen type Eigenschaft auf "success" gesetzt ist) mit request als target ausgelöst. Sobald es ausgelöst wurde, wird die Funktion onsuccess() auf request ausgelöst mit dem Erfolgsereignis als Argument. Wenn nicht alles fehlerfrei ablief, wird ein Fehlerereignis (d.h. ein DOM-Ereignis, dessen type Eigenschaft auf "error" gesetzt ist) auf request ausgelöst. Dies löst die Funktion onerror() aus mit dem Fehlerereignis als Argument.

+ +

Die IndexedDB-API ist so entworfen, dass sie die Notwendigkeit zur Fehlerbehandlung minimiert, also werden Sie wahrscheinlich nicht viele Fehlerereignisse sehen (zumindest nicht nachdem Sie sich an die API gewöhnt haben!). Beim Öffnen von Datenbanken jedoch gibt es ein paar typische Zustände, die Fehlerereignisse erzeugen. Das wahrscheinlichste Problem ist, dass der Benutzer festgelegt hat, den Webapps das Erzeugen von Datenbanken nicht zu erlauben. Eines der Hauptentwurfsziele von IndexedDB ist es, das Speichern von großen Datenmengen zur Offline-Verwendung zu erlauben. (Um mehr darüber zu erfahren, wieviel Speicher jedem Browser zur Verfügung steht, lesen Sie Storage limits).

+ +

Offensichtlich wollen Browser nicht irgendeinem Werbenetzwerk oder einer böswilligen Website erlauben, Ihren Computer zu verschmutzen, deshalb warnen Browser den Benutzer, wenn eine Webapp zum ersten Mal versucht einen IndexedDB-Speicher zu öffnen. Der Benutzer kann wählen, ob er den Zugriff erlaubt oder verbietet. Außerdem ist IndexedDB komplett deaktiviert im Privaten Modus der Browser (Privater Modus für Firefox und Incognito Modus für Chrome). Der Hauptzweck vom Surfen im Privaten Modus ist es, keine Spuren zu hinterlassen, daher schlägt der Versuch fehl, eine Datenbank in diesem Modus zu öffnen.

+ +

Nun nehmen wir an, dass der Benutzer Ihren Anfragen erlaubt hat, eine Datenbank zu erstellen, und Sie haben ein Erfolgsereignis erhalten, um den Erfolgs-Callback auszulösen; was kommt als nächstes? Die Anfrage wurde hier mit einem Aufruf von indexedDB.open() erzeugt, also ist request.result eine Instanz von IDBDatabase, und Sie wollen diese auf jeden Fall für später speichern. Ihr Code könnte etwa so aussehen:

+ +
var db;
+var request = indexedDB.open("MeineTestdatenbank");
+request.onerror = function(event) {
+  alert("Warum haben Sie meiner Webapp nicht erlaubt IndexedDB zu verwenden?!");
+};
+request.onsuccess = function(event) {
+  db = request.result;
+};
+
+ +

Fehlerbehandlung

+ +

Wie bereits oben erwähnt, werden Error events bei entsprechenden Fehlern ausgelöst und in der Objekthierarachie weiter nach oben gereicht. Solche Fehlerereignisse  werden zunächst in der entsprechende Anfrage ausgelöst, die den Fehler verursacht hat. Anschließend werden sie zur Transaktion weitergereicht und schließlich zum Datenbankobjekt.  Wenn man nicht für jede Anfrage einen Error-Handler schreiben möchte, kann man der Datenbank einen einzigen Error-Handler hinzufügen:

+ +
db.onerror = function(event) {
+  // Allgemeine Fehlerbehandlung, die für alle Anfragen an die Datenbank gilt.
+  alert("Datenbankfehler: " + event.target.errorCode);
+};
+
+ +

Einer der häufigsten Fehler, die beim Öffnen der Datenbank auftreten, ist  VER_ERR.  Er zeigt an, dass die Versionsnummer der lokal gespeicherten Datenbank  größer  als die Versionsnummer ist,  die man zu öffnen versucht. Ein solcher Fehler muss immer durch eine Fehlerbehandlung berücksichtigt werden.

+ +

Erstellen oder Updaten der Datenbank

+ +

Wenn eine neue Version der Datenbank erstellt wird, wird das onupgradeneeded Event ausgelöst. In der Handler-Funktion dieses Events musst du für die Erstellung der Datenbankspeicher, welche für diese Version benötigt wird, sorgen:

+ +
// Dieses Event ist lediglich in modernen Browsern verfügbar
+request.onupgradeneeded = function(event) {
+  var db = event.target.result;
+
+  // Erstelle ein ObjectStore für diese Datenbank
+  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
+};
+ +

The Versionsnummer der Datenbank ist vom Typ unsigned long long, so dass sie eine sehr große Ganzzahl sein kann.

+ +
+

Das Bedeutet auch, dass sie nicht vom Typ float sein darf,  ansonsten wird sie zur nächstkleineren Ganzzahl abgerundet, so dass die Transaktion weder starten kann noch ein  upgradeneeded ausgelöst wird. Beispielsweise sollte man nicht 2.4 als Versionsnummer verwenden:

+ +
var request = indexedDB.open("MeineTestdatenbank", 2.4); // don't do this, as the version will be rounded to 2
+
+ +

Wenn man die Versionsnummer der Datenbank erhöht, wird ein  onupgradeneeded -Ereignis ausgelöst. In diesem Fall übernimmt die neue Datenbank automatisch die Objectstores von der Vorgängerversion der Datenbank, so dass man diese nicht erneut erzeugen muss. Lediglich neue Objectsores müssen angelegt oder nicht mehr gebrauchte der Vorgängerversion gelöscht werden. Wenn man einen bereits existierenden Objectstore ändern will (beispielsweise den keyPath veränden), ist es allerdings notwendig, den alten Objectstore zu löschen und einen neuen anzulegen. (Beachten Sie, dass dies die im Objectstore gespeicherten Informationen löscht, so dass man sie vorher auslesen und an anderer Stelle sichern sollte, bevor man ein Datenbankupgrade durchführt.)

+ +

WebKit unterstützt die aktuelle Version dieser Spezifikation, wie sie in Chrome 23+ ausgeliefert wird, ebenso Opera 17+ und IE10+. Andere und ältere Implementierungen unterstützen nicht die aktuelle Version dieser Spezifikation und stellen damit auch noch nicht die Möglichkeit bereit, auf die Methodensignatur indexedDB.open(name, version).onupgradeneeded zugreifen zu könenn. Um ältere Versionen einer Datenbank auf neuer Versionen zu aktualisieren, siehe IDBDatabase reference article.

+ +

Strukturierung der Datenbank

+ +

Im Folgenden wird gezeigt, wie man eine die Daten  strukturiert. IndexedDB verwendet Objectstores anstatt Tabellen, wobei eine einzelne Datenbank viele verschiedene Objectsores enthalten kann. Jeglicher Wert im Objectstore ist einem bestimmten Schlüssel (key) zugeordnet. Es werden dabei unterschiedliche Arten von Schlüsseln verarbeitet, abhängig davon, ob der Objectstore  einen key path oder einen key generator benutzt.

+ +

Die folgende Tabelle gibt eine Übersicht über die bereitgestellten Arten von Schlüssseln.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Key PathKey GeneratorBeschreibung
NeinNeinDieser Objectstore kann Daten beliebigen Typs speichern, inklusive primitiven Typen wie Zahlen und Zeichenketten (Strings). Beim Hinzufügen von Werten muss ein separater Schlüssel angegeben werden.
JaNeinDieser Objectstore kann nur JavaScript-Objekte speichern. Die Objekte müssen ein Attribut haben, dass wie der key path benannt ist.
NeinJaDieser Objectstore kann Daten beliebigen Typs speichern. Beim Hinzufügen von Werten kann ein separater Schlüssel angegeben werden. Fehlt der Schlüssel, so wird er generiert.
JaJaDieser Objectstore kann nur JavaScript-Objekte speichern. In der Regel wird der Schlüssel automatisch generiert und als gleichnamiges Attribut im Objekt gespeichert. Falls es jedoch bereits ein Attribut mit dem Namen gibt, so wird der Schlüssel nicht generiert, sondern der Wert des Attributes wird verwendet.
+ +

You can also create indices on any object store, provided the object store holds objects, not primitives. An index lets you look up the values stored in an object store using the value of a property of the stored object, rather than the object's key.

+ +

Additionally, indexes have the ability to enforce simple constraints on the stored data. By setting the unique flag when creating the index, the index ensures that no two objects are stored with both having the same value for the index's key path. So, for example, if you have an object store which holds a set of people, and you want to ensure that no two people have the same email address, you can use an index with the unique flag set to enforce this.

+ +

That may sound confusing, but this simple example should illustrate the concepts:

+ +
// This is what our customer data looks like.
+const customerData = [
+  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
+  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
+];
+const dbName = "the_name";
+
+var request = indexedDB.open(dbName, 2);
+
+request.onerror = function(event) {
+  // Handle errors.
+};
+request.onupgradeneeded = function(event) {
+  var db = event.target.result;
+
+  // Create an objectStore to hold information about our customers. We're
+  // going to use "ssn" as our key path because it's guaranteed to be
+  // unique.
+  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
+
+  // Create an index to search customers by name. We may have duplicates
+  // so we can't use a unique index.
+  objectStore.createIndex("name", "name", { unique: false });
+
+  // Create an index to search customers by email. We want to ensure that
+  // no two customers have the same email, so use a unique index.
+  objectStore.createIndex("email", "email", { unique: true });
+
+  // Store values in the newly created objectStore.
+  for (var i in customerData) {
+    objectStore.add(customerData[i]);
+  }
+};
+
+ +

As mentioned previously, onupgradeneeded is the only place where you can alter the structure of the database. In it, you can create and delete object stores and build and remove indices.

+ +
Object stores are created with a single call to createObjectStore(). The method takes a name of the store, and a parameter object. Even though the parameter object is optional, it is very important, because it lets you define important optional properties and refine the type of object store you want to create. In our case, we've asked for an object store named "customers" and defined a keyPath that is the property that makes an individual object in the store unique. That property in this example is "ssn" since a social security number is guaranteed to be unique. "ssn" must be present on every object that is stored in the objectStore.
+ +

We've also asked for an index named "name" that looks at the name property of the stored objects. As with createObjectStore(), createIndex() takes an optional options object that refines the type of index that you want to create. Adding objects that don't have a name property still succeeds, but the object won't appear in the "name" index.

+ +

We can now retrieve the stored customer objects using their ssn from the object store directly, or using their name by using the index. To learn how this is done, see the section on using an index.

+ +

Hinzufügen und löschen von Daten

+ +

Before you can do anything with your new database, you need to start a transaction. Transactions come from the database object, and you have to specify which object stores you want the transaction to span. Also, you need to decide if you're going to make changes to the database or if you just need to read from it. Although transactions have three modes (read-only, read/write, and versionchange), you're better off using a read-only transaction when you can, because they can run concurrently

+ +

Daten zur Datenbank hinzufügen

+ +

If you've just created a database, then you probably want to write to it. Here's what that looks like:

+ +
var transaction = db.transaction(["customers"], "readwrite");
+// Note: Older experimental implementations use the deprecated constant IDBTransaction.READ_WRITE instead of "readwrite".
+// In case you want to support such an implementation, you can just write:
+// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);
+ +

The transaction() function takes two arguments (though one is optional) and returns a transaction object. The first argument is a list of object stores that the transaction will span. You can pass an empty array if you want the transaction to span all object stores, but don't do it because the spec says an empty array should generate an InvalidAccessError. If you don't specify anything for the second argument, you get a read-only transaction. Since you want to write to it here you need to pass the "readwrite" flag.

+ +

Now that you have a transaction you need to understand its lifetime. Transactions are tied very closely to the event loop. If you make a transaction and return to the event loop without using it then the transaction will become inactive. The only way to keep the transaction active is to make a request on it. When the request is finished you'll get a DOM event and, assuming that the request succeeded, you'll have another opportunity to extend the transaction during that callback. If you return to the event loop without extending the transaction then it will become inactive, and so on. As long as there are pending requests the transaction remains active. Transaction lifetimes are really very simple but it might take a little time to get used to. A few more examples will help, too. If you start seeing TRANSACTION_INACTIVE_ERR error codes then you've messed something up.

+ +

Transactions can receive DOM events of three different types: error, abort, and complete. We've talked about the way that error events bubble, so a transaction receives error events from any requests that are generated from it. A more subtle point here is that the default behavior of an error is to abort the transaction in which it occurred. Unless you handle the error by calling preventDefault() on the error event, the entire transaction is rolled back. This design forces you to think about and handle errors, but you can always add a catchall error handler to the database if fine grained error handling is too cumbersome. If you don't handle an error event or if you call abort() on the transaction, then the transaction is rolled back and an abort event is fired on the transaction. Otherwise, after all pending requests have completed, you'll get a complete event. If you're doing lots of database operations, then tracking the transaction rather than individual requests can certainly aide your sanity.

+ +

Now that you have a transaction, you'll need to get the object store from it. Transactions only let you have an object store that you specified when creating the transaction. Then you can add all the data you need.

+ +
// Do something when all the data is added to the database.
+transaction.oncomplete = function(event) {
+  alert("All done!");
+};
+
+transaction.onerror = function(event) {
+  // Don't forget to handle errors!
+};
+
+var objectStore = transaction.objectStore("customers");
+for (var i in customerData) {
+  var request = objectStore.add(customerData[i]);
+  request.onsuccess = function(event) {
+    // event.target.result == customerData[i].ssn;
+  };
+}
+ +

The result of a request generated from a call to add() is the key of the value that was added. So in this case, it should equal the ssn property of the object that was added, since the object store uses the ssn property for the key path. Note that the add() function requires that no object already be in the database with the same key. If you're trying to modify an existing entry, or you don't care if one exists already, use the put() function.

+ +

Daten aus der Datenbank löschen

+ +

Löschen von Daten ist recht ähnlich:

+ +
var request = db.transaction(["customers"], "readwrite")
+                .objectStore("customers")
+                .delete("444-44-4444");
+request.onsuccess = function(event) {
+  // It's gone!
+};
+ +

Daten aus der Datenbank auslesen

+ +

Now that the database has some info in it, you can retrieve it in several ways. First, the simple get(). You need to provide the key to retrieve the value, like so:

+ +
var transaction = db.transaction(["customers"]);
+var objectStore = transaction.objectStore("customers");
+var request = objectStore.get("444-44-4444");
+request.onerror = function(event) {
+  // Handle errors!
+};
+request.onsuccess = function(event) {
+  // Do something with the request.result!
+  alert("Name for SSN 444-44-4444 is " + request.result.name);
+};
+ +

That's a lot of code for a "simple" retrieval. Here's how you can shorten it up a bit, assuming that you handle errors at the database level:

+ +
db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
+  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
+};
+ +

See how this works? Since there's only one object store, you can avoid passing a list of object stores you need in your transaction and just pass the name as a string. Also, you're only reading from the database, so you don't need a "readwrite" transaction. Calling transaction() with no mode specified gives you a "readonly" transaction. Another subtlety here is that you don't actually save the request object to a variable. Since the DOM event has the request as its target you can use the event to get to the result property. Easy, right?!

+ +

Benutzung eines Cursors

+ +

Using get() requires that you know which key you want to retrieve. If you want to step through all the values in your object store, then you can use a cursor. Here's what it looks like:

+ +
var objectStore = db.transaction("customers").objectStore("customers");
+
+objectStore.openCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
+    cursor.continue();
+  }
+  else {
+    alert("No more entries!");
+  }
+};
+ +

The openCursor() function takes several arguments. First, you can limit the range of items that are retrieved by using a key range object that we'll get to in a minute. Second, you can specify the direction that you want to iterate. In the above example, we're iterating over all objects in ascending order. The success callback for cursors is a little special. The cursor object itself is the result of the request (above we're using the shorthand, so it's event.target.result). Then the actual key and value can be found on the key and value properties of the cursor object. If you want to keep going, then you have to call continue() on the cursor. When you've reached the end of the data (or if there were no entries that matched your openCursor() request) you still get a success callback, but the result property is undefined.

+ +

One common pattern with cursors is to retrieve all objects in an object store and add them to an array, like this:

+ +
var customers = [];
+
+objectStore.openCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    customers.push(cursor.value);
+    cursor.continue();
+  }
+  else {
+    alert("Got all customers: " + customers);
+  }
+};
+ +
Warnung: Die folgende Funktion ist nicht Teil des IndexedDB Standards.
+ +

Mozilla has also implemented getAll() to handle this case. It isn't part of the IndexedDB standard, so it may disappear in the future. We've included it because we think it's useful. The following code does precisely the same thing as above:

+ +
objectStore.getAll().onsuccess = function(event) {
+  alert("Got all customers: " + event.target.result);
+};
+ +

There is a performance cost associated with looking at the value property of a cursor, because the object is created lazily. When you use getAll(), Gecko must create all the objects at once. If you're just interested in looking at each of the keys, for instance, it is much more efficient to use a cursor than to use getAll(). If you're trying to get an array of all the objects in an object store, though, use getAll().

+ +

Benutzung eines Index

+ +

Storing customer data using the SSN as a key is logical since the SSN uniquely identifies an individual. (Whether this is a good idea for privacy is a different question, outside the scope of this article.) If you need to look up a customer by name, however, you'll need to iterate over every SSN in the database until you find the right one. Searching in this fashion would be very slow, so instead you can use an index.

+ +
var index = objectStore.index("name");
+index.get("Donna").onsuccess = function(event) {
+  alert("Donna's SSN is " + event.target.result.ssn);
+};
+ +

The "name" cursor isn't unique, so there could be more than one entry with the name set to "Donna". In that case you always get the one with the lowest key value.

+ +

If you need to access all the entries with a given name you can use a cursor. You can open two different types of cursors on indexes. A normal cursor maps the index property to the object in the object store. A key cursor maps the index property to the key used to store the object in the object store. The differences are illustrated here:

+ +
index.openCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // cursor.key is a name, like "Bill", and cursor.value is the whole object.
+    alert("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
+    cursor.continue();
+  }
+};
+
+index.openKeyCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // cursor.key is a name, like "Bill", and cursor.value is the SSN.
+    // No way to directly get the rest of the stored object.
+    alert("Name: " + cursor.key + ", SSN: " + cursor.value);
+    cursor.continue();
+  }
+};
+ +

Specifying the range and direction of cursors

+ +

If you would like to limit the range of values you see in a cursor, you can use a key range object and pass it as the first argument to openCursor() or openKeyCursor(). You can make a key range that only allows a single key, or one the has a lower or upper bound, or one that has both a lower and upper bound. The bound may be "closed" (i.e., the key range includes the given value) or "open" (i.e., the key range does not include the given value). Here's how it works:

+ +
// Only match "Donna"
+var singleKeyRange = IDBKeyRange.only("Donna");
+
+// Match anything past "Bill", including "Bill"
+var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
+
+// Match anything past "Bill", but don't include "Bill"
+var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
+
+// Match anything up to, but not including, "Donna"
+var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
+
+//Match anything between "Bill" and "Donna", but not including "Donna"
+var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
+
+index.openCursor(boundKeyRange).onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // Do something with the matches.
+    cursor.continue();
+  }
+};
+ +

Sometimes you may want to iterate in descending order rather than in ascending order (the default direction for all cursors). Switching direction is accomplished by passing prev to the openCursor() function:

+ +
objectStore.openCursor(null, IDBCursor.prev).onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // Do something with the entries.
+    cursor.continue();
+  }
+};
+ +

Since the "name" index isn't unique, there might be multiple entries where name is the same. Note that such a situation cannot occur with object stores since the key must always be unique. If you wish to filter out duplicates during cursor iteration over indexes, you can pass nextunique (or prevunique if you're going backwards) as the direction parameter. When nextunique or prevunique is used, the entry with the lowest key is always the one returned.

+ +
index.openKeyCursor(null, IDBCursor.nextunique).onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // Do something with the entries.
+    cursor.continue();
+  }
+};
+ +

Versionsänderung während eine Webapplikation in einem anderen Tab geöffnet ist

+ +

When your web app changes in such a way that a version change is required for your database, you need to consider what happens if the user has the old version of your app open in one tab and then loads the new version of your app in another. When you call open() with a greater version than the actual version of the database, all other open databases must explicitly acknowledge the request before you can start making changes to the database. Here's how it works:

+ +
var openReq = mozIndexedDB.open("MyTestDatabase", 2);
+
+openReq.onblocked = function(event) {
+  // If some other tab is loaded with the database, then it needs to be closed
+  // before we can proceed.
+  alert("Please close all other tabs with this site open!");
+};
+
+openReq.onupgradeneeded = function(event) {
+  // All other databases have been closed. Set everything up.
+  db.createObjectStore(/* ... */);
+  useDatabase(db);
+}
+
+openReq.onsuccess = function(event) {
+  var db = event.target.result;
+  useDatabase(db);
+  return;
+}
+
+function useDatabase(db) {
+  // Make sure to add a handler to be notified if another page requests a version
+  // change. We must close the database. This allows the other page to upgrade the database.
+  // If you don't do this then the upgrade won't happen until the user close the tab.
+  db.onversionchange = function(event) {
+    db.close();
+    alert("A new version of this page is ready. Please reload!");
+  };
+
+  // Do stuff with the database.
+}
+
+ +

Sicherheit

+ +

IndexedDB uses the same-origin principle, which means that it ties the store to the origin of the site that creates it (typically, this is the site domain or subdomain), so it cannot be accessed by any other origin.

+ +

It's important to note that IndexedDB doesn't work for content loaded into a frame from another site (either {{ HTMLElement("frame") }} or {{ HTMLElement("iframe") }}. This is a security measure. Details as to why this matters are forthcoming. See {{ bug(595307) }}.

+ +

Warnung über die Schließung des Browsers

+ +

When the browser shuts down (e.g., when the user selects Exit or clicks the Close button), any pending IndexedDB transactions are (silently) aborted -- they will not complete, and they will not trigger the error handler. Since the user can exit the browser at any time, this means that you cannot rely upon any particular transaction to complete or to know that it did not complete. There are several implications of this behavior.

+ +

First, you should take care to always leave your database in a consistent state at the end of every transaction. For example, suppose that you are using IndexedDB to store a list of items that you allow the user to edit. You save the list after the edit by clearing the object store and then writing out the new list. If you clear the object store in one transaction and write the new list in another transaction, there is a danger that the browser will close after the clear but before the write, leaving you with an empty database. To avoid this, you should combine the clear and the write into a single transaction.

+ +

Second, you should never tie database transactions to unload events. If the unload event is triggered by the browser closing, any transactions created in the unload event handler will never complete. An intuitive approach to maintaining some information across browser sessions is to read it from the database when the browser (or a particular page) is opened, update it as the user interacts with the browser, and then save it to the database when the browser (or page) closes. However, this will not work. The database transactions will be created in the unload event handler, but because they are asynchronous they will be aborted before they can execute.

+ +

In fact, there is no way to guarantee that IndexedDB transactions will complete, even with normal browser shutdown. See {{ bug(870645) }}.

+ +

Vollständiges IndexedDB Beispiel

+ +

HTML Content

+ +
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
+
+    <h1>IndexedDB Demo: storing blobs, e-publication example</h1>
+    <div class="note">
+      <p>
+        Works and tested with:
+      </p>
+      <div id="compat">
+      </div>
+    </div>
+
+    <div id="msg">
+    </div>
+
+    <form id="register-form">
+      <table>
+        <tbody>
+          <tr>
+            <td>
+              <label for="pub-title" class="required">
+                Title:
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-title" name="pub-title" />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="pub-biblioid" class="required">
+                Bibliographic ID:<br/>
+                <span class="note">(ISBN, ISSN, etc.)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-biblioid" name="pub-biblioid"/>
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="pub-year">
+                Year:
+              </label>
+            </td>
+            <td>
+              <input type="number" id="pub-year" name="pub-year" />
+            </td>
+          </tr>
+        </tbody>
+        <tbody>
+          <tr>
+            <td>
+              <label for="pub-file">
+                File image:
+              </label>
+            </td>
+            <td>
+              <input type="file" id="pub-file"/>
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="pub-file-url">
+                Online-file image URL:<br/>
+                <span class="note">(same origin URL)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-file-url" name="pub-file-url"/>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+
+      <div class="button-pane">
+        <input type="button" id="add-button" value="Add Publication" />
+        <input type="reset" id="register-form-reset"/>
+      </div>
+    </form>
+
+    <form id="delete-form">
+      <table>
+        <tbody>
+          <tr>
+            <td>
+              <label for="pub-biblioid-to-delete">
+                Bibliographic ID:<br/>
+                <span class="note">(ISBN, ISSN, etc.)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-biblioid-to-delete"
+                     name="pub-biblioid-to-delete" />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="key-to-delete">
+                Key:<br/>
+                <span class="note">(for example 1, 2, 3, etc.)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="key-to-delete"
+                     name="key-to-delete" />
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      <div class="button-pane">
+        <input type="button" id="delete-button" value="Delete Publication" />
+        <input type="button" id="clear-store-button"
+               value="Clear the whole store" class="destructive" />
+      </div>
+    </form>
+
+    <form id="search-form">
+      <div class="button-pane">
+        <input type="button" id="search-list-button"
+               value="List database content" />
+      </div>
+    </form>
+
+    <div>
+      <div id="pub-msg">
+      </div>
+      <div id="pub-viewer">
+      </div>
+      <ul id="pub-list">
+      </ul>
+    </div>
+
+ +

CSS Content

+ +
body {
+  font-size: 0.8em;
+  font-family: Sans-Serif;
+}
+
+form {
+  background-color: #cccccc;
+  border-radius: 0.3em;
+  display: inline-block;
+  margin-bottom: 0.5em;
+  padding: 1em;
+}
+
+table {
+  border-collapse: collapse;
+}
+
+input {
+  padding: 0.3em;
+  border-color: #cccccc;
+  border-radius: 0.3em;
+}
+
+.required:after {
+  content: "*";
+  color: red;
+}
+
+.button-pane {
+  margin-top: 1em;
+}
+
+#pub-viewer {
+  float: right;
+  width: 48%;
+  height: 20em;
+  border: solid #d092ff 0.1em;
+}
+#pub-viewer iframe {
+  width: 100%;
+  height: 100%;
+}
+
+#pub-list {
+  width: 46%;
+  background-color: #eeeeee;
+  border-radius: 0.3em;
+}
+#pub-list li {
+  padding-top: 0.5em;
+  padding-bottom: 0.5em;
+  padding-right: 0.5em;
+}
+
+#msg {
+  margin-bottom: 1em;
+}
+
+.action-success {
+  padding: 0.5em;
+  color: #00d21e;
+  background-color: #eeeeee;
+  border-radius: 0.2em;
+}
+
+.action-failure {
+  padding: 0.5em;
+  color: #ff1408;
+  background-color: #eeeeee;
+  border-radius: 0.2em;
+}
+
+.note {
+  font-size: smaller;
+}
+
+.destructive {
+  background-color: orange;
+}
+.destructive:hover {
+  background-color: #ff8000;
+}
+.destructive:active {
+  background-color: red;
+}
+
+ +

 

+ +

JavaScript Content

+ +
(function () {
+  var COMPAT_ENVS = [
+    ['Firefox', ">= 16.0"],
+    ['Google Chrome',
+     ">= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support"]
+  ];
+  var compat = $('#compat');
+  compat.empty();
+  compat.append('<ul id="compat-list"></ul>');
+  COMPAT_ENVS.forEach(function(val, idx, array) {
+    $('#compat-list').append('<li>' + val[0] + ': ' + val[1] + '</li>');
+  });
+
+  const DB_NAME = 'mdn-demo-indexeddb-epublications';
+  const DB_VERSION = 1; // Use a long long for this value (don't use a float)
+  const DB_STORE_NAME = 'publications';
+
+  var db;
+
+  // Used to keep track of which view is displayed to avoid to uselessly reload it
+  var current_view_pub_key;
+
+  function openDb() {
+    console.log("openDb ...");
+    var req = indexedDB.open(DB_NAME, DB_VERSION);
+    req.onsuccess = function (evt) {
+      // Better use "this" than "req" to get the result to avoid problems with
+      // garbage collection.
+      // db = req.result;
+      db = this.result;
+      console.log("openDb DONE");
+    };
+    req.onerror = function (evt) {
+      console.error("openDb:", evt.target.errorCode);
+    };
+
+    req.onupgradeneeded = function (evt) {
+      console.log("openDb.onupgradeneeded");
+      var store = evt.currentTarget.result.createObjectStore(
+        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
+
+      store.createIndex('biblioid', 'biblioid', { unique: true });
+      store.createIndex('title', 'title', { unique: false });
+      store.createIndex('year', 'year', { unique: false });
+    };
+  }
+
+  /**
+   * @param {string} store_name
+   * @param {string} mode either "readonly" or "readwrite"
+   */
+  function getObjectStore(store_name, mode) {
+    var tx = db.transaction(store_name, mode);
+    return tx.objectStore(store_name);
+  }
+
+  function clearObjectStore(store_name) {
+    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+    var req = store.clear();
+    req.onsuccess = function(evt) {
+      displayActionSuccess("Store cleared");
+      displayPubList(store);
+    };
+    req.onerror = function (evt) {
+      console.error("clearObjectStore:", evt.target.errorCode);
+      displayActionFailure(this.error);
+    };
+  }
+
+  function getBlob(key, store, success_callback) {
+    var req = store.get(key);
+    req.onsuccess = function(evt) {
+      var value = evt.target.result;
+      if (value)
+        success_callback(value.blob);
+    };
+  }
+
+  /**
+   * @param {IDBObjectStore=} store
+   */
+  function displayPubList(store) {
+    console.log("displayPubList");
+
+    if (typeof store == 'undefined')
+      store = getObjectStore(DB_STORE_NAME, 'readonly');
+
+    var pub_msg = $('#pub-msg');
+    pub_msg.empty();
+    var pub_list = $('#pub-list');
+    pub_list.empty();
+    // Reseting the iframe so that it doesn't display previous content
+    newViewerFrame();
+
+    var req;
+    req = store.count();
+    // Requests are executed in the order in which they were made against the
+    // transaction, and their results are returned in the same order.
+    // Thus the count text below will be displayed before the actual pub list
+    // (not that it is algorithmically important in this case).
+    req.onsuccess = function(evt) {
+      pub_msg.append('<p>There are <strong>' + evt.target.result +
+                     '</strong> record(s) in the object store.</p>');
+    };
+    req.onerror = function(evt) {
+      console.error("add error", this.error);
+      displayActionFailure(this.error);
+    };
+
+    var i = 0;
+    req = store.openCursor();
+    req.onsuccess = function(evt) {
+      var cursor = evt.target.result;
+
+      // If the cursor is pointing at something, ask for the data
+      if (cursor) {
+        console.log("displayPubList cursor:", cursor);
+        req = store.get(cursor.key);
+        req.onsuccess = function (evt) {
+          var value = evt.target.result;
+          var list_item = $('<li>' +
+                            '[' + cursor.key + '] ' +
+                            '(biblioid: ' + value.biblioid + ') ' +
+                            value.title +
+                            '</li>');
+          if (value.year != null)
+            list_item.append(' - ' + value.year);
+
+          if (value.hasOwnProperty('blob') &&
+              typeof value.blob != 'undefined') {
+            var link = $('<a href="' + cursor.key + '">File</a>');
+            link.on('click', function() { return false; });
+            link.on('mouseenter', function(evt) {
+                      setInViewer(evt.target.getAttribute('href')); });
+            list_item.append(' / ');
+            list_item.append(link);
+          } else {
+            list_item.append(" / No attached file");
+          }
+          pub_list.append(list_item);
+        };
+
+        // Move on to the next object in store
+        cursor.continue();
+
+        // This counter serves only to create distinct ids
+        i++;
+      } else {
+        console.log("No more entries");
+      }
+    };
+  }
+
+  function newViewerFrame() {
+    var viewer = $('#pub-viewer');
+    viewer.empty();
+    var iframe = $('<iframe />');
+    viewer.append(iframe);
+    return iframe;
+  }
+
+  function setInViewer(key) {
+    console.log("setInViewer:", arguments);
+    key = Number(key);
+    if (key == current_view_pub_key)
+      return;
+
+    current_view_pub_key = key;
+
+    var store = getObjectStore(DB_STORE_NAME, 'readonly');
+    getBlob(key, store, function(blob) {
+      console.log("setInViewer blob:", blob);
+      var iframe = newViewerFrame();
+
+      // It is not possible to set a direct link to the
+      // blob to provide a mean to directly download it.
+      if (blob.type == 'text/html') {
+        var reader = new FileReader();
+        reader.onload = (function(evt) {
+          var html = evt.target.result;
+          iframe.load(function() {
+            $(this).contents().find('html').html(html);
+          });
+        });
+        reader.readAsText(blob);
+      } else if (blob.type.indexOf('image/') == 0) {
+        iframe.load(function() {
+          var img_id = 'image-' + key;
+          var img = $('<img id="' + img_id + '"/>');
+          $(this).contents().find('body').html(img);
+          var obj_url = window.URL.createObjectURL(blob);
+          $(this).contents().find('#' + img_id).attr('src', obj_url);
+          window.URL.revokeObjectURL(obj_url);
+        });
+      } else if (blob.type == 'application/pdf') {
+        $('*').css('cursor', 'wait');
+        var obj_url = window.URL.createObjectURL(blob);
+        iframe.load(function() {
+          $('*').css('cursor', 'auto');
+        });
+        iframe.attr('src', obj_url);
+        window.URL.revokeObjectURL(obj_url);
+      } else {
+        iframe.load(function() {
+          $(this).contents().find('body').html("No view available");
+        });
+      }
+
+    });
+  }
+
+  /**
+   * @param {string} biblioid
+   * @param {string} title
+   * @param {number} year
+   * @param {string} url the URL of the image to download and store in the local
+   *   IndexedDB database. The resource behind this URL is subjected to the
+   *   "Same origin policy", thus for this method to work, the URL must come from
+   *   the same origin than the web site/app this code is deployed on.
+   */
+  function addPublicationFromUrl(biblioid, title, year, url) {
+    console.log("addPublicationFromUrl:", arguments);
+
+    var xhr = new XMLHttpRequest();
+    xhr.open('GET', url, true);
+    // Setting the wanted responseType to "blob"
+    // http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
+    xhr.responseType = 'blob';
+    xhr.onload = function (evt) {
+                           if (xhr.status == 200) {
+                             console.log("Blob retrieved");
+                             var blob = xhr.response;
+                             console.log("Blob:", blob);
+                             addPublication(biblioid, title, year, blob);
+                           } else {
+                             console.error("addPublicationFromUrl error:",
+                                           xhr.responseText, xhr.status);
+                           }
+                         };
+    xhr.send();
+
+    // We can't use jQuery here because as of jQuery 1.8.3 the new "blob"
+    // responseType is not handled.
+    // http://bugs.jquery.com/ticket/11461
+    // http://bugs.jquery.com/ticket/7248
+    // $.ajax({
+    //   url: url,
+    //   type: 'GET',
+    //   xhrFields: { responseType: 'blob' },
+    //   success: function(data, textStatus, jqXHR) {
+    //     console.log("Blob retrieved");
+    //     console.log("Blob:", data);
+    //     // addPublication(biblioid, title, year, data);
+    //   },
+    //   error: function(jqXHR, textStatus, errorThrown) {
+    //     console.error(errorThrown);
+    //     displayActionFailure("Error during blob retrieval");
+    //   }
+    // });
+  }
+
+  /**
+   * @param {string} biblioid
+   * @param {string} title
+   * @param {number} year
+   * @param {Blob=} blob
+   */
+  function addPublication(biblioid, title, year, blob) {
+    console.log("addPublication arguments:", arguments);
+    var obj = { biblioid: biblioid, title: title, year: year };
+    if (typeof blob != 'undefined')
+      obj.blob = blob;
+
+    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+    var req;
+    try {
+      req = store.add(obj);
+    } catch (e) {
+      if (e.name == 'DataCloneError')
+        displayActionFailure("This engine doesn't know how to clone a Blob, " +
+                             "use Firefox");
+      throw e;
+    }
+    req.onsuccess = function (evt) {
+      console.log("Insertion in DB successful");
+      displayActionSuccess();
+      displayPubList(store);
+    };
+    req.onerror = function() {
+      console.error("addPublication error", this.error);
+      displayActionFailure(this.error);
+    };
+  }
+
+  /**
+   * @param {string} biblioid
+   */
+  function deletePublicationFromBib(biblioid) {
+    console.log("deletePublication:", arguments);
+    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+    var req = store.index('biblioid');
+    req.get(biblioid).onsuccess = function(evt) {
+      if (typeof evt.target.result == 'undefined') {
+        displayActionFailure("No matching record found");
+        return;
+      }
+      deletePublication(evt.target.result.id, store);
+    };
+    req.onerror = function (evt) {
+      console.error("deletePublicationFromBib:", evt.target.errorCode);
+    };
+  }
+
+  /**
+   * @param {number} key
+   * @param {IDBObjectStore=} store
+   */
+  function deletePublication(key, store) {
+    console.log("deletePublication:", arguments);
+
+    if (typeof store == 'undefined')
+      store = getObjectStore(DB_STORE_NAME, 'readwrite');
+
+    // As per spec http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
+    // the result of the Object Store Deletion Operation algorithm is
+    // undefined, so it's not possible to know if some records were actually
+    // deleted by looking at the request result.
+    var req = store.get(key);
+    req.onsuccess = function(evt) {
+      var record = evt.target.result;
+      console.log("record:", record);
+      if (typeof record == 'undefined') {
+        displayActionFailure("No matching record found");
+        return;
+      }
+      // Warning: The exact same key used for creation needs to be passed for
+      // the deletion. If the key was a Number for creation, then it needs to
+      // be a Number for deletion.
+      req = store.delete(key);
+      req.onsuccess = function(evt) {
+        console.log("evt:", evt);
+        console.log("evt.target:", evt.target);
+        console.log("evt.target.result:", evt.target.result);
+        console.log("delete successful");
+        displayActionSuccess("Deletion successful");
+        displayPubList(store);
+      };
+      req.onerror = function (evt) {
+        console.error("deletePublication:", evt.target.errorCode);
+      };
+    };
+    req.onerror = function (evt) {
+      console.error("deletePublication:", evt.target.errorCode);
+      };
+  }
+
+  function displayActionSuccess(msg) {
+    msg = typeof msg != 'undefined' ? "Success: " + msg : "Success";
+    $('#msg').html('<span class="action-success">' + msg + '</span>');
+  }
+  function displayActionFailure(msg) {
+    msg = typeof msg != 'undefined' ? "Failure: " + msg : "Failure";
+    $('#msg').html('<span class="action-failure">' + msg + '</span>');
+  }
+  function resetActionStatus() {
+    console.log("resetActionStatus ...");
+    $('#msg').empty();
+    console.log("resetActionStatus DONE");
+  }
+
+  function addEventListeners() {
+    console.log("addEventListeners");
+
+    $('#register-form-reset').click(function(evt) {
+      resetActionStatus();
+    });
+
+    $('#add-button').click(function(evt) {
+      console.log("add ...");
+      var title = $('#pub-title').val();
+      var biblioid = $('#pub-biblioid').val();
+      if (!title || !biblioid) {
+        displayActionFailure("Required field(s) missing");
+        return;
+      }
+      var year = $('#pub-year').val();
+      if (year != '') {
+        // Better use Number.isInteger if the engine has EcmaScript 6
+        if (isNaN(year))  {
+          displayActionFailure("Invalid year");
+          return;
+        }
+        year = Number(year);
+      } else {
+        year = null;
+      }
+
+      var file_input = $('#pub-file');
+      var selected_file = file_input.get(0).files[0];
+      console.log("selected_file:", selected_file);
+      // Keeping a reference on how to reset the file input in the UI once we
+      // have its value, but instead of doing that we rather use a "reset" type
+      // input in the HTML form.
+      //file_input.val(null);
+      var file_url = $('#pub-file-url').val();
+      if (selected_file) {
+        addPublication(biblioid, title, year, selected_file);
+      } else if (file_url) {
+        addPublicationFromUrl(biblioid, title, year, file_url);
+      } else {
+        addPublication(biblioid, title, year);
+      }
+
+    });
+
+    $('#delete-button').click(function(evt) {
+      console.log("delete ...");
+      var biblioid = $('#pub-biblioid-to-delete').val();
+      var key = $('#key-to-delete').val();
+
+      if (biblioid != '') {
+        deletePublicationFromBib(biblioid);
+      } else if (key != '') {
+        // Better use Number.isInteger if the engine has EcmaScript 6
+        if (key == '' || isNaN(key))  {
+          displayActionFailure("Invalid key");
+          return;
+        }
+        key = Number(key);
+        deletePublication(key);
+      }
+    });
+
+    $('#clear-store-button').click(function(evt) {
+      clearObjectStore();
+    });
+
+    var search_button = $('#search-list-button');
+    search_button.click(function(evt) {
+      displayPubList();
+    });
+
+  }
+
+  openDb();
+  addEventListeners();
+
+})(); // Immediately-Invoked Function Expression (IIFE)
+
+ +

{{ LiveSampleLink('Full_IndexedDB_example', "Test the online live demo") }}

+ +

Nächster Schritt

+ +

If you want to start tinkering with the API, jump in to the reference documentation and checking out the different methods.

+ +

Siehe auch

+ +

Reference

+ + + +

Tutorials

+ + + +

Related articles

+ + + +

Firefox

+ + diff --git a/files/de/web/api/navigator/registerprotocolhandler/web-based_protocol_handlers/index.html b/files/de/web/api/navigator/registerprotocolhandler/web-based_protocol_handlers/index.html new file mode 100644 index 0000000000..5f48e63097 --- /dev/null +++ b/files/de/web/api/navigator/registerprotocolhandler/web-based_protocol_handlers/index.html @@ -0,0 +1,127 @@ +--- +title: Webbasierte Protokoll-Handler +slug: Web/API/Navigator/registerProtocolHandler/Webbasierte_protokoll-handler +translation_of: Web/API/Navigator/registerProtocolHandler/Web-based_protocol_handlers +--- +
{{Fx_minversion_header(3)}}
+ +

Hintergrund

+ +

Man findet im Web häufiger Seiten, die Verweise mit anderen Protokollen als HTTP einsetzen. Ein Beispiel ist das mailto: Protokoll:

+ +
+
<a href="mailto:webmaster@example.com">Web Master</a>
+
+ +

Seitenersteller können mailto: Verweise nutzen, wenn sie einen bequemen Weg anbieten möchten, E-Mails direkt aus ihrer Webseite heraus zu versenden. Wird der Link aktiviert, sollte der Browser das im Betriebssystem eingestelle Standardprogramm für E-Mail starten. Das ist ein Beispiel für einen desktopbasierten Protokoll-Handler.

+ +

Webbasierte Protokoll-Handler erlauben webbasierten Anwendungen ebenfall an solchen Prozessen teilzunehmen. Das wird mit der Migration vieler Anwendungen ins Web zunehmend wichtiger. Tatsächlich gibt es bereits viele Webanwendungen, die einen mailto: Verweis verarbeiten könnten.

+ +

Registrierung

+ +

Das Einrichten einer webbasierten Anwendung als Protokoll-Handler ist unkompliziert. Die Webapp nutzt registerProtocolHandler(), um sich selbst beim Browser als potentiellen Handler für ein bestimmtes Protokoll zu registrieren. Ein Beispiel:

+ +
navigator.registerProtocolHandler("burger",
+                                  "http://www.google.co.uk/?uri=%s",
+                                  "Burger handler");
+ +

Die Parameter sind:

+ + + +

Führt ein Browser diesen Code aus, wird er über einen Dialog die Erlaubnis des Nutzers einholen, die Webanwendung als Handler für dieses Protokoll zu registrieren. Firefox zeigt eine Nachricht in der Notification Bar:

+ +

+ +
Hinweis: Das bei der Registrierung angegebene URL Template muss mit der die Anfrage stellenden Webseite übereinstimmen, oder die Registrierung schlägt fehl. Beispielsweise kann http://example.com/homepage.html einen Protokoll-Handler für http://example.com/handle_mailto/%s registrieren, aber nicht für http://example.org/handle_mailto/%s.
+ +

Wird derselbe Handler mehrfach registriert, meldet der Browser das durch ein weiteres Pop-Up, das auf die bereits erfolgte Registrierung hinweist. Daher ist es sinnvoll, die Registrierung mit einem vorangehenden Check abzusichern.

+ +

Beispiel

+ +
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html lang="en">
+<head>
+  <title>Web Protocol Handler Sample - Register</title>
+  <script type="text/javascript">
+    navigator.registerProtocolHandler("burger",
+                                  "http://www.google.co.uk/?uri=%s",
+                                  "Burger handler");
+  </script>
+</head>
+<body>
+  <h1>Web Protocol Handler Sample</h1>
+  <p>This web page will install a web protocol handler for the <code>fake:</code> protocol.</p>
+</body>
+</html>
+
+ +

Aktivierung

+ +

Ab jetzt wird der Browser bei jedem Klick auf einen Verweis, der das neu registrierte Protokoll verwendet, die Ausführung auf die registrierte URL umleiten. Firefox wird zuvor standardmäßig die Bestätigung durch den Nutzer einholen.

+ +

Beispiel

+ +
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html lang="en">
+<head>
+  <title>Web Protocol Handler Sample - Test</title>
+</head>
+<body>
+  <p>Hey have you seen <a href="burger:cheeseburger">this</a> before?</p>
+</body>
+</html>
+
+ +

Handling

+ +

Der nächste Schritt ist das tatsächliche Handling. Der Browser kombiniert die href aus dem aktivierten Link mit dem registrierten URL Template und führt dann damit einen HTTP GET Request aus. Hervorgehend aus den vorangegangenen Beispielen würde der Request auf folgender URL stattfinden:

+ +
http://www.google.co.uk/?uri=burger:cheeseburger
+
+ +

Serverseitiger Code kann dann die query String Parameter extrahieren und die gewünschte Aktion ausführen.

+ +
Hinweis: Dem serverseitigen Code wird der gesamte Inhalt der href übergeben. D.h. der Server muss das Protokoll aus den Daten parsen.
+ +

Beispiel

+ +
<?php
+$value = "";
+if ( isset ( $_GET["value"] ) ) {
+  $value = $_GET["value"];
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html lang="en">
+<head>
+    <title>Web Protocol Handler Sample</title>
+</head>
+<body>
+  <h1>Web Protocol Handler Sample - Handler</h1>
+  <p>This web page is called when handling a <code>burger:</code> protocol action. The data sent:</p>
+  <textarea>
+<?php echo(htmlspecialchars($value, ENT_QUOTES, 'UTF-8')); ?>
+  </textarea>
+</body>
+</html>
+
+ +

Verweise

+ + + +

Siehe auch

+ + diff --git a/files/de/web/api/navigator/registerprotocolhandler/webbasierte_protokoll-handler/index.html b/files/de/web/api/navigator/registerprotocolhandler/webbasierte_protokoll-handler/index.html deleted file mode 100644 index 5f48e63097..0000000000 --- a/files/de/web/api/navigator/registerprotocolhandler/webbasierte_protokoll-handler/index.html +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Webbasierte Protokoll-Handler -slug: Web/API/Navigator/registerProtocolHandler/Webbasierte_protokoll-handler -translation_of: Web/API/Navigator/registerProtocolHandler/Web-based_protocol_handlers ---- -
{{Fx_minversion_header(3)}}
- -

Hintergrund

- -

Man findet im Web häufiger Seiten, die Verweise mit anderen Protokollen als HTTP einsetzen. Ein Beispiel ist das mailto: Protokoll:

- -
-
<a href="mailto:webmaster@example.com">Web Master</a>
-
- -

Seitenersteller können mailto: Verweise nutzen, wenn sie einen bequemen Weg anbieten möchten, E-Mails direkt aus ihrer Webseite heraus zu versenden. Wird der Link aktiviert, sollte der Browser das im Betriebssystem eingestelle Standardprogramm für E-Mail starten. Das ist ein Beispiel für einen desktopbasierten Protokoll-Handler.

- -

Webbasierte Protokoll-Handler erlauben webbasierten Anwendungen ebenfall an solchen Prozessen teilzunehmen. Das wird mit der Migration vieler Anwendungen ins Web zunehmend wichtiger. Tatsächlich gibt es bereits viele Webanwendungen, die einen mailto: Verweis verarbeiten könnten.

- -

Registrierung

- -

Das Einrichten einer webbasierten Anwendung als Protokoll-Handler ist unkompliziert. Die Webapp nutzt registerProtocolHandler(), um sich selbst beim Browser als potentiellen Handler für ein bestimmtes Protokoll zu registrieren. Ein Beispiel:

- -
navigator.registerProtocolHandler("burger",
-                                  "http://www.google.co.uk/?uri=%s",
-                                  "Burger handler");
- -

Die Parameter sind:

- - - -

Führt ein Browser diesen Code aus, wird er über einen Dialog die Erlaubnis des Nutzers einholen, die Webanwendung als Handler für dieses Protokoll zu registrieren. Firefox zeigt eine Nachricht in der Notification Bar:

- -

- -
Hinweis: Das bei der Registrierung angegebene URL Template muss mit der die Anfrage stellenden Webseite übereinstimmen, oder die Registrierung schlägt fehl. Beispielsweise kann http://example.com/homepage.html einen Protokoll-Handler für http://example.com/handle_mailto/%s registrieren, aber nicht für http://example.org/handle_mailto/%s.
- -

Wird derselbe Handler mehrfach registriert, meldet der Browser das durch ein weiteres Pop-Up, das auf die bereits erfolgte Registrierung hinweist. Daher ist es sinnvoll, die Registrierung mit einem vorangehenden Check abzusichern.

- -

Beispiel

- -
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-<html lang="en">
-<head>
-  <title>Web Protocol Handler Sample - Register</title>
-  <script type="text/javascript">
-    navigator.registerProtocolHandler("burger",
-                                  "http://www.google.co.uk/?uri=%s",
-                                  "Burger handler");
-  </script>
-</head>
-<body>
-  <h1>Web Protocol Handler Sample</h1>
-  <p>This web page will install a web protocol handler for the <code>fake:</code> protocol.</p>
-</body>
-</html>
-
- -

Aktivierung

- -

Ab jetzt wird der Browser bei jedem Klick auf einen Verweis, der das neu registrierte Protokoll verwendet, die Ausführung auf die registrierte URL umleiten. Firefox wird zuvor standardmäßig die Bestätigung durch den Nutzer einholen.

- -

Beispiel

- -
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-<html lang="en">
-<head>
-  <title>Web Protocol Handler Sample - Test</title>
-</head>
-<body>
-  <p>Hey have you seen <a href="burger:cheeseburger">this</a> before?</p>
-</body>
-</html>
-
- -

Handling

- -

Der nächste Schritt ist das tatsächliche Handling. Der Browser kombiniert die href aus dem aktivierten Link mit dem registrierten URL Template und führt dann damit einen HTTP GET Request aus. Hervorgehend aus den vorangegangenen Beispielen würde der Request auf folgender URL stattfinden:

- -
http://www.google.co.uk/?uri=burger:cheeseburger
-
- -

Serverseitiger Code kann dann die query String Parameter extrahieren und die gewünschte Aktion ausführen.

- -
Hinweis: Dem serverseitigen Code wird der gesamte Inhalt der href übergeben. D.h. der Server muss das Protokoll aus den Daten parsen.
- -

Beispiel

- -
<?php
-$value = "";
-if ( isset ( $_GET["value"] ) ) {
-  $value = $_GET["value"];
-}
-?>
-
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
-<html lang="en">
-<head>
-    <title>Web Protocol Handler Sample</title>
-</head>
-<body>
-  <h1>Web Protocol Handler Sample - Handler</h1>
-  <p>This web page is called when handling a <code>burger:</code> protocol action. The data sent:</p>
-  <textarea>
-<?php echo(htmlspecialchars($value, ENT_QUOTES, 'UTF-8')); ?>
-  </textarea>
-</body>
-</html>
-
- -

Verweise

- - - -

Siehe auch

- - diff --git a/files/de/web/api/node/innertext/index.html b/files/de/web/api/node/innertext/index.html deleted file mode 100644 index bd1594471a..0000000000 --- a/files/de/web/api/node/innertext/index.html +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Node.innerText -slug: Web/API/Node/innerText -tags: - - API DOM Property Reference -translation_of: Web/API/HTMLElement/innerText ---- -
{{APIRef("DOM")}}
- -

Zusammenfassung

- -

Node.innerText ist eine Eigenschaft die die "gerenderten" Text Inhalte einer node und ihrer nachfahren representiert. Als getter nähert es sich dem Text an, den ein User erhalten würde wenn sie/er den Inhalt des Elementes mit dem Kursor highlighten und dann zum Clipboard kopieren würde. Dieses Feature wurde ursprünglich von Internet Explorer eingeführt, und wurde förmlich in den HTML standards von 2016 spezifiziert nachdem es von allen Hauptbrowsern übernommen wurde.

- -

{{domxref("Node.textContent")}} ist eine etwas ähnliche Alternative. Es gibt allerdings wichtige Unterschiede zwischen den beiden.

- -

Spezifikation

- - - - - - - - - - - - - - -
SpezifikationStatusKommentar
{{SpecName('HTML WHATWG', 'dom.html#the-innertext-idl-attribute', 'innerText')}}{{Spec2('HTML WHATWG')}} -

Eingeführt, basiert auf dem draft of the innerText specification. Siehe whatwg/html#465 und whatwg/compat#5 für die Historie.

-
- -

Browser Kompatibilität

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support4{{ CompatGeckoDesktop(45) }}69.6 (probably earlier)3
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support2.3 (probably earlier){{ CompatGeckoMobile(45) }}10 (probably earlier)124.1 (probably earlier)
-
- -

Siehe auch

- - diff --git a/files/de/web/api/vollbild_api/index.html b/files/de/web/api/vollbild_api/index.html deleted file mode 100644 index 1ddce4c572..0000000000 --- a/files/de/web/api/vollbild_api/index.html +++ /dev/null @@ -1,305 +0,0 @@ ---- -title: Vollbild API -slug: Web/API/Vollbild_API -tags: - - API - - DOM - - JS - - Tutorial - - Vollbild API -translation_of: Web/API/Fullscreen_API ---- -

{{DefaultAPISidebar("Fullscreen API")}}

- -

Die Vollbild-API bietet einen einfachen Weg, um Webinhalte auf dem gesamten Bildschirm des Nutzers anzuzeigen. Der Browser kann einfach angewiesen werden, Elemente und, falls vorhanden, deren Kinder den gesamten Bildschirm einnehmen zu lassen, wodurch jegliche andere Inhalte vorübergehend ausgeblendet werden.

- -
-

Momentan benutzen nicht alle Browser die Version der API ohne Präfixe. Siehe Vendor Präfixe für Unterschiede zwischen Präfixen und Namen. 

- -

Für eine universelle Lösung des Problems siehe Fscreen.

-
- -

Aktivieren des Vollbildmodus

- -

Wenn sie ein Element gefunden haben, welches Sie im Vollbildmodus anzeigen möchten (etwa ein {{ HTMLElement("video") }}), wir der Vollbildmodus einfach durch den Aufruf der {{ domxref("Element.requestFullscreen()") }}-Methode gestartet.

- -

Nehmen wir dieses {{ HTMLElement("video") }}-Element als Beispiel:

- -
<video controls id="myvideo">
-  <source src="somevideo.webm"></source>
-  <source src="somevideo.mp4"></source>
-</video>
-
- -

Wir können dieses Video mit folgendem Skript in den Vollbildmodus versetzen:

- -
var elem = document.getElementById("myvideo");
-if (elem.requestFullscreen) {
-  elem.requestFullscreen();
-}
-
- -

Anzeige-Schwierigkeiten

- -

An dieser Stelle lohnt es sich, einen wichtigen Unterschied zwischen Gecko und WebKit aufzuzeigen: Gecko fügt automatisch CSS-Regeln zum betroffenen Element hinzu, damit es den ganzen Bildschirm einnimmt: "width: 100%; height: 100%". WebKit tut dies allerdings nicht; stattdessen wird das Vollbild-Element in der selben Größe zentriert vor einem schwarzen Hintergrund dargestellt. Um das gleich Vollbild-Verhalten in WebKit zu erhalten, müssen Sie selbst eine "width: 100%; height: 100%;"-CSS-Regel zum entsprechenden Element hinzufügen:

- -
#myvideo:-webkit-full-screen {
-  width: 100%;
-  height: 100%;
-}
-
- -

Andererseits, wenn Sie stattdessen versuchen, das WebKit-Verhalten auf Gecko zu erreichen, müssen sie das Element, das präsentiert werden soll, in einem anderen Element platzieren, welches sie stattdessen in den Vollbild-Modus versetzen. Dann können sie CSS-Regeln verwenden, um das innere Element so zu layouten, wie Sie wünschen.

- -

Benachrichtigung

- -

Wenn der Vollbild-Modus erfolgreich gestartet wird, erhält das document, welches das entsprechende Element enthält ein {{ event("fullscreenchange") }}-Event. Wenn der Vollbild-Modus wieder verlassen wird, erhält das document wiederum ein {{ event("fullscreenchange") }}-Event. Beachten Sie jedoch, dass das {{ event("fullscreenchange") }}-Event selbst keine Informationen darüber bereitstellt, ob das document in oder aus den Vollbild-Modus wechselt. Stattdessen muss überprüft werden, ob das Attribut {{ domxref("document.fullscreenElement", "fullscreenElement") }}, des document nicht null ist. In diesem Fall befindet sich der Browser im Vollbild-Modus.

- -

Wenn die Vollbild-Anfrage scheitert

- -

Es ist nicht garantiert, dass der Wechsel in den Vollbild-Modus möglich ist. {{ HTMLElement("iframe") }}-Elements etwa haben das {{ HTMLAttrXRef("allowfullscreen", "iframe") }}-Attribut, um ihrem Inhalt zu erlauben, in den Vollbild-Modus zu wechseln. Zusätzlich gibt es bestimmte Arten von Inhalten, wie etwa windowed plug-ins, welche nicht im Vollbild-Modus angezeigt werden können. Wenn versucht wird, ein Element, welches nicht im Vollbild-Modus dargestellt werden kann (oder ein Eltern- bzw. Kind-Knoten eines solchen Elements), wird dies nicht funktionieren. Stattdessen wird das Element, welches den Vollbild-Modus angefragt hat, ein mozfullscreenerror-Event erhalten. Wenn eine Vollbild-Anfrage scheitert, wird Firefox eine Fehlermeldung auf der Web-Konsole ausgeben, welche erklärt, warum die Anfrage fehlgeschlagen ist. In Chrome und neueren Versionen von Opera werden allerdings keine solchen Warnungen produziert.

- -
-

Hinweis: Vollbild-Anfragen müssen aus einem Event-Handler heraus gestellt werden oder die Anfrage wird abgelehnt. 

-
- -

Verlassen des Vollbildmodus

- -

Der Nutzer hat immer die Möglichkeit, selbst den Vollbildmodus zu verlassen; siehe {{ anch("Things your users want to know") }}. Der Vollbildmodus kann aber auch programmatisch mithilfe der  {{domxref("Document.exitFullscreen()")}}-Methode beendet werden.

- -

Weitere Informationen

- -

Das {{ domxref("document") }} hält weitere Informationen bereit, welche bei der Entwicklung von Vollbild-Web-Apps hilfreich sein können:

- -
-
{{ domxref("document.fullscreenElement", "fullscreenElement") }}
-
Das fullscreenElement-Attribut gibt das {{ domxref("element") }} an, welches aktuell im Vollbild-Modus angezeigt wird. Wenn dies nicht null ist, befindet sich das document im Vollbildmodus. Sonst, wenn dieses Attribut null ist, befindet sich das Dokument nicht im Vollbildmodus.
-
{{ domxref("document.fullscreenEnabled", "fullscreenEnabled") }}
-
Das fullscreenEnabled-Attribut gibt an, ob das document aktuell in einem Zustand ist, in welchem der Vollbild-Modus erlaubt ist.
-
- -

Was die Nutzer wissen wollen

- -

Sie sollten die Nutzer wissen lassen, dass sie den Vollbildmodus jederzeit mit ESC (oder F11) verlassen können.

- -

Zusätzlich beendet das Navigieren zu einer anderen Seite oder das Wechseln von Tabs oder Programmen (etwa mit  Alt+Tab) auch den Vollbildmodus.

- -

Beispiel

- -

In diesem Beispiel wird ein Video auf einer Webseite dargestellt. Durch drücken von Return oder Enter kann der Nutzer den Vollbildmodus des Videos umschalten.

- -

Live-Beispiel ansehen

- -

Watching for the Enter key

- -

When the page is loaded, this code is run to set up an event listener to watch for the Enter key.

- -
document.addEventListener("keydown", function(e) {
-  if (e.keyCode == 13) {
-    toggleFullScreen();
-  }
-}, false);
-
- -

Toggling fullscreen mode

- -

This code is called when the user hits the Enter key, as seen above.

- -
function toggleFullScreen() {
-  if (!document.fullscreenElement) {
-      document.documentElement.requestFullscreen();
-  } else {
-    if (document.exitFullscreen) {
-      document.exitFullscreen();
-    }
-  }
-}
-
- -

This starts by looking at the value of the fullscreenElement attribute on the {{ domxref("document") }} (checking it prefixed with both moz, ms, or webkit). If it's null, the document is currently in windowed mode, so we need to switch to fullscreen mode. Switching to fullscreen mode is done by calling {{ domxref("element.requestFullscreen()") }}.

- -

If fullscreen mode is already active (fullscreenElement is non-null), we call {{ domxref("document.exitFullscreen()") }}.

- -

Vendor Präfixe

- -

For the moment not all browsers are implementing the unprefixed version of the API (for vendor agnostic access to the Fullscreen API you can use Fscreen). Here is the table summarizing the prefixes and name differences between them:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StandardBlink (Chrome & Opera)Gecko (Firefox)Internet Explorer 11EdgeSafari (WebKit)
{{domxref("Document.fullscreen")}}webkitIsFullScreenmozFullScreen-webkitIsFullScreenwebkitIsFullScreen
{{domxref("Document.fullscreenEnabled")}}webkitFullscreenEnabledmozFullScreenEnabledmsFullscreenEnabledwebkitFullscreenEnabledwebkitFullscreenEnabled
{{domxref("Document.fullscreenElement")}}webkitFullscreenElementmozFullScreenElementmsFullscreenElementwebkitFullscreenElementwebkitFullscreenElement
{{domxref("Document.onfullscreenchange")}}onwebkitfullscreenchangeonmozfullscreenchangeMSFullscreenChangeonwebkitfullscreenchangeonwebkitfullscreenchange
{{domxref("Document.onfullscreenerror")}}onwebkitfullscreenerroronmozfullscreenerrorMSFullscreenErroronwebkitfullscreenerroronwebkitfullscreenerror
{{domxref("Document.exitFullscreen()")}}webkitExitFullscreen()mozCancelFullScreen()msExitFullscreen()webkitExitFullscreen()webkitExitFullscreen()
{{domxref("Element.requestFullscreen()")}}webkitRequestFullscreen()mozRequestFullScreen()msRequestFullscreen()webkitRequestFullscreen()webkitRequestFullscreen()
- -

Spezifikationen

- - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName("Fullscreen")}}{{Spec2("Fullscreen")}}Initial version.
- -

Browser Kompatibilität

- -

All browsers implement this APIs. Nevertheless some implement it with prefixed names with slightly different spelling; e.g., instead of requestFullscreen(), there is MozRequestFullScreen().

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support15 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoDesktop("9.0") }} {{ property_prefix("-moz") }}
- {{CompatGeckoDesktop("47")}} (behind full-screen-api.unprefix.enabled)
11 {{ property_prefix("-ms") }}12.105.0 {{ property_prefix("-webkit") }}
fullscreenEnabled20 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoDesktop("10.0") }} {{ property_prefix("-moz") }}
- {{CompatGeckoDesktop("47")}} (behind full-screen-api.unprefix.enabled)
11 {{ property_prefix("-ms") }}12.105.1 {{ property_prefix("-webkit") }}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureAndroidChromeEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{ CompatUnknown() }}28 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoMobile("9.0") }}{{ property_prefix("-moz") }}
- {{CompatGeckoMobile("47")}} (behind full-screen-api.unprefix.enabled)
{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
fullscreenEnabled{{ CompatUnknown() }}28 {{ property_prefix("-webkit") }}{{CompatVersionUnknown}}{{ CompatGeckoMobile("10.0") }} {{ property_prefix("moz") }}
- {{CompatGeckoMobile("47")}} (behind full-screen-api.unprefix.enabled)
{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
-
- -

Siehe auch

- - diff --git a/files/de/web/api/webgl_api/tutorial/3d-objekte_mit_webgl_erstellen/index.html b/files/de/web/api/webgl_api/tutorial/3d-objekte_mit_webgl_erstellen/index.html deleted file mode 100644 index d3a21591fd..0000000000 --- a/files/de/web/api/webgl_api/tutorial/3d-objekte_mit_webgl_erstellen/index.html +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: 3D-Objekte mit WebGL erstellen -slug: Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen -tags: - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren", "Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden")}}

- -

Bringen wir unser Quadrat in die dritte Dimension, indem wir fünf oder mehr Flächen hinzufügen und daraus einen Würfel machen. Um das effizient zu machen, wechseln wir vom Zeichnen direkt über die Vertices zur gl.drawArray() Methode, um den Vertex-Array als eine Tabelle zu verwenden und die einzelnen Vertices in dieser Tabelle als Referenz für Positionen jeder Fläche zu definieren, indem wir gl.drawElements() aufrufen.

- -

Bedenken Sie: Jede Fläche benötigt vier Vertices, die diese definieren, aber jeder Vertex wird von drei Flächen verwendet. Wir können eine Menge Daten sparen, indem wir eine Liste aller 24 Vertices erstellen und uns dann auf jeden Vertex durch dessen Index in der Liste beziehen, anstatt den gesamten Koordinatensatz zu verwenden.

- -

Die Vertex-Positionen des Würfels definieren

- -

Zunächst wollen wir den Positionsspeicher der Vertices erstellen, indem wir den Code in initBuffers() ändern. Das geschieht genau so wie für das Quadrat, allerdings haben wir hier ein paar Datensätze mehr, da wir 24 Vertices (4 pro Seite) haben müssen:

- -
  var vertices = [
-    // vordere Fläche
-    -1.0, -1.0,  1.0,
-     1.0, -1.0,  1.0,
-     1.0,  1.0,  1.0,
-    -1.0,  1.0,  1.0,
-
-    // hintere Fläche
-    -1.0, -1.0, -1.0,
-    -1.0,  1.0, -1.0,
-     1.0,  1.0, -1.0,
-     1.0, -1.0, -1.0,
-
-    // obere Fläche
-    -1.0,  1.0, -1.0,
-    -1.0,  1.0,  1.0,
-     1.0,  1.0,  1.0,
-     1.0,  1.0, -1.0,
-
-    // untere Fläche
-    -1.0, -1.0, -1.0,
-     1.0, -1.0, -1.0,
-     1.0, -1.0,  1.0,
-    -1.0, -1.0,  1.0,
-
-    // rechte Fläche
-     1.0, -1.0, -1.0,
-     1.0,  1.0, -1.0,
-     1.0,  1.0,  1.0,
-     1.0, -1.0,  1.0,
-
-    // linke Fläche
-    -1.0, -1.0, -1.0,
-    -1.0, -1.0,  1.0,
-    -1.0,  1.0,  1.0,
-    -1.0,  1.0, -1.0
-  ];
-
- -

Die Farben der Vertices definieren

- -

Außerdem müssen wir einen Array für die Farben der 24 Vertices erstellen. Dieser Code definiert zunächst die Farben für jede Fläche und verwendet dann eine Schleife, um jeden der Vertices mit einer Farbe zu bestücken.

- -
  var colors = [
-    [1.0,  1.0,  1.0,  1.0],    // vordere Fläche: weiß
-    [1.0,  0.0,  0.0,  1.0],    // hintere Fläche: rot
-    [0.0,  1.0,  0.0,  1.0],    // obere Fläche: grün
-    [0.0,  0.0,  1.0,  1.0],    // untere Fläche: blau
-    [1.0,  1.0,  0.0,  1.0],    // rechte Fläche: gelb
-    [1.0,  0.0,  1.0,  1.0]     // linke Fläche: violett
-  ];
-
-  var generatedColors = [];
-
-  for (j=0; j<6; j++) {
-    var c = colors[j];
-
-    for (var i=0; i<4; i++) {
-      generatedColors = generatedColors.concat(c);
-    }
-  }
-
-  cubeVerticesColorBuffer = gl.createBuffer();
-  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
-  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(generatedColors), gl.STATIC_DRAW);
-
- -

Das Element-Array definieren

- -

Sobald die Vertex-Arrays generiert worden sind, müssen wir das Element-Array erstellen.

- -
  cubeVerticesIndexBuffer = gl.createBuffer();
-  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
-
-  // Dieser Array definiert jede Fläche als zwei Dreiecke über die Indizes
-  // im Vertex-Array, um die Position jedes Dreiecks festzulegen.
-
-  var cubeVertexIndices = [
-    0,  1,  2,      0,  2,  3,    // vorne
-    4,  5,  6,      4,  6,  7,    // hinten
-    8,  9,  10,     8,  10, 11,   // oben
-    12, 13, 14,     12, 14, 15,   // unten
-    16, 17, 18,     16, 18, 19,   // rechts
-    20, 21, 22,     20, 22, 23    // links
-  ]
-
-  // Sende nun das Element-Array zum GL
-
-  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
-      new WebGLUnsignedShortArray(cubeVertexIndices), gl.STATIC_DRAW);
-
- -

Das cubeVertexIndices Array definiert jede Fläche als ein paar von Dreiecken, alle Vertices des Dreiecks werden als ein Index im Vertex-Array des Würfels festgelegt. Daher ist der Würfel aus einer Sammlung von 12 Dreiecken beschrieben.

- -

Den Würfel zeichnen

- -

Als nächstes müssen wir etwas Code zur drawScene() Funktion hinzufügen, um über den Indexspeicher des Würfels zu zeichnen. Wir fügen neue bindBuffer() und drawElements() Aufrufe hinzu:

- -
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
-  setMatrixUniforms();
-  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
-
- -

Da jede Seite unseres Würfels aus zwei Dreiecken besteht, gibt es 6 Vertices pro Seite, oder 36 Vertices im Würfel, obwohl einige davon doppelt sind. Da unser Index-Array jedoch aus einfachen Integern besteht, stellt dies keinen unkoordinierbaren Betrag an Daten dar, welcher für jeden Frame der Animation durchgegangen werden muss.

- -

Jetzt haben wir einen animierten Würfel, welcher herum springt, rotiert und über sechs unterschiedliche Seiten verfügt. Wenn Ihr Browser WebGL unterstützt, schauen Sie sich hier die Demo in Aktion an.

- -

{{PreviousNext("Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren", "Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden")}}

diff --git a/files/de/web/api/webgl_api/tutorial/adding_2d_content_to_a_webgl_context/index.html b/files/de/web/api/webgl_api/tutorial/adding_2d_content_to_a_webgl_context/index.html new file mode 100644 index 0000000000..56c267f394 --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/adding_2d_content_to_a_webgl_context/index.html @@ -0,0 +1,238 @@ +--- +title: Hinzufügen von 2D Inhalten in einen WebGL-Kontext +slug: Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext +tags: + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Einführung_in_WebGL", "Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen")}}

+ +

Sobald der WebGL-Kontext erfolgreich erstellt wurde, können wir anfangen darin zu rendern. Am einfachsten beginnen wir mit einem einfachen, zweidimensionalen, untextuierten Objekt. Fangen wir also damit an, ein Stück Code zu schreiben, um ein Quadrat zu zeichnen.

+ +

Hinweis zur deutschen Übersetzung dieses Abschnitts

+ +

Die deutsche Übersetzung bezieht sich auf ältere Version des Beispielcodes als der englische Originaltext. Der im deutschen Text beschriebene Code hat einige Probleme:

+ + + +

Um das Beispiel selber nachzuprogrammieren ist es daher empfehlenswert, auf die englische Version des Tutorials zu wechseln. Vielleicht haben Sie ja auch Lust, diese Version ins Deutsche zu übersetzen?

+ +

Beleuchtung der Szene

+ +

Das Wichtigste, das wir verstehen müssen bevor wir anfangen können, ist, dass wir, uns bereits in einer dreidimensionalen Umgebung befinden, obwohl wir nur ein zweidimensionales Objekt in diesem Beispiel rendern wollen. Das heißt, wir müssen jetzt bereits Shader einsetzen, die unsere einfache Szene beleuchten und solche erstellen, die unser Objekt zeichnen. Diese Shader werden festlegen wie unser Quadrat später beleuchtet sein wird.

+ +

Initialisierung der Shader

+ +

Shader sind durch die OpenGL ES Shading Language (pdf) spezifiziert. Damit es einfacher ist unsere Inhalte zu warten und zu aktualisieren, können wir unseren Code so schreiben, dass die Shader im HTML Dokument gefunden werden, anstatt alles mittels JavaScript zu bauen. Werfen wir einen Blick auf unsere initShaders() Routine, welche diese Aufgabe übernimmt:

+ +
function initShaders() {
+  var fragmentShader = getShader(gl, "shader-fs");
+  var vertexShader = getShader(gl, "shader-vs");
+
+  // Erzeuge Shader
+
+  shaderProgram = gl.createProgram();
+  gl.attachShader(shaderProgram, vertexShader);
+  gl.attachShader(shaderProgram, fragmentShader);
+  gl.linkProgram(shaderProgram);
+
+  // Wenn die das Aufrufen der Shader fehlschlägt,
+  // gib eine Fehlermeldung aus:
+
+  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+    alert("Initialisierung des Shaderprogramms nicht möglich.");
+  }
+
+  gl.useProgram(shaderProgram);
+
+  vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
+  gl.enableVertexAttribArray(vertexPositionAttribute);
+}
+
+
+ +

Es werden zwei Shader in dieser Routine geladen; der Erste, der fragmentShader, wird vom script Element mit der Id "shader-fs" geladen. Der Zweite, der vertexShader, wird vom script Element mit der Id "shader-vs" geladen. Wir werden im nächsten Abschnitt noch näher auf die getShader() Funktion eingehen. Diese Routine holt sich dann die Shader aus dem DOM.

+ +

In diesem Teil erstellen wir noch das Shaderprogramm, in dem wir die Funktion createProgram() aufrufen, die beiden Shader anhängen und das Shaderprogramm verlinken. Danach wird der Zustand des LINK_STATUS Parameters überprüft, um sicher zu gehen, dass das Programm erfolgreich verlinkt wurde. Wenn das der Fall ist, aktivieren wir das neue Shaderprogramm.

+ +

Shader aus dem DOM laden

+ +

Die getShader() Routine ruft ein Shaderprogramm mit dem festgelegtem Namen aus dem DOM auf, gibt das kompilierte Shaderprogramm zurück oder ist leer, wenn nichts geladen oder kompiliert worden konnte.

+ +
function getShader(gl, id) {
+  var shaderScript = document.getElementById(id);
+
+  if (!shaderScript) {
+    return null;
+  }
+
+  var theSource = "";
+  var currentChild = shaderScript.firstChild;
+
+  while(currentChild) {
+    if (currentChild.nodeType == 3) {
+      theSource += currentChild.textContent;
+    }
+
+    currentChild = currentChild.nextSibling;
+  }
+ +

Wenn das Element mit der festgelegten Id gefunden wurde, wird der Text in die Variable theSource gespeichert.

+ +
  var shader;
+
+  if (shaderScript.type == "x-shader/x-fragment") {
+    shader = gl.createShader(gl.FRAGMENT_SHADER);
+  } else if (shaderScript.type == "x-shader/x-vertex") {
+    shader = gl.createShader(gl.VERTEX_SHADER);
+  } else {
+    return null;  // Unbekannter Shadertyp
+  }
+ +

Jetzt wo der Code für die Shader gelesen wurde, können wir uns die MIME Typen der Shader angucken, um festzulegen, ob es ein Vertex-Shader (MIME Typ: "x-shader/x-vertex") oder ein Fragment-Shader (MIME Typ: "x-shader/x-fragment") ist. Danach werden dann die entsprechenden Shadertypen erstellt.

+ +
  gl.shaderSource(shader, theSource);
+
+  // Kompiliere das Shaderprogramm
+
+  gl.compileShader(shader);
+
+  // Überprüfe, ob die Kompilierung erfolgreich war
+
+  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+    alert("Es ist ein Fehler beim Kompilieren der Shader aufgetaucht: " + gl.getShaderInfoLog(shader));
+    return null;
+  }
+
+  return shader;
+}
+
+ +

Schließlich wird der Shader kompiliert. Falls ein Fehler während der Kompilierung auftritt, zeigen wir die Fehlermeldung an und geben null zurück. Andernfalls wird der kompilierte Shader zurückgegeben.

+ +

Die Shader

+ +

Nun müssen wir die eigentlichen Shaderprogramme in unser HTML schreiben. Wie genau diese Shader arbeiten, übersteigt das Ziel dieses Tutorials, wir gehen daher nur auf das Wesentliche ein.

+ +

Fragment-Shader

+ +

Jeder Pixel in einem Vieleck wird Fragment in der GL-Fachsprache genannt. Die Aufgabe des Fragment-Shaders ist es, die Farbe für jeden Pixel bereitzustellen. In unserem Fall ordnen wir einfach jedem Pixel eine weiße Farbe zu.

+ +

gl_FragColor ist eine eingebaute GL Variable, die für die Farbe des Fragments verwendet wird.

+ +
<script id="shader-fs" type="x-shader/x-fragment">
+
+  void main(void) {
+    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
+  }
+</script>
+
+ +

Vertex-Shader

+ +

Der Vertex-Shader definiert die Position und Form von jedem Punkt.

+ +
<script id="shader-vs" type="x-shader/x-vertex">
+  attribute vec3 aVertexPosition;
+
+  uniform mat4 uMVMatrix;
+  uniform mat4 uPMatrix;
+
+
+  void main(void) {
+    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+  }
+</script>
+
+ +

Das Objekt erstellen

+ +

Bevor wir unser Quadrat rendern können, müssen wir einen Puffer erstellen, der unsere Punkte enthält. Das werden wir mittels einer Funktion machen, die wir initBuffers() nennen. Wenn wir zu mehr fortgeschrittenen WebGL-Konzepten kommen, wird diese Routine vergrößert, um mehr - und komplexere - 3D-Objekte zu erstellen.

+ +
var horizAspect = 480.0/640.0;
+
+function initBuffers() {
+  squareVerticesBuffer = gl.createBuffer();
+  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
+
+  var vertices = [
+    1.0,  1.0,  0.0,
+    -1.0, 1.0,  0.0,
+    1.0,  -1.0, 0.0,
+    -1.0, -1.0, 0.0
+  ];
+
+  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+}
+
+ +

Diese Routine ist, durch die einfache Art der Szene in diesem Beispiel, sehr einfach gehalten. Es geht los mit dem Aufruf der createBuffer() Methode, die einen Puffer erstellt in dem wir die Punkte speichern können. Der Puffer wird, durch Aufrufen der bindBuffer() Methode, mit dem Kontext verbunden.

+ +

Wenn das erledigt ist, erstellen wir einen JavaScript Array, der die Koordinaten für jeden Punkt des Quadrats enthält. Dieser wird dann in einen WebGL FloatArray umgewandelt und durch die bufferData() Methode werden die Punkte für das Objekt festgelegt.

+ +

Die Szene zeichnen

+ +

Jetzt sind die Shader aufgebaut und das Objekt ist erstellt. Wir können die Szene rendern lassen. Da wir in dieser Szene nichts animieren, ist unsere drawScene() Funktion sehr einfach. Es werden einige nützliche Routinen verwendet, die wir uns kurz anschauen.

+ +
function drawScene() {
+  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+  perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0);
+
+  loadIdentity();
+  mvTranslate([-0.0, 0.0, -6.0]);
+
+  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
+  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
+  setMatrixUniforms();
+  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+}
+
+ +

Als Erstes wird der Kontext auf unsere Hintergrundfarbe gesetzt und die Kameraperspektive festgelegt. Wir definieren ein Blickfeld von 45°, mit einem Höhen-/Breitenverhältnis von 640 zu 480 (die Größe unseres Canvas). Außerdem legen wir fest, dass wir nur Objekte zwischen 0.1 und 100 Einheiten gerendert haben wollen.

+ +

Dann wird die Position des Quadrats, über das Laden der ursprünglichen Position und der Verschiebung um 6 Einheiten von der Kamera weg, ermittelt. Danach, verbinden wir den Puffer des Quadrats mit dem Kontext, konfigurieren es, und zeichnen das Objekt, in dem wir die drawArrays() Methode aufrufen.

+ +

Das Ergebnis kann hier ausprobiert werden, wenn Sie einen Browser verwenden, der WebGL unterstützt.

+ +

Matrix Operationen

+ +

Matrix Operationen sind schon kompliziert genug. Keiner möchte wirklich den ganzen Code selbst schreiben, der benötigt wird um die Berechnungen selber durchzuführen. Glücklicherweise gibt es Sylvester, eine sehr handliche Bibliothek, die bestens mit Vektor und Matrix Operationen in JavaScript umgehen kann.

+ +

Die glUtils.js Datei, die in dieser Demo benutzt wird, wird bei einer ganzen Reihe von WebGL-Demos, die Web zu finden sind, verwendet. Keiner scheint sich völlig sicher zu sein, woher diese Bibliothek ursprünglich herkommt, aber es vereinfacht den Gebrauch von Sylvester noch weiter, in dem Methoden hinzugefügt werden, die auch spezielle Matrizentypen ermöglichen und HTML ausgegeben werden kann, um die Matrizen anzeigen zu lassen.

+ +

Zusätzlich, definiert diese Demo ein paar hilfreiche Routinen, um an diese Bibliothek für spezielle Aufgaben anzukoppeln. Was genau gemacht wird, ist kein Teil dieses Artikels, aber es gibt einige gute Referenzen zu Matrizen, die online verfügbar sind. Siehe unter Siehe auch, um ein paar aufzulisten.

+ +
function loadIdentity() {
+  mvMatrix = Matrix.I(4);
+}
+
+function multMatrix(m) {
+  mvMatrix = mvMatrix.x(m);
+}
+
+function mvTranslate(v) {
+  multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
+}
+
+function setMatrixUniforms() {
+  var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
+  gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten()));
+
+  var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
+  gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten()));
+}
+
+ +

Siehe auch

+ + + +

{{PreviousNext("Web/API/WebGL_API/Tutorial/Einführung_in_WebGL", "Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen")}}

diff --git a/files/de/web/api/webgl_api/tutorial/animating_objects_with_webgl/index.html b/files/de/web/api/webgl_api/tutorial/animating_objects_with_webgl/index.html new file mode 100644 index 0000000000..a9eafed6db --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/animating_objects_with_webgl/index.html @@ -0,0 +1,118 @@ +--- +title: Objekte mit WebGL animieren +slug: Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen", "Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen") }}

+ +

Unser Code vom vorherigen Beispiel ist bereits so konfiguriert, dass die WebGL-Szene alle 15 Millisekunden neu gezeichnet wird. Bis jetzt wird zu jeder Zeit das gleiche Bild immer neu gezeichnet. Das wollen wir nun ändern, sodass sich unser Quadrat auf der Bildfläche bewegt.

+ +

In diesem Beispiel rotieren und verschieben wir unser Quadrat in alle drei Dimensionen, sodass es schon in einem 3D Raum existieren kann (obwohl wir bisher nur ein 2D-Objekt erstellt haben).

+ +

Das Quadrat rotieren lassen

+ +

Fangen wir damit an, das Quadrat im Raum zu rotieren. Als erstes brauchen wir dazu eine Variable in welche wir die Rotation des Quadrats verfolgen können:

+ +
var squareRotation = 0.0;
+
+ +

Jetzt müssen wir die drawScene() Funktion ändern, um die aktuelle Rotation auf das Quadrat anzuwenden, wenn dies gezeichnet werden soll. Nach der Umrechnung der Startposition für das Quadrat, wenden wir eine Rotation wie diese an:

+ +
mvPushMatrix();
+mvRotate(squareRotation, [1, 0, 1]);
+
+ +

Das speichert die aktuelle Model-View Matrix und rotiert die Matrix mit dem aktuellen Wert von squareRotation um die X und Z Achsen.

+ +

Nach dem Zeichen müssen wir die Originalmatrix wiederherstellen:

+ +
mvPopMatrix();
+
+ +

Wir speichern und stellen die Originalmatrix dann wieder her, um zu verhindern, dass die Rotation auf andere Objekte angewendet wird, die wir vielleicht später noch zeichnen wollen.

+ +

Um letztendlich etwas zu animieren, brauchen wir noch ein paar Zeilen Code, welcher den Wert von squareRotation über die Zeit verändert. Dafür erstellen wir eine neue Variable, die die Zeit aufzeichnet, welche wir zuletzt animiert haben (wir nennen diese lastSquareUpdateTime), dann fügen wir den folgenden Code an das Ende der drawScene() Funktion:

+ +
var currentTime = (new Date).getTime();
+if (lastSquareUpdateTime) {
+  var delta = currentTime - lastSquareUpdateTime;
+  squareRotation += (30 * delta) / 1000.0;
+}
+lastSquareUpdateTime = currentTime;
+
+ +

Dieser Code benutzt den Betrag der Zeit, die vergangen ist, seitdem wir zum letzten Mal den Wert von squareRotation geändert haben, um festzustellen wie weit das Quadrat rotiert werden soll.

+ +

Das Quadrat bewegen

+ +

Wir können das Quadrat auch verschieben indem wir eine unterschiedliche Position berechnen lassen, bevor wir es zeichnen. Dieses Beispiel zeigt wie eine grundlegende Animation gemacht werden kann. Allerdings möchten Sie in einer echten Anwendung wohl eher etwas weniger Irrsinniges machen.

+ +

Verfolgen wir die Abstände zu jeder Achse für unsere Verschiebung in drei neuen Variablen:

+ +
var squareXOffset = 0.0;
+var squareYOffset = 0.0;
+var squareZOffset = 0.0;
+
+ +

Und den Betrag, welcher unsere Postion auf jeder Achse verändern soll, in diesen Variablen:

+ +
var xIncValue = 0.2;
+var yIncValue = -0.4;
+var zIncValue = 0.3;
+
+ +

Nun können wir einfach diesen Code zum vorherigen Code, der die Rotation aktualisiert, hinzufügen:

+ +
squareXOffset += xIncValue * ((30 * delta) / 1000.0);
+squareYOffset += yIncValue * ((30 * delta) / 1000.0);
+squareZOffset += zIncValue * ((30 * delta) / 1000.0);
+
+if (Math.abs(squareYOffset) > 2.5) {
+  xIncValue = -xIncValue;
+  yIncValue = -yIncValue;
+  zIncValue = -zIncValue;
+}
+
+ +

Das bringt unser Quadrat dazu seine Größe zu verändern, sich willkürlich auf der Fläche zu verschieben, sich vom Betrachter weg und zum Betrachter hin zu bewegen und das alles während das Quadrat auch noch rotiert. Das sieht dann schon eher wie ein Bildschirmschoner aus.

+ +

Wenn Ihr Browser WebGL unterstützt, ist hier das Beispiel in Aktion.

+ +

Weitere Matrixoperationen

+ +

Dieses Beispiel verwendet einige zusätzliche Matrixoperationen, darunter zwei Routinen, die die Matrizen verschieben, wiederherstellen und in einem Stack aufbewahren und eine Routine, die die Matrix um eine gewissen Anzahl von Grad rotiert:

+ +
var mvMatrixStack = [];
+
+function mvPushMatrix(m) {
+  if (m) {
+    mvMatrixStack.push(m.dup());
+    mvMatrix = m.dup();
+  } else {
+    mvMatrixStack.push(mvMatrix.dup());
+  }
+}
+
+function mvPopMatrix() {
+  if (!mvMatrixStack.length) {
+    throw("Can't pop from an empty matrix stack.");
+  }
+
+  mvMatrix = mvMatrixStack.pop();
+  return mvMatrix;
+}
+
+function mvRotate(angle, v) {
+  var inRadians = angle * Math.PI / 180.0;
+
+  var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4();
+  multMatrix(m);
+}
+
+ +

Diese Routinen wurden von einem Beispiel ausgeliehen, welches von Vlad Vukićević geschrieben wurde.

+ +

{{PreviousNext("Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen", "Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen") }}

diff --git a/files/de/web/api/webgl_api/tutorial/animating_textures_in_webgl/index.html b/files/de/web/api/webgl_api/tutorial/animating_textures_in_webgl/index.html new file mode 100644 index 0000000000..bfeb362b8e --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/animating_textures_in_webgl/index.html @@ -0,0 +1,89 @@ +--- +title: Animierte Texturen in WebGL +slug: Web/API/WebGL_API/Tutorial/Animierte_Texturen_in_WebGL +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL +--- +

{{WebGLSidebar("Tutorial") }} {{Previous("Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

+ +

In dieser Demo bauen wir auf das vorherige Beispiel auf, indem wir die statischen Texturen mit den Frames eines OGG-Video ersetzen. Das ist nicht sehr schwer zu schreiben, sieht aber umso besser aus. Fangen wir an.

+ +

Zugang zum Video

+ +

Zunächst müssen wir etwas HTML hinzufügen, um das video Element zu erstellen, welches wir verwenden, um die Video-Frames zu erhalten:

+ +
<video id="video" src="Firefox.ogv" autobuffer='true'">
+  Ihr Browser scheint das HTML5 <code>&lt;video&gt;</code> Element nicht zu unterstützen.
+</video>
+
+ +

Das erstellt einfach ein Element, um die Video-Datei "Firefox.ogv" abzuspielen. Wir benutzen CSS, um zu verhindern, dass das Video angezeigt wird:

+ +
video {
+  display: none;
+}
+
+ +

Dann schenken wir dem JavaScript Code wieder unsere Aufmerksamkeit und fügen eine neue Zeile zur start() Funktion hinzu, um einen Bezug zum Video-Element herzustellen:

+ +
videoElement = document.getElementById("video");
+
+ +

Und wir ersetzen den Code, der die Intervall-gesteuerten Aufrufe von drawScene() setzt, mit diesem Code:

+ +
videoElement.addEventListener("canplaythrough", startVideo, true);
+videoElement.addEventListener("ended", videoDone, true);
+
+ +

Die Idee ist hier, dass wir die Animation nicht starten wollen, bevor das Video nicht ausreichend zwischengespeichert wurde, sodass es dann ohne Unterbrechung abgespielt werden kann. Wir fügen also einen Event-Listener hinzu, um auf das video Element zu warten bis es uns mitteilt, dass genug zwischengespeichert wurde und das komplette Video ohne Pause abgespielt werden kann. Wir fügen außerdem einen zweiten Event-Listener hinzu, sodass wir die Animation stoppen können, wenn das Video beendet ist und wir so nicht unnötig den Prozessor belasten.

+ +

Die startVideo() Funktion sieht so aus:

+ +
function startVideo() {
+  videoElement.play();
+  videoElement.addEventListener("timeupdate", updateTexture, true);
+  setInterval(drawScene, 15);
+}
+
+ +

Hier wird einfach das Video gestartet und ein Event-Handler bereitgestellt, der aufgerufen wird, wenn ein neuer Frame des Videos verfügbar ist. Dann werden die Intervall-gesteuerten Aufrufe der drawScene() Funktion eingerichtet, um den Würfel zu rendern.

+ +

Die videoDone() Funktion ruft einfach {{ domxref("window.clearInterval") }} auf, um die Intervallaufrufe zu beenden, die die Animation aktualisieren.

+ +

Die Video-Frames als Textur verwenden

+ +

Die nächste Änderung erhält die initTexture() Funktion, welche viel einfacher wird, weil kein Bild mehr geladen werden muss. Stattdessen muss nun das Textur-Mapping aktiviert werden und ein leeres Textur-Objekt zum späteren Gebrauch erstellt werden:

+ +
function initTextures() {
+  gl.enable(gl.TEXTURE_2D);
+  cubeTexture = gl.createTexture();
+}
+
+ +

So sieht die updateTexture() Funktion aus. Hier wird wirklich Arbeit verrichtet:

+ +
function updateTexture() {
+  gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
+  gl.texImage2D(gl.TEXTURE_2D, 0, videoElement, true);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
+  gl.generateMipmap(gl.TEXTURE_2D);
+  gl.bindTexture(gl.TEXTURE_2D, null);
+}
+
+ +

Sie kennen diesen Code bereits. Es ist fast identisch zur handleTextureLoaded() Routine im vorherigen Beispiel, mit der Ausnahme, dass wir texImage2D() nicht mit einem Image Objekt sondern mit dem video Element aufrufen.

+ +

updateTexture() wird jedes Mal aufgerufen, wenn sich das timeupdate Event vom video Element meldet. Dieses Event wird gesendet, wenn sich die Zeit des aktuellen Frames ändert, sodass wir wissen, dass wir unsere Textur nur dann aktualisieren müssen, wenn neue Daten verfügbar sind.

+ +

Das war alles! Wenn Sie eine Browser mit Unterstützung von WebGL verwenden, können Sie sich das Beispiel in Aktion ansehen.

+ +

Siehe auch

+ + + +

{{Previous("Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

diff --git a/files/de/web/api/webgl_api/tutorial/animierte_texturen_in_webgl/index.html b/files/de/web/api/webgl_api/tutorial/animierte_texturen_in_webgl/index.html deleted file mode 100644 index bfeb362b8e..0000000000 --- a/files/de/web/api/webgl_api/tutorial/animierte_texturen_in_webgl/index.html +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: Animierte Texturen in WebGL -slug: Web/API/WebGL_API/Tutorial/Animierte_Texturen_in_WebGL -tags: - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL ---- -

{{WebGLSidebar("Tutorial") }} {{Previous("Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

- -

In dieser Demo bauen wir auf das vorherige Beispiel auf, indem wir die statischen Texturen mit den Frames eines OGG-Video ersetzen. Das ist nicht sehr schwer zu schreiben, sieht aber umso besser aus. Fangen wir an.

- -

Zugang zum Video

- -

Zunächst müssen wir etwas HTML hinzufügen, um das video Element zu erstellen, welches wir verwenden, um die Video-Frames zu erhalten:

- -
<video id="video" src="Firefox.ogv" autobuffer='true'">
-  Ihr Browser scheint das HTML5 <code>&lt;video&gt;</code> Element nicht zu unterstützen.
-</video>
-
- -

Das erstellt einfach ein Element, um die Video-Datei "Firefox.ogv" abzuspielen. Wir benutzen CSS, um zu verhindern, dass das Video angezeigt wird:

- -
video {
-  display: none;
-}
-
- -

Dann schenken wir dem JavaScript Code wieder unsere Aufmerksamkeit und fügen eine neue Zeile zur start() Funktion hinzu, um einen Bezug zum Video-Element herzustellen:

- -
videoElement = document.getElementById("video");
-
- -

Und wir ersetzen den Code, der die Intervall-gesteuerten Aufrufe von drawScene() setzt, mit diesem Code:

- -
videoElement.addEventListener("canplaythrough", startVideo, true);
-videoElement.addEventListener("ended", videoDone, true);
-
- -

Die Idee ist hier, dass wir die Animation nicht starten wollen, bevor das Video nicht ausreichend zwischengespeichert wurde, sodass es dann ohne Unterbrechung abgespielt werden kann. Wir fügen also einen Event-Listener hinzu, um auf das video Element zu warten bis es uns mitteilt, dass genug zwischengespeichert wurde und das komplette Video ohne Pause abgespielt werden kann. Wir fügen außerdem einen zweiten Event-Listener hinzu, sodass wir die Animation stoppen können, wenn das Video beendet ist und wir so nicht unnötig den Prozessor belasten.

- -

Die startVideo() Funktion sieht so aus:

- -
function startVideo() {
-  videoElement.play();
-  videoElement.addEventListener("timeupdate", updateTexture, true);
-  setInterval(drawScene, 15);
-}
-
- -

Hier wird einfach das Video gestartet und ein Event-Handler bereitgestellt, der aufgerufen wird, wenn ein neuer Frame des Videos verfügbar ist. Dann werden die Intervall-gesteuerten Aufrufe der drawScene() Funktion eingerichtet, um den Würfel zu rendern.

- -

Die videoDone() Funktion ruft einfach {{ domxref("window.clearInterval") }} auf, um die Intervallaufrufe zu beenden, die die Animation aktualisieren.

- -

Die Video-Frames als Textur verwenden

- -

Die nächste Änderung erhält die initTexture() Funktion, welche viel einfacher wird, weil kein Bild mehr geladen werden muss. Stattdessen muss nun das Textur-Mapping aktiviert werden und ein leeres Textur-Objekt zum späteren Gebrauch erstellt werden:

- -
function initTextures() {
-  gl.enable(gl.TEXTURE_2D);
-  cubeTexture = gl.createTexture();
-}
-
- -

So sieht die updateTexture() Funktion aus. Hier wird wirklich Arbeit verrichtet:

- -
function updateTexture() {
-  gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
-  gl.texImage2D(gl.TEXTURE_2D, 0, videoElement, true);
-  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
-  gl.generateMipmap(gl.TEXTURE_2D);
-  gl.bindTexture(gl.TEXTURE_2D, null);
-}
-
- -

Sie kennen diesen Code bereits. Es ist fast identisch zur handleTextureLoaded() Routine im vorherigen Beispiel, mit der Ausnahme, dass wir texImage2D() nicht mit einem Image Objekt sondern mit dem video Element aufrufen.

- -

updateTexture() wird jedes Mal aufgerufen, wenn sich das timeupdate Event vom video Element meldet. Dieses Event wird gesendet, wenn sich die Zeit des aktuellen Frames ändert, sodass wir wissen, dass wir unsere Textur nur dann aktualisieren müssen, wenn neue Daten verfügbar sind.

- -

Das war alles! Wenn Sie eine Browser mit Unterstützung von WebGL verwenden, können Sie sich das Beispiel in Aktion ansehen.

- -

Siehe auch

- - - -

{{Previous("Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

diff --git a/files/de/web/api/webgl_api/tutorial/beleuchtung_in_webgl/index.html b/files/de/web/api/webgl_api/tutorial/beleuchtung_in_webgl/index.html deleted file mode 100644 index bd65b5bb40..0000000000 --- a/files/de/web/api/webgl_api/tutorial/beleuchtung_in_webgl/index.html +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Beleuchtung in WebGL -slug: Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL -tags: - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Lighting_in_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden", "Web/API/WebGL_API/Tutorial/Animierte_Texturen_in_WebGL")}}

- -

Als Erstes sollten wir verstehen, dass WebGL nicht wie OpenGL über eine eigene Unterstützung für die Beleuchtung verfügt. Das müssen wir selbst machen. Glücklicherweise ist es nicht sehr schwer und dieser Artikel versucht Ihnen die Grundlagen dazu näher zu bringen.

- -

Beleuchtung und Schattierung simulieren

- -

Obwohl wir nicht weiter ins Detail über die Theorie hinter simulierter Beleuchtung in 3D Grafiken gehen wollen, was außerdem weit über diesen Artikel hinaus gehen würde, ist es gut zu wissen wie es ungefähr funktioniert. Anstatt hier nun in aller Tiefe zu diskutieren, sollten Sie einen Blick auf den »Phong Shading« Artikel auf Wikipedia werfen, welcher einen guten Überblick über das meist genutzte Beleuchtungsmodell liefert.

- -

Es gibt drei grundlegende Typen zur Beleuchtung:

- -

Umgebungslicht ist das Licht, das die Szene umgibt. Es weist in keine Richtung und bestrahlt jede Oberfläche in gleicher Weise, egal in welche Richtung es zeigt.

- -

Gerichtetes Licht ist das Licht, das von einer festgelegten Richtung ausgestrahlt wird. Dieses Licht ist so weit weg, sodass sich jeder Photon parallel zu jedem anderen Photon bewegt. Sonnenlicht ist zum Beispiel gerichtetes (direktionales) Licht.

- -

Punktbeleuchtung ist das Licht, das von einem Punkt ausgestrahlt wird und von dort radial in alle Richtungen verläuft. So funktionieren zum Beispiel die Glühbirnen im Alltag.

- -

Für unsere Zwecke vereinfachen wir das Beleuchtungsmodell, indem wir nur gerichtetes Licht und Umgebungslicht betrachten. Wir haben hier keine Highlights, die wir mit Punktbeleuchtung oder Glanzlicht in dieser Szene hervorheben wollen. Stattdessen werden wir Umgebungslicht mit einem einzelnen, gerichteten Licht verwenden, welches auf den rotierenden Würfel aus der vorherigen Demo zeigt.

- -

Wenn Sie die Konzepte der Punktbeleuchtung und des Glanzlichtes hinter sich gelassen haben, gibt es dennoch zwei kleine Informationen, die Sie benötigen werden, wenn wir unser gerichtetes Licht implementieren:

- -
    -
  1. Wir müssen die Oberflächennormale mit jedem Vertex verbinden. Das ist ein Vektor, der senkrecht zur Oberfläche des Vertex ist.
  2. -
  3. Wir müssen die Richtung in welche das Licht strahlt wissen. Diese wird durch den Richtungsvektor angegeben.
  4. -
- -

Dann aktualisieren wir den Vertex-Shader, um die Farbe jedes Vertices, unter Berücksichtigung des Umgebungslichts und dem Effekt des gerichteten Lichts (sowie dessen Winkel mit dem es auf die Oberfläche trifft), einzustellen. Wir werden sehen, wie genau wir das machen, wenn wir einen Blick auf den Code für den Shader werfen.

- -

Die Normalen für die Vertices erstellen

- -

Als Erstes müssen wir einen Array für die Normalen für alle Vertices erstellen, die unseren Würfel umfassen. Da ein Würfel ein sehr einfaches Objekt ist, ist dies auch sehr einfach zu erstellen. Für komplexere Objekte wird das Berechnen der Normalen schon umfassender.

- -
  cubeVerticesNormalBuffer = gl.createBuffer();
-  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
-
-  var vertexNormals = [
-    // vorne
-     0.0,  0.0,  1.0,
-     0.0,  0.0,  1.0,
-     0.0,  0.0,  1.0,
-     0.0,  0.0,  1.0,
-
-    // hinten
-     0.0,  0.0, -1.0,
-     0.0,  0.0, -1.0,
-     0.0,  0.0, -1.0,
-     0.0,  0.0, -1.0,
-
-    // oben
-     0.0,  1.0,  0.0,
-     0.0,  1.0,  0.0,
-     0.0,  1.0,  0.0,
-     0.0,  1.0,  0.0,
-
-    // unten
-     0.0, -1.0,  0.0,
-     0.0, -1.0,  0.0,
-     0.0, -1.0,  0.0,
-     0.0, -1.0,  0.0,
-
-    // rechts
-     1.0,  0.0,  0.0,
-     1.0,  0.0,  0.0,
-     1.0,  0.0,  0.0,
-     1.0,  0.0,  0.0,
-
-    // links
-    -1.0,  0.0,  0.0,
-    -1.0,  0.0,  0.0,
-    -1.0,  0.0,  0.0,
-    -1.0,  0.0,  0.0
-  ];
-
-  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(vertexNormals), gl.STATIC_DRAW);
-
- -

Das sollte Ihnen schon bekannt vorkommen: Wir erstellen einen neuen Buffer, verknüpfen den Array damit und senden dann unseren Vertex-Normalen-Array in den Buffer, indem wir bufferData() aufrufen.

- -

Dann fügen wir Code zu drawScene() hinzu, um das Normalen-Array mit einem Shader-Attribut zu verknüpfen, sodass der Shader-Code darauf zugreifen kann:

- -
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
-  gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
-
- -

Schließlich müssen wir den Code aktualisieren, welcher die Einheitsmatrizen erstellt, um eine Normalenmatrix zu generieren und zum Shader auszuliefern. Diese wird verwendet, um die Normalen umzuwandeln, wenn die aktuelle Orientierung des Würfels in Beziehung zur Lichtquelle behandelt wird:

- -
  var normalMatrix = mvMatrix.inverse();
-  normalMatrix = normalMatrix.transpose();
-  var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
-  gl.uniformMatrix4fv(nUniform, false, new WebGLFloatArray(normalMatrix.flatten()));
-
- -

Die Shader aktualisieren

- -

Jetzt sind alle Daten, die von den Shadern gebraucht werden, verfügbar. Wir müssen nun den Code in den Shadern selbst aktualisieren.

- -

Der Vertex-Shader

- -

Zunächst aktualisieren wir den Vertex-Shader, sodass dieser einen Schattenwert für jeden Vertex auf Basis des Umgebungslichts sowie des direktionalen Lichts berechnet. Werfen wir einen Blick auf den Code:

- -
    <script id="shader-vs" type="x-shader/x-vertex">
-      attribute vec3 aVertexNormal;
-      attribute vec3 aVertexPosition;
-      attribute vec2 aTextureCoord;
-
-      uniform mat4 uNormalMatrix;
-      uniform mat4 uMVMatrix;
-      uniform mat4 uPMatrix;
-
-      varying vec2 vTextureCoord;
-      varying vec3 vLighting;
-
-      void main(void) {
-        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
-        vTextureCoord = aTextureCoord;
-
-        // Beleuchtungseffekt anwenden
-
-        vec3 ambientLight = vec3(0.6, 0.6, 0.6);
-        vec3 directionalLightColor = vec3(0.5, 0.5, 0.75);
-        vec3 directionalVector = vec3(0.85, 0.8, 0.75);
-
-        vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
-
-        float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
-        vLighting = ambientLight + (directionalLightColor * directional);
-      }
-    </script>
-
- -

Sobald die Position des Vertex berechnet wurde und wir die Koordinaten des Texels (welcher passend zum Vertex ist) erhalten haben, können wir den Schatten für den Vertex berechnen.

- -

Als Erstes wandeln wir die Normale auf Basis der aktuellen Position und Orientierung des Würfels um, indem wir die Normale des Vertexes mit der Normalenmatrix multiplizieren. Dann können wir den Betrag an direktionalem Licht, welcher auf den Vertex angewendet werden soll, berechnen, indem wir das Skalarprodukt der umgewandelten Normalen und des direktionalen Vektors (Richtung aus der das Licht kommt) bilden. Wenn dieser Wert kleiner als Null ist, setzen wir den Wert auf Null fest, da man nicht weniger als Null Licht haben kann.

- -

Wenn der Betrag des direktionalen Lichts berechnet wurde, können wir den Beleuchtungswert generieren, indem wir das Umgebungslicht nehmen und das Produkt der Farbe des direktionalen Lichts und den Betrag an direktionalem Licht bereitstellen. Als Ergebnis haben wir nun einen RGB-Wert, welcher vom Fragment-Shader verwendet wird, um die Farbe jedes Pixels den wir rendern einzustellen.

- -

Der Fragment-Shader

- -

Der Fragment-Shader muss nun aktualisiert werden, um den berechneten Beleuchtungswert vom Vertex-Shader zu berücksichtigen:

- -
    <script id="shader-fs" type="x-shader/x-fragment">
-      varying vec2 vTextureCoord;
-      varying vec3 vLighting;
-
-      uniform sampler2D uSampler;
-
-      void main(void) {
-        vec4 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
-
-        gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);
-      }
-    </script>
-
- -

Hier erhalten wir nun die Farbe des Texels, genau so wie wir es im vorherigen Beispiel getan haben. Bevor wir die Farbe der Fragmente aber festlegen, multiplizieren wir die Texel-Farbe mit dem Beleuchtungswert, um die Texel-Farbe so einzustellen, dass diese den Effekt der Lichtquelle berücksichtigt.

- -

Und das war's! Wenn Sie einen Browser verwenden, der WebGL unterstützt, können Sie einen Blick auf die Live-Demo werfen.

- -

Übungen für den Leser

- -

Dies ist natürlich ein sehr einfaches Beispiel, welches eine Beleuchtung pro Vertex implementiert. Für fortgeschrittene Grafiken, möchten Sie sicher eine Beleuchtung pro Pixel implementieren, aber dies wird Sie in die Richtung leiten.

- -

Sie können nun also versuchen mit der Richtung der Lichtquelle zu experimentieren, die Farbe der Leuchtquelle zu ändern und so weiter.

- -

{{PreviousNext("Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden", "Web/API/WebGL_API/Tutorial/Animierte_Texturen_in_WebGL")}}

diff --git a/files/de/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html b/files/de/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html new file mode 100644 index 0000000000..d3a21591fd --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html @@ -0,0 +1,126 @@ +--- +title: 3D-Objekte mit WebGL erstellen +slug: Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren", "Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden")}}

+ +

Bringen wir unser Quadrat in die dritte Dimension, indem wir fünf oder mehr Flächen hinzufügen und daraus einen Würfel machen. Um das effizient zu machen, wechseln wir vom Zeichnen direkt über die Vertices zur gl.drawArray() Methode, um den Vertex-Array als eine Tabelle zu verwenden und die einzelnen Vertices in dieser Tabelle als Referenz für Positionen jeder Fläche zu definieren, indem wir gl.drawElements() aufrufen.

+ +

Bedenken Sie: Jede Fläche benötigt vier Vertices, die diese definieren, aber jeder Vertex wird von drei Flächen verwendet. Wir können eine Menge Daten sparen, indem wir eine Liste aller 24 Vertices erstellen und uns dann auf jeden Vertex durch dessen Index in der Liste beziehen, anstatt den gesamten Koordinatensatz zu verwenden.

+ +

Die Vertex-Positionen des Würfels definieren

+ +

Zunächst wollen wir den Positionsspeicher der Vertices erstellen, indem wir den Code in initBuffers() ändern. Das geschieht genau so wie für das Quadrat, allerdings haben wir hier ein paar Datensätze mehr, da wir 24 Vertices (4 pro Seite) haben müssen:

+ +
  var vertices = [
+    // vordere Fläche
+    -1.0, -1.0,  1.0,
+     1.0, -1.0,  1.0,
+     1.0,  1.0,  1.0,
+    -1.0,  1.0,  1.0,
+
+    // hintere Fläche
+    -1.0, -1.0, -1.0,
+    -1.0,  1.0, -1.0,
+     1.0,  1.0, -1.0,
+     1.0, -1.0, -1.0,
+
+    // obere Fläche
+    -1.0,  1.0, -1.0,
+    -1.0,  1.0,  1.0,
+     1.0,  1.0,  1.0,
+     1.0,  1.0, -1.0,
+
+    // untere Fläche
+    -1.0, -1.0, -1.0,
+     1.0, -1.0, -1.0,
+     1.0, -1.0,  1.0,
+    -1.0, -1.0,  1.0,
+
+    // rechte Fläche
+     1.0, -1.0, -1.0,
+     1.0,  1.0, -1.0,
+     1.0,  1.0,  1.0,
+     1.0, -1.0,  1.0,
+
+    // linke Fläche
+    -1.0, -1.0, -1.0,
+    -1.0, -1.0,  1.0,
+    -1.0,  1.0,  1.0,
+    -1.0,  1.0, -1.0
+  ];
+
+ +

Die Farben der Vertices definieren

+ +

Außerdem müssen wir einen Array für die Farben der 24 Vertices erstellen. Dieser Code definiert zunächst die Farben für jede Fläche und verwendet dann eine Schleife, um jeden der Vertices mit einer Farbe zu bestücken.

+ +
  var colors = [
+    [1.0,  1.0,  1.0,  1.0],    // vordere Fläche: weiß
+    [1.0,  0.0,  0.0,  1.0],    // hintere Fläche: rot
+    [0.0,  1.0,  0.0,  1.0],    // obere Fläche: grün
+    [0.0,  0.0,  1.0,  1.0],    // untere Fläche: blau
+    [1.0,  1.0,  0.0,  1.0],    // rechte Fläche: gelb
+    [1.0,  0.0,  1.0,  1.0]     // linke Fläche: violett
+  ];
+
+  var generatedColors = [];
+
+  for (j=0; j<6; j++) {
+    var c = colors[j];
+
+    for (var i=0; i<4; i++) {
+      generatedColors = generatedColors.concat(c);
+    }
+  }
+
+  cubeVerticesColorBuffer = gl.createBuffer();
+  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
+  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(generatedColors), gl.STATIC_DRAW);
+
+ +

Das Element-Array definieren

+ +

Sobald die Vertex-Arrays generiert worden sind, müssen wir das Element-Array erstellen.

+ +
  cubeVerticesIndexBuffer = gl.createBuffer();
+  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
+
+  // Dieser Array definiert jede Fläche als zwei Dreiecke über die Indizes
+  // im Vertex-Array, um die Position jedes Dreiecks festzulegen.
+
+  var cubeVertexIndices = [
+    0,  1,  2,      0,  2,  3,    // vorne
+    4,  5,  6,      4,  6,  7,    // hinten
+    8,  9,  10,     8,  10, 11,   // oben
+    12, 13, 14,     12, 14, 15,   // unten
+    16, 17, 18,     16, 18, 19,   // rechts
+    20, 21, 22,     20, 22, 23    // links
+  ]
+
+  // Sende nun das Element-Array zum GL
+
+  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
+      new WebGLUnsignedShortArray(cubeVertexIndices), gl.STATIC_DRAW);
+
+ +

Das cubeVertexIndices Array definiert jede Fläche als ein paar von Dreiecken, alle Vertices des Dreiecks werden als ein Index im Vertex-Array des Würfels festgelegt. Daher ist der Würfel aus einer Sammlung von 12 Dreiecken beschrieben.

+ +

Den Würfel zeichnen

+ +

Als nächstes müssen wir etwas Code zur drawScene() Funktion hinzufügen, um über den Indexspeicher des Würfels zu zeichnen. Wir fügen neue bindBuffer() und drawElements() Aufrufe hinzu:

+ +
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
+  setMatrixUniforms();
+  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
+
+ +

Da jede Seite unseres Würfels aus zwei Dreiecken besteht, gibt es 6 Vertices pro Seite, oder 36 Vertices im Würfel, obwohl einige davon doppelt sind. Da unser Index-Array jedoch aus einfachen Integern besteht, stellt dies keinen unkoordinierbaren Betrag an Daten dar, welcher für jeden Frame der Animation durchgegangen werden muss.

+ +

Jetzt haben wir einen animierten Würfel, welcher herum springt, rotiert und über sechs unterschiedliche Seiten verfügt. Wenn Ihr Browser WebGL unterstützt, schauen Sie sich hier die Demo in Aktion an.

+ +

{{PreviousNext("Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren", "Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden")}}

diff --git "a/files/de/web/api/webgl_api/tutorial/einf\303\274hrung_in_webgl/index.html" "b/files/de/web/api/webgl_api/tutorial/einf\303\274hrung_in_webgl/index.html" deleted file mode 100644 index 5906f95761..0000000000 --- "a/files/de/web/api/webgl_api/tutorial/einf\303\274hrung_in_webgl/index.html" +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Einführung in WebGL -slug: Web/API/WebGL_API/Tutorial/Einführung_in_WebGL -tags: - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{Next("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext")}}

- -

Mit WebGL steht eine API zur Verfügung, die auf OpenGL ES 2.0 basiert, um 3D Rendering im HTML canvas Element zu ermöglichen. WebGL Programme bestehen aus Steuercode in JavaScript und Shadercode (GLSL), der auf dem Grafikprozessor (GPU) des Computers ausgeführt wird. WebGL Elemente können mit anderen HTML Elementen kombiniert werden.

- -

Dieser Artikel ist eine Einführung in die Grundlagen von WebGL. Es wird vorausgesetzt, dass einige mathematischen Kenntnisse im 3D-Bereich (Matrizen) vorhanden sind. Dieser Artikel wird daher keine 3D-Grafik-Konzepte vermitteln. Es gibt einen anfängergeeigneten Leitfaden mit Programmieraufgaben in unserem Lernbereich: Learn WebGL for 2D and 3D graphics.

- -

DIe hier verwendeten Codebeispiele finden sich auch im webgl-examples GitHub repository.

- -

3D Rendering vorbereiten

- -

Um WebGL benutzen zu können, wird als erstes ein canvas Element benötigt. Der folgende HTML-Code definiert eine canvas Zeichenfläche.

- -
<body>
-  <canvas id="glCanvas" width="640" height="480"></canvas>
-</body>
-
- -

Vorbereitung des WebGL-Kontexts

- -

Die main() Funktion im JavaScript Code wird aufgerufen, nachdem das Dokument geladen wurde. Die Aufgabe der Funktion ist, den WebGL-Kontext festzulegen und mit dem Rendering zu beginnen.

- -
main();
-
-function main() {
-  const canvas = document.querySelector("#glCanvas");
-  // Initialisierung des GL Kontexts
-  const gl = canvas.getContext("webgl");
-
-  // Nur fortfahren, wenn WebGL verfügbar ist und funktioniert
-  if (!gl) {
-    alert("Unable to initialize WebGL. Your browser or machine may not support it.");
-    return;
-  }
-
-  // Setze clear color auf schwarz, vollständig sichtbar
-  gl.clearColor(0.0, 0.0, 0.0, 1.0);
-  // Lösche den color buffer mit definierter clear color
-  gl.clear(gl.COLOR_BUFFER_BIT);
-}
- -

Als erstes verschaffen wir uns eine Referenz zum canvas Element und speichern sie in der canvas Variable.

- -

Sobald wir den Bezug zum canvas haben, versuchen wir einen zugehörigen  WebGLRenderingContext zu erhalten, indem wir getContext aufrufen und dabei den String "webgl" mitgeben. Falls der Browser WebGL nicht unterstützt, wird getContext null zurückgeben, woraufhin wir den Nutzer benachrichtigen und das Script verlassen.

- -

Wenn der Kontext erfolgreich initialisiert wurde, ist gl eine Variable für den Kontext. In diesem Fall setzen wir die clear color auf schwarz, und löschen den zugehörigen Kontext (der canvas wird mit der Hintergrundfarbe neu gezeichnet)

- -

An dieser Stelle ist genug Code vorhanden, um den WebGL-Kontext erfolgreich zu initialisieren. Ein großer, leerer, schwarzer Kasten sollte zu sehen sein, der darauf wartet mit weiteren Inhalten gefüttert zu werden.

- -

{{EmbedGHLiveSample('webgl-examples/tutorial/sample1/index.html', 670, 510) }}

- -

Vollständigen Code ansehen | Demo in neuer Seite öffnen

- -

Siehe auch

- -

 

- - - -

 

- -

{{Next("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext")}}

diff --git "a/files/de/web/api/webgl_api/tutorial/farben_mittels_shader_in_einen_webgl-kontext_hinzuf\303\274gen/index.html" "b/files/de/web/api/webgl_api/tutorial/farben_mittels_shader_in_einen_webgl-kontext_hinzuf\303\274gen/index.html" deleted file mode 100644 index 94068822d5..0000000000 --- "a/files/de/web/api/webgl_api/tutorial/farben_mittels_shader_in_einen_webgl-kontext_hinzuf\303\274gen/index.html" +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Farben mittels Shader in einen WebGL-Kontext hinzufügen -slug: >- - Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen -tags: - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext", "Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren")}}

- -

Wir haben ein simples Quadrat im vorherigen Teil erstellt, im nächsten Schritt wollen wir ein bisschen Farbe ins Spiel bringen. Dafür sind Änderungen an den Shadern erforderlich.

- -

Farben zu den Eckpunkten hinzufügen

- -

In der Computergrafik werden Objekte mit einer Reihe von Punkten erstellt. Jeder Punkt hat eine Position und eine Farbe. Standardmäßig werden alle anderen Pixelfarben (und alle weiteren Attribute, darunter die Position) über eine lineare Interpolation berechnet, die automatisch glatte Verläufe erstellt. Vorher hat unser Vertex-Shader keine festgelegten Farben auf die Punkte (Vertices) angewendet und der Fragment-Shader legte die feste Farbe weiß für jeden Pixel fest, sodass das gesamte Quadrat komplett weiß gezeichnet wurde.

- -

Nun wollen wir in jeder Ecke des Quadrats einen Verlauf in einer unterschiedlichen Farbe rendern: rot, blau, grün und weiß. Als erstes sollten wir daher diese Farben in den vier Eckpunkten einrichten. Um das zu machen, müssen wir zunächst einen Array der Vertex-Farben erstellen und diesen dann in einen WebGL Buffer speichern. Das erreichen wir durch die folgenden Zeilen in unserer initBuffers() Funktion:

- -
var colors = [
-  1.0,  1.0,  1.0,  1.0,    // weiß
-  1.0,  0.0,  0.0,  1.0,    // rot
-  0.0,  1.0,  0.0,  1.0,    // grün
-  0.0,  0.0,  1.0,  1.0     // blau
-];
-
-squareVerticesColorBuffer = gl.createBuffer();
-gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
-gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
-}
-
- -

Dieser Code definiert zuerst einen JavaScript Array, welcher die vier Farb-Vektoren mit jeweils vier Werten für die jeweilige Farbe enthält. Dann wird ein neuer WebGL Buffer angewiesen diese Farben zu speichern und der Array wird in WebGL Floats konvertiert und im Buffer gespeichert.

- -

Um die Farben schließlich zu verwenden, muss der der Vertex-Shader aktualisiert werden, um die entsprechende Farbe vom Farb-Buffer zu erhalten:

- -
<script id="shader-vs" type="x-shader/x-vertex">
-  attribute vec3 aVertexPosition;
-  attribute vec4 aVertexColor;
-
-  uniform mat4 uMVMatrix;
-  uniform mat4 uPMatrix;
-
-  varying vec4 vColor;
-
-  void main(void) {
-    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
-    vColor = aVertexColor;
-  }
-</script>
-
- -

Der wichtigste Unterschied ist hier, dass wir für jeden Punkt (Vertex) die Farbe entsprechend des Farb-Arrays setzen.

- -

Die Fragmente mit Farben versehen

- -

Als Erinnerung, so sah unser Fragment-Shader vorher aus:

- -
<script id="shader-fs" type="x-shader/x-fragment">
-  void main(void) {
-    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
-  }
-</script>
-
- -

Um nun die interpolierte Farbe für jeden Pixel aufzugreifen, müssen wir dies einfach so ändern, dass wir den Wert der vColor Variable erhalten:

- -
<script id="shader-fs" type="x-shader/x-fragment">
-  varying vec4 vColor;
-
-  void main(void) {
-    gl_FragColor = vColor;
-  }
-</script>
-
- -

Das ist eine sehr einfache Änderung. Anstatt des festen Wertes vorher, erhält jedes Fragment jetzt einfach die interpolierte Farbe, basierend auf der Position relativ zu den Kontenpunkten (Vertices).

- -

Mit den Farben zeichnen

- -

Als nächstes ist es nötig, Code zur initShaders() Routine hinzuzufügen, um das Farbattribut für das Shader-Programm zu initialisieren:

- -
  vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
-  gl.enableVertexAttribArray(vertexColorAttribute);
-
- -

Dann wird drawScene() abgeändert, um schließlich die Farben zu verwenden, wenn das Quadrat gezeichnet wird:

- -
  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
-  gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
-
- -

Jetzt sollten Sie das Beispiel in einem WebGL kompatiblen Browser sehen und Ihre Ausgabe sollte so wie im Bild unten gezeichnet werden (das Quadrat ist innerhalb eines schwarzen Felds zu sehen):

- -

screenshot.png

- -

{{PreviousNext("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext", "Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren")}}

diff --git a/files/de/web/api/webgl_api/tutorial/getting_started_with_webgl/index.html b/files/de/web/api/webgl_api/tutorial/getting_started_with_webgl/index.html new file mode 100644 index 0000000000..5906f95761 --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/getting_started_with_webgl/index.html @@ -0,0 +1,73 @@ +--- +title: Einführung in WebGL +slug: Web/API/WebGL_API/Tutorial/Einführung_in_WebGL +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{Next("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext")}}

+ +

Mit WebGL steht eine API zur Verfügung, die auf OpenGL ES 2.0 basiert, um 3D Rendering im HTML canvas Element zu ermöglichen. WebGL Programme bestehen aus Steuercode in JavaScript und Shadercode (GLSL), der auf dem Grafikprozessor (GPU) des Computers ausgeführt wird. WebGL Elemente können mit anderen HTML Elementen kombiniert werden.

+ +

Dieser Artikel ist eine Einführung in die Grundlagen von WebGL. Es wird vorausgesetzt, dass einige mathematischen Kenntnisse im 3D-Bereich (Matrizen) vorhanden sind. Dieser Artikel wird daher keine 3D-Grafik-Konzepte vermitteln. Es gibt einen anfängergeeigneten Leitfaden mit Programmieraufgaben in unserem Lernbereich: Learn WebGL for 2D and 3D graphics.

+ +

DIe hier verwendeten Codebeispiele finden sich auch im webgl-examples GitHub repository.

+ +

3D Rendering vorbereiten

+ +

Um WebGL benutzen zu können, wird als erstes ein canvas Element benötigt. Der folgende HTML-Code definiert eine canvas Zeichenfläche.

+ +
<body>
+  <canvas id="glCanvas" width="640" height="480"></canvas>
+</body>
+
+ +

Vorbereitung des WebGL-Kontexts

+ +

Die main() Funktion im JavaScript Code wird aufgerufen, nachdem das Dokument geladen wurde. Die Aufgabe der Funktion ist, den WebGL-Kontext festzulegen und mit dem Rendering zu beginnen.

+ +
main();
+
+function main() {
+  const canvas = document.querySelector("#glCanvas");
+  // Initialisierung des GL Kontexts
+  const gl = canvas.getContext("webgl");
+
+  // Nur fortfahren, wenn WebGL verfügbar ist und funktioniert
+  if (!gl) {
+    alert("Unable to initialize WebGL. Your browser or machine may not support it.");
+    return;
+  }
+
+  // Setze clear color auf schwarz, vollständig sichtbar
+  gl.clearColor(0.0, 0.0, 0.0, 1.0);
+  // Lösche den color buffer mit definierter clear color
+  gl.clear(gl.COLOR_BUFFER_BIT);
+}
+ +

Als erstes verschaffen wir uns eine Referenz zum canvas Element und speichern sie in der canvas Variable.

+ +

Sobald wir den Bezug zum canvas haben, versuchen wir einen zugehörigen  WebGLRenderingContext zu erhalten, indem wir getContext aufrufen und dabei den String "webgl" mitgeben. Falls der Browser WebGL nicht unterstützt, wird getContext null zurückgeben, woraufhin wir den Nutzer benachrichtigen und das Script verlassen.

+ +

Wenn der Kontext erfolgreich initialisiert wurde, ist gl eine Variable für den Kontext. In diesem Fall setzen wir die clear color auf schwarz, und löschen den zugehörigen Kontext (der canvas wird mit der Hintergrundfarbe neu gezeichnet)

+ +

An dieser Stelle ist genug Code vorhanden, um den WebGL-Kontext erfolgreich zu initialisieren. Ein großer, leerer, schwarzer Kasten sollte zu sehen sein, der darauf wartet mit weiteren Inhalten gefüttert zu werden.

+ +

{{EmbedGHLiveSample('webgl-examples/tutorial/sample1/index.html', 670, 510) }}

+ +

Vollständigen Code ansehen | Demo in neuer Seite öffnen

+ +

Siehe auch

+ +

 

+ + + +

 

+ +

{{Next("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext")}}

diff --git "a/files/de/web/api/webgl_api/tutorial/hinzuf\303\274gen_von_2d_inhalten_in_einen_webgl-kontext/index.html" "b/files/de/web/api/webgl_api/tutorial/hinzuf\303\274gen_von_2d_inhalten_in_einen_webgl-kontext/index.html" deleted file mode 100644 index 56c267f394..0000000000 --- "a/files/de/web/api/webgl_api/tutorial/hinzuf\303\274gen_von_2d_inhalten_in_einen_webgl-kontext/index.html" +++ /dev/null @@ -1,238 +0,0 @@ ---- -title: Hinzufügen von 2D Inhalten in einen WebGL-Kontext -slug: Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext -tags: - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Einführung_in_WebGL", "Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen")}}

- -

Sobald der WebGL-Kontext erfolgreich erstellt wurde, können wir anfangen darin zu rendern. Am einfachsten beginnen wir mit einem einfachen, zweidimensionalen, untextuierten Objekt. Fangen wir also damit an, ein Stück Code zu schreiben, um ein Quadrat zu zeichnen.

- -

Hinweis zur deutschen Übersetzung dieses Abschnitts

- -

Die deutsche Übersetzung bezieht sich auf ältere Version des Beispielcodes als der englische Originaltext. Der im deutschen Text beschriebene Code hat einige Probleme:

- - - -

Um das Beispiel selber nachzuprogrammieren ist es daher empfehlenswert, auf die englische Version des Tutorials zu wechseln. Vielleicht haben Sie ja auch Lust, diese Version ins Deutsche zu übersetzen?

- -

Beleuchtung der Szene

- -

Das Wichtigste, das wir verstehen müssen bevor wir anfangen können, ist, dass wir, uns bereits in einer dreidimensionalen Umgebung befinden, obwohl wir nur ein zweidimensionales Objekt in diesem Beispiel rendern wollen. Das heißt, wir müssen jetzt bereits Shader einsetzen, die unsere einfache Szene beleuchten und solche erstellen, die unser Objekt zeichnen. Diese Shader werden festlegen wie unser Quadrat später beleuchtet sein wird.

- -

Initialisierung der Shader

- -

Shader sind durch die OpenGL ES Shading Language (pdf) spezifiziert. Damit es einfacher ist unsere Inhalte zu warten und zu aktualisieren, können wir unseren Code so schreiben, dass die Shader im HTML Dokument gefunden werden, anstatt alles mittels JavaScript zu bauen. Werfen wir einen Blick auf unsere initShaders() Routine, welche diese Aufgabe übernimmt:

- -
function initShaders() {
-  var fragmentShader = getShader(gl, "shader-fs");
-  var vertexShader = getShader(gl, "shader-vs");
-
-  // Erzeuge Shader
-
-  shaderProgram = gl.createProgram();
-  gl.attachShader(shaderProgram, vertexShader);
-  gl.attachShader(shaderProgram, fragmentShader);
-  gl.linkProgram(shaderProgram);
-
-  // Wenn die das Aufrufen der Shader fehlschlägt,
-  // gib eine Fehlermeldung aus:
-
-  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
-    alert("Initialisierung des Shaderprogramms nicht möglich.");
-  }
-
-  gl.useProgram(shaderProgram);
-
-  vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
-  gl.enableVertexAttribArray(vertexPositionAttribute);
-}
-
-
- -

Es werden zwei Shader in dieser Routine geladen; der Erste, der fragmentShader, wird vom script Element mit der Id "shader-fs" geladen. Der Zweite, der vertexShader, wird vom script Element mit der Id "shader-vs" geladen. Wir werden im nächsten Abschnitt noch näher auf die getShader() Funktion eingehen. Diese Routine holt sich dann die Shader aus dem DOM.

- -

In diesem Teil erstellen wir noch das Shaderprogramm, in dem wir die Funktion createProgram() aufrufen, die beiden Shader anhängen und das Shaderprogramm verlinken. Danach wird der Zustand des LINK_STATUS Parameters überprüft, um sicher zu gehen, dass das Programm erfolgreich verlinkt wurde. Wenn das der Fall ist, aktivieren wir das neue Shaderprogramm.

- -

Shader aus dem DOM laden

- -

Die getShader() Routine ruft ein Shaderprogramm mit dem festgelegtem Namen aus dem DOM auf, gibt das kompilierte Shaderprogramm zurück oder ist leer, wenn nichts geladen oder kompiliert worden konnte.

- -
function getShader(gl, id) {
-  var shaderScript = document.getElementById(id);
-
-  if (!shaderScript) {
-    return null;
-  }
-
-  var theSource = "";
-  var currentChild = shaderScript.firstChild;
-
-  while(currentChild) {
-    if (currentChild.nodeType == 3) {
-      theSource += currentChild.textContent;
-    }
-
-    currentChild = currentChild.nextSibling;
-  }
- -

Wenn das Element mit der festgelegten Id gefunden wurde, wird der Text in die Variable theSource gespeichert.

- -
  var shader;
-
-  if (shaderScript.type == "x-shader/x-fragment") {
-    shader = gl.createShader(gl.FRAGMENT_SHADER);
-  } else if (shaderScript.type == "x-shader/x-vertex") {
-    shader = gl.createShader(gl.VERTEX_SHADER);
-  } else {
-    return null;  // Unbekannter Shadertyp
-  }
- -

Jetzt wo der Code für die Shader gelesen wurde, können wir uns die MIME Typen der Shader angucken, um festzulegen, ob es ein Vertex-Shader (MIME Typ: "x-shader/x-vertex") oder ein Fragment-Shader (MIME Typ: "x-shader/x-fragment") ist. Danach werden dann die entsprechenden Shadertypen erstellt.

- -
  gl.shaderSource(shader, theSource);
-
-  // Kompiliere das Shaderprogramm
-
-  gl.compileShader(shader);
-
-  // Überprüfe, ob die Kompilierung erfolgreich war
-
-  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
-    alert("Es ist ein Fehler beim Kompilieren der Shader aufgetaucht: " + gl.getShaderInfoLog(shader));
-    return null;
-  }
-
-  return shader;
-}
-
- -

Schließlich wird der Shader kompiliert. Falls ein Fehler während der Kompilierung auftritt, zeigen wir die Fehlermeldung an und geben null zurück. Andernfalls wird der kompilierte Shader zurückgegeben.

- -

Die Shader

- -

Nun müssen wir die eigentlichen Shaderprogramme in unser HTML schreiben. Wie genau diese Shader arbeiten, übersteigt das Ziel dieses Tutorials, wir gehen daher nur auf das Wesentliche ein.

- -

Fragment-Shader

- -

Jeder Pixel in einem Vieleck wird Fragment in der GL-Fachsprache genannt. Die Aufgabe des Fragment-Shaders ist es, die Farbe für jeden Pixel bereitzustellen. In unserem Fall ordnen wir einfach jedem Pixel eine weiße Farbe zu.

- -

gl_FragColor ist eine eingebaute GL Variable, die für die Farbe des Fragments verwendet wird.

- -
<script id="shader-fs" type="x-shader/x-fragment">
-
-  void main(void) {
-    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
-  }
-</script>
-
- -

Vertex-Shader

- -

Der Vertex-Shader definiert die Position und Form von jedem Punkt.

- -
<script id="shader-vs" type="x-shader/x-vertex">
-  attribute vec3 aVertexPosition;
-
-  uniform mat4 uMVMatrix;
-  uniform mat4 uPMatrix;
-
-
-  void main(void) {
-    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
-  }
-</script>
-
- -

Das Objekt erstellen

- -

Bevor wir unser Quadrat rendern können, müssen wir einen Puffer erstellen, der unsere Punkte enthält. Das werden wir mittels einer Funktion machen, die wir initBuffers() nennen. Wenn wir zu mehr fortgeschrittenen WebGL-Konzepten kommen, wird diese Routine vergrößert, um mehr - und komplexere - 3D-Objekte zu erstellen.

- -
var horizAspect = 480.0/640.0;
-
-function initBuffers() {
-  squareVerticesBuffer = gl.createBuffer();
-  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
-
-  var vertices = [
-    1.0,  1.0,  0.0,
-    -1.0, 1.0,  0.0,
-    1.0,  -1.0, 0.0,
-    -1.0, -1.0, 0.0
-  ];
-
-  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
-}
-
- -

Diese Routine ist, durch die einfache Art der Szene in diesem Beispiel, sehr einfach gehalten. Es geht los mit dem Aufruf der createBuffer() Methode, die einen Puffer erstellt in dem wir die Punkte speichern können. Der Puffer wird, durch Aufrufen der bindBuffer() Methode, mit dem Kontext verbunden.

- -

Wenn das erledigt ist, erstellen wir einen JavaScript Array, der die Koordinaten für jeden Punkt des Quadrats enthält. Dieser wird dann in einen WebGL FloatArray umgewandelt und durch die bufferData() Methode werden die Punkte für das Objekt festgelegt.

- -

Die Szene zeichnen

- -

Jetzt sind die Shader aufgebaut und das Objekt ist erstellt. Wir können die Szene rendern lassen. Da wir in dieser Szene nichts animieren, ist unsere drawScene() Funktion sehr einfach. Es werden einige nützliche Routinen verwendet, die wir uns kurz anschauen.

- -
function drawScene() {
-  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-
-  perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0);
-
-  loadIdentity();
-  mvTranslate([-0.0, 0.0, -6.0]);
-
-  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
-  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
-  setMatrixUniforms();
-  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
-}
-
- -

Als Erstes wird der Kontext auf unsere Hintergrundfarbe gesetzt und die Kameraperspektive festgelegt. Wir definieren ein Blickfeld von 45°, mit einem Höhen-/Breitenverhältnis von 640 zu 480 (die Größe unseres Canvas). Außerdem legen wir fest, dass wir nur Objekte zwischen 0.1 und 100 Einheiten gerendert haben wollen.

- -

Dann wird die Position des Quadrats, über das Laden der ursprünglichen Position und der Verschiebung um 6 Einheiten von der Kamera weg, ermittelt. Danach, verbinden wir den Puffer des Quadrats mit dem Kontext, konfigurieren es, und zeichnen das Objekt, in dem wir die drawArrays() Methode aufrufen.

- -

Das Ergebnis kann hier ausprobiert werden, wenn Sie einen Browser verwenden, der WebGL unterstützt.

- -

Matrix Operationen

- -

Matrix Operationen sind schon kompliziert genug. Keiner möchte wirklich den ganzen Code selbst schreiben, der benötigt wird um die Berechnungen selber durchzuführen. Glücklicherweise gibt es Sylvester, eine sehr handliche Bibliothek, die bestens mit Vektor und Matrix Operationen in JavaScript umgehen kann.

- -

Die glUtils.js Datei, die in dieser Demo benutzt wird, wird bei einer ganzen Reihe von WebGL-Demos, die Web zu finden sind, verwendet. Keiner scheint sich völlig sicher zu sein, woher diese Bibliothek ursprünglich herkommt, aber es vereinfacht den Gebrauch von Sylvester noch weiter, in dem Methoden hinzugefügt werden, die auch spezielle Matrizentypen ermöglichen und HTML ausgegeben werden kann, um die Matrizen anzeigen zu lassen.

- -

Zusätzlich, definiert diese Demo ein paar hilfreiche Routinen, um an diese Bibliothek für spezielle Aufgaben anzukoppeln. Was genau gemacht wird, ist kein Teil dieses Artikels, aber es gibt einige gute Referenzen zu Matrizen, die online verfügbar sind. Siehe unter Siehe auch, um ein paar aufzulisten.

- -
function loadIdentity() {
-  mvMatrix = Matrix.I(4);
-}
-
-function multMatrix(m) {
-  mvMatrix = mvMatrix.x(m);
-}
-
-function mvTranslate(v) {
-  multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
-}
-
-function setMatrixUniforms() {
-  var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
-  gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten()));
-
-  var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
-  gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten()));
-}
-
- -

Siehe auch

- - - -

{{PreviousNext("Web/API/WebGL_API/Tutorial/Einführung_in_WebGL", "Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen")}}

diff --git a/files/de/web/api/webgl_api/tutorial/lighting_in_webgl/index.html b/files/de/web/api/webgl_api/tutorial/lighting_in_webgl/index.html new file mode 100644 index 0000000000..bd65b5bb40 --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/lighting_in_webgl/index.html @@ -0,0 +1,172 @@ +--- +title: Beleuchtung in WebGL +slug: Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Lighting_in_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden", "Web/API/WebGL_API/Tutorial/Animierte_Texturen_in_WebGL")}}

+ +

Als Erstes sollten wir verstehen, dass WebGL nicht wie OpenGL über eine eigene Unterstützung für die Beleuchtung verfügt. Das müssen wir selbst machen. Glücklicherweise ist es nicht sehr schwer und dieser Artikel versucht Ihnen die Grundlagen dazu näher zu bringen.

+ +

Beleuchtung und Schattierung simulieren

+ +

Obwohl wir nicht weiter ins Detail über die Theorie hinter simulierter Beleuchtung in 3D Grafiken gehen wollen, was außerdem weit über diesen Artikel hinaus gehen würde, ist es gut zu wissen wie es ungefähr funktioniert. Anstatt hier nun in aller Tiefe zu diskutieren, sollten Sie einen Blick auf den »Phong Shading« Artikel auf Wikipedia werfen, welcher einen guten Überblick über das meist genutzte Beleuchtungsmodell liefert.

+ +

Es gibt drei grundlegende Typen zur Beleuchtung:

+ +

Umgebungslicht ist das Licht, das die Szene umgibt. Es weist in keine Richtung und bestrahlt jede Oberfläche in gleicher Weise, egal in welche Richtung es zeigt.

+ +

Gerichtetes Licht ist das Licht, das von einer festgelegten Richtung ausgestrahlt wird. Dieses Licht ist so weit weg, sodass sich jeder Photon parallel zu jedem anderen Photon bewegt. Sonnenlicht ist zum Beispiel gerichtetes (direktionales) Licht.

+ +

Punktbeleuchtung ist das Licht, das von einem Punkt ausgestrahlt wird und von dort radial in alle Richtungen verläuft. So funktionieren zum Beispiel die Glühbirnen im Alltag.

+ +

Für unsere Zwecke vereinfachen wir das Beleuchtungsmodell, indem wir nur gerichtetes Licht und Umgebungslicht betrachten. Wir haben hier keine Highlights, die wir mit Punktbeleuchtung oder Glanzlicht in dieser Szene hervorheben wollen. Stattdessen werden wir Umgebungslicht mit einem einzelnen, gerichteten Licht verwenden, welches auf den rotierenden Würfel aus der vorherigen Demo zeigt.

+ +

Wenn Sie die Konzepte der Punktbeleuchtung und des Glanzlichtes hinter sich gelassen haben, gibt es dennoch zwei kleine Informationen, die Sie benötigen werden, wenn wir unser gerichtetes Licht implementieren:

+ +
    +
  1. Wir müssen die Oberflächennormale mit jedem Vertex verbinden. Das ist ein Vektor, der senkrecht zur Oberfläche des Vertex ist.
  2. +
  3. Wir müssen die Richtung in welche das Licht strahlt wissen. Diese wird durch den Richtungsvektor angegeben.
  4. +
+ +

Dann aktualisieren wir den Vertex-Shader, um die Farbe jedes Vertices, unter Berücksichtigung des Umgebungslichts und dem Effekt des gerichteten Lichts (sowie dessen Winkel mit dem es auf die Oberfläche trifft), einzustellen. Wir werden sehen, wie genau wir das machen, wenn wir einen Blick auf den Code für den Shader werfen.

+ +

Die Normalen für die Vertices erstellen

+ +

Als Erstes müssen wir einen Array für die Normalen für alle Vertices erstellen, die unseren Würfel umfassen. Da ein Würfel ein sehr einfaches Objekt ist, ist dies auch sehr einfach zu erstellen. Für komplexere Objekte wird das Berechnen der Normalen schon umfassender.

+ +
  cubeVerticesNormalBuffer = gl.createBuffer();
+  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
+
+  var vertexNormals = [
+    // vorne
+     0.0,  0.0,  1.0,
+     0.0,  0.0,  1.0,
+     0.0,  0.0,  1.0,
+     0.0,  0.0,  1.0,
+
+    // hinten
+     0.0,  0.0, -1.0,
+     0.0,  0.0, -1.0,
+     0.0,  0.0, -1.0,
+     0.0,  0.0, -1.0,
+
+    // oben
+     0.0,  1.0,  0.0,
+     0.0,  1.0,  0.0,
+     0.0,  1.0,  0.0,
+     0.0,  1.0,  0.0,
+
+    // unten
+     0.0, -1.0,  0.0,
+     0.0, -1.0,  0.0,
+     0.0, -1.0,  0.0,
+     0.0, -1.0,  0.0,
+
+    // rechts
+     1.0,  0.0,  0.0,
+     1.0,  0.0,  0.0,
+     1.0,  0.0,  0.0,
+     1.0,  0.0,  0.0,
+
+    // links
+    -1.0,  0.0,  0.0,
+    -1.0,  0.0,  0.0,
+    -1.0,  0.0,  0.0,
+    -1.0,  0.0,  0.0
+  ];
+
+  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(vertexNormals), gl.STATIC_DRAW);
+
+ +

Das sollte Ihnen schon bekannt vorkommen: Wir erstellen einen neuen Buffer, verknüpfen den Array damit und senden dann unseren Vertex-Normalen-Array in den Buffer, indem wir bufferData() aufrufen.

+ +

Dann fügen wir Code zu drawScene() hinzu, um das Normalen-Array mit einem Shader-Attribut zu verknüpfen, sodass der Shader-Code darauf zugreifen kann:

+ +
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
+  gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
+
+ +

Schließlich müssen wir den Code aktualisieren, welcher die Einheitsmatrizen erstellt, um eine Normalenmatrix zu generieren und zum Shader auszuliefern. Diese wird verwendet, um die Normalen umzuwandeln, wenn die aktuelle Orientierung des Würfels in Beziehung zur Lichtquelle behandelt wird:

+ +
  var normalMatrix = mvMatrix.inverse();
+  normalMatrix = normalMatrix.transpose();
+  var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
+  gl.uniformMatrix4fv(nUniform, false, new WebGLFloatArray(normalMatrix.flatten()));
+
+ +

Die Shader aktualisieren

+ +

Jetzt sind alle Daten, die von den Shadern gebraucht werden, verfügbar. Wir müssen nun den Code in den Shadern selbst aktualisieren.

+ +

Der Vertex-Shader

+ +

Zunächst aktualisieren wir den Vertex-Shader, sodass dieser einen Schattenwert für jeden Vertex auf Basis des Umgebungslichts sowie des direktionalen Lichts berechnet. Werfen wir einen Blick auf den Code:

+ +
    <script id="shader-vs" type="x-shader/x-vertex">
+      attribute vec3 aVertexNormal;
+      attribute vec3 aVertexPosition;
+      attribute vec2 aTextureCoord;
+
+      uniform mat4 uNormalMatrix;
+      uniform mat4 uMVMatrix;
+      uniform mat4 uPMatrix;
+
+      varying vec2 vTextureCoord;
+      varying vec3 vLighting;
+
+      void main(void) {
+        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+        vTextureCoord = aTextureCoord;
+
+        // Beleuchtungseffekt anwenden
+
+        vec3 ambientLight = vec3(0.6, 0.6, 0.6);
+        vec3 directionalLightColor = vec3(0.5, 0.5, 0.75);
+        vec3 directionalVector = vec3(0.85, 0.8, 0.75);
+
+        vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
+
+        float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
+        vLighting = ambientLight + (directionalLightColor * directional);
+      }
+    </script>
+
+ +

Sobald die Position des Vertex berechnet wurde und wir die Koordinaten des Texels (welcher passend zum Vertex ist) erhalten haben, können wir den Schatten für den Vertex berechnen.

+ +

Als Erstes wandeln wir die Normale auf Basis der aktuellen Position und Orientierung des Würfels um, indem wir die Normale des Vertexes mit der Normalenmatrix multiplizieren. Dann können wir den Betrag an direktionalem Licht, welcher auf den Vertex angewendet werden soll, berechnen, indem wir das Skalarprodukt der umgewandelten Normalen und des direktionalen Vektors (Richtung aus der das Licht kommt) bilden. Wenn dieser Wert kleiner als Null ist, setzen wir den Wert auf Null fest, da man nicht weniger als Null Licht haben kann.

+ +

Wenn der Betrag des direktionalen Lichts berechnet wurde, können wir den Beleuchtungswert generieren, indem wir das Umgebungslicht nehmen und das Produkt der Farbe des direktionalen Lichts und den Betrag an direktionalem Licht bereitstellen. Als Ergebnis haben wir nun einen RGB-Wert, welcher vom Fragment-Shader verwendet wird, um die Farbe jedes Pixels den wir rendern einzustellen.

+ +

Der Fragment-Shader

+ +

Der Fragment-Shader muss nun aktualisiert werden, um den berechneten Beleuchtungswert vom Vertex-Shader zu berücksichtigen:

+ +
    <script id="shader-fs" type="x-shader/x-fragment">
+      varying vec2 vTextureCoord;
+      varying vec3 vLighting;
+
+      uniform sampler2D uSampler;
+
+      void main(void) {
+        vec4 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
+
+        gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);
+      }
+    </script>
+
+ +

Hier erhalten wir nun die Farbe des Texels, genau so wie wir es im vorherigen Beispiel getan haben. Bevor wir die Farbe der Fragmente aber festlegen, multiplizieren wir die Texel-Farbe mit dem Beleuchtungswert, um die Texel-Farbe so einzustellen, dass diese den Effekt der Lichtquelle berücksichtigt.

+ +

Und das war's! Wenn Sie einen Browser verwenden, der WebGL unterstützt, können Sie einen Blick auf die Live-Demo werfen.

+ +

Übungen für den Leser

+ +

Dies ist natürlich ein sehr einfaches Beispiel, welches eine Beleuchtung pro Vertex implementiert. Für fortgeschrittene Grafiken, möchten Sie sicher eine Beleuchtung pro Pixel implementieren, aber dies wird Sie in die Richtung leiten.

+ +

Sie können nun also versuchen mit der Richtung der Lichtquelle zu experimentieren, die Farbe der Leuchtquelle zu ändern und so weiter.

+ +

{{PreviousNext("Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden", "Web/API/WebGL_API/Tutorial/Animierte_Texturen_in_WebGL")}}

diff --git a/files/de/web/api/webgl_api/tutorial/objekte_mit_webgl_animieren/index.html b/files/de/web/api/webgl_api/tutorial/objekte_mit_webgl_animieren/index.html deleted file mode 100644 index a9eafed6db..0000000000 --- a/files/de/web/api/webgl_api/tutorial/objekte_mit_webgl_animieren/index.html +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: Objekte mit WebGL animieren -slug: Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren -tags: - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen", "Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen") }}

- -

Unser Code vom vorherigen Beispiel ist bereits so konfiguriert, dass die WebGL-Szene alle 15 Millisekunden neu gezeichnet wird. Bis jetzt wird zu jeder Zeit das gleiche Bild immer neu gezeichnet. Das wollen wir nun ändern, sodass sich unser Quadrat auf der Bildfläche bewegt.

- -

In diesem Beispiel rotieren und verschieben wir unser Quadrat in alle drei Dimensionen, sodass es schon in einem 3D Raum existieren kann (obwohl wir bisher nur ein 2D-Objekt erstellt haben).

- -

Das Quadrat rotieren lassen

- -

Fangen wir damit an, das Quadrat im Raum zu rotieren. Als erstes brauchen wir dazu eine Variable in welche wir die Rotation des Quadrats verfolgen können:

- -
var squareRotation = 0.0;
-
- -

Jetzt müssen wir die drawScene() Funktion ändern, um die aktuelle Rotation auf das Quadrat anzuwenden, wenn dies gezeichnet werden soll. Nach der Umrechnung der Startposition für das Quadrat, wenden wir eine Rotation wie diese an:

- -
mvPushMatrix();
-mvRotate(squareRotation, [1, 0, 1]);
-
- -

Das speichert die aktuelle Model-View Matrix und rotiert die Matrix mit dem aktuellen Wert von squareRotation um die X und Z Achsen.

- -

Nach dem Zeichen müssen wir die Originalmatrix wiederherstellen:

- -
mvPopMatrix();
-
- -

Wir speichern und stellen die Originalmatrix dann wieder her, um zu verhindern, dass die Rotation auf andere Objekte angewendet wird, die wir vielleicht später noch zeichnen wollen.

- -

Um letztendlich etwas zu animieren, brauchen wir noch ein paar Zeilen Code, welcher den Wert von squareRotation über die Zeit verändert. Dafür erstellen wir eine neue Variable, die die Zeit aufzeichnet, welche wir zuletzt animiert haben (wir nennen diese lastSquareUpdateTime), dann fügen wir den folgenden Code an das Ende der drawScene() Funktion:

- -
var currentTime = (new Date).getTime();
-if (lastSquareUpdateTime) {
-  var delta = currentTime - lastSquareUpdateTime;
-  squareRotation += (30 * delta) / 1000.0;
-}
-lastSquareUpdateTime = currentTime;
-
- -

Dieser Code benutzt den Betrag der Zeit, die vergangen ist, seitdem wir zum letzten Mal den Wert von squareRotation geändert haben, um festzustellen wie weit das Quadrat rotiert werden soll.

- -

Das Quadrat bewegen

- -

Wir können das Quadrat auch verschieben indem wir eine unterschiedliche Position berechnen lassen, bevor wir es zeichnen. Dieses Beispiel zeigt wie eine grundlegende Animation gemacht werden kann. Allerdings möchten Sie in einer echten Anwendung wohl eher etwas weniger Irrsinniges machen.

- -

Verfolgen wir die Abstände zu jeder Achse für unsere Verschiebung in drei neuen Variablen:

- -
var squareXOffset = 0.0;
-var squareYOffset = 0.0;
-var squareZOffset = 0.0;
-
- -

Und den Betrag, welcher unsere Postion auf jeder Achse verändern soll, in diesen Variablen:

- -
var xIncValue = 0.2;
-var yIncValue = -0.4;
-var zIncValue = 0.3;
-
- -

Nun können wir einfach diesen Code zum vorherigen Code, der die Rotation aktualisiert, hinzufügen:

- -
squareXOffset += xIncValue * ((30 * delta) / 1000.0);
-squareYOffset += yIncValue * ((30 * delta) / 1000.0);
-squareZOffset += zIncValue * ((30 * delta) / 1000.0);
-
-if (Math.abs(squareYOffset) > 2.5) {
-  xIncValue = -xIncValue;
-  yIncValue = -yIncValue;
-  zIncValue = -zIncValue;
-}
-
- -

Das bringt unser Quadrat dazu seine Größe zu verändern, sich willkürlich auf der Fläche zu verschieben, sich vom Betrachter weg und zum Betrachter hin zu bewegen und das alles während das Quadrat auch noch rotiert. Das sieht dann schon eher wie ein Bildschirmschoner aus.

- -

Wenn Ihr Browser WebGL unterstützt, ist hier das Beispiel in Aktion.

- -

Weitere Matrixoperationen

- -

Dieses Beispiel verwendet einige zusätzliche Matrixoperationen, darunter zwei Routinen, die die Matrizen verschieben, wiederherstellen und in einem Stack aufbewahren und eine Routine, die die Matrix um eine gewissen Anzahl von Grad rotiert:

- -
var mvMatrixStack = [];
-
-function mvPushMatrix(m) {
-  if (m) {
-    mvMatrixStack.push(m.dup());
-    mvMatrix = m.dup();
-  } else {
-    mvMatrixStack.push(mvMatrix.dup());
-  }
-}
-
-function mvPopMatrix() {
-  if (!mvMatrixStack.length) {
-    throw("Can't pop from an empty matrix stack.");
-  }
-
-  mvMatrix = mvMatrixStack.pop();
-  return mvMatrix;
-}
-
-function mvRotate(angle, v) {
-  var inRadians = angle * Math.PI / 180.0;
-
-  var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4();
-  multMatrix(m);
-}
-
- -

Diese Routinen wurden von einem Beispiel ausgeliehen, welches von Vlad Vukićević geschrieben wurde.

- -

{{PreviousNext("Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen", "Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen") }}

diff --git a/files/de/web/api/webgl_api/tutorial/texturen_in_webgl_verwenden/index.html b/files/de/web/api/webgl_api/tutorial/texturen_in_webgl_verwenden/index.html deleted file mode 100644 index eb59417694..0000000000 --- a/files/de/web/api/webgl_api/tutorial/texturen_in_webgl_verwenden/index.html +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Texturen in WebGL verwenden -slug: Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden -tags: - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen", "Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

- -

Jetzt wo unser Beispielprogramm über einen rotierenden 3D-Würfel verfügt, wollen wir darauf eine Textur legen, statt der bisher verwendeten, einfachen Farben.

- -

Texturen laden

- -

Als Erstes müssen wir ein paar Zeilen Code hinzufügen, um die Texturen zu laden. In unserem Fall werden wir eine einzige Textur verwenden, die auf alle sechs Seiten des rotierenden Würfels gelegt wird, aber die gleiche Technik kann verwendet werden, um jede beliebig viele Texturen auf ein Objekt zu legen.

- -

Der Code, der die Textur lädt, sieht so aus:

- -
function initTextures() {
-  gl.enable(gl.TEXTURE_2D);
-  cubeTexture = gl.createTexture();
-  cubeImage = new Image();
-  cubeImage.onload = function() { handleTextureLoaded(cubeImage, cubeTexture); }
-  cubeImage.src = "cubetexture.png";
-}
-
-function handleTextureLoaded(image, texture) {
-  gl.bindTexture(gl.TEXTURE_2D, texture);
-  gl.texImage2D(gl.TEXTURE_2D, 0, image, true);
-  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
-  gl.generateMipmap(gl.TEXTURE_2D);
-  gl.bindTexture(gl.TEXTURE_2D, null);
-}
-
- -

Die Routine initTextures() aktiviert zunächst die Unterstützung von Texturen, dann wird das GL Textur-Objekt cubeTexture durch Aufruf der GL createTexture() Funktion erstellt. Um die Textur von der Bilddatei zu laden, wird dann ein Image-Objekt erstellt und in die Grafikdatei geladen, die wir als unsere Textur verwenden wollen. Die handleTextureLoaded() Callback-Routine wird ausgeführt, wenn das Bild geladen wurde.

- -

Um schließlich die Textur zu erstellen, legen wir fest, dass die neue Textur die aktuelle Textur ist, mit welcher wir arbeiten wollen und verbinden diese mit gl.TEXTURE_2D. Danach wird das geladene Bild mit texImage2D() die Bilddaten in die Textur schreiben.

- -

Die nächsten zwei Zeilen legen Filter für die Textur fest, die steuern, wie das Bild gefiltert wird, wenn es skaliert wird. In diesem Fall verwenden wir lineare Filter, wenn das Bild hoch skaliert wird und Mip-Mapping wenn wir herunter skalieren. Dann wird die Mip-Map generiert, indem generateMipMap() aufgerufen wird. Schließlich teilen wir WebGL mit, dass wir mit der Arbeit an der Textur fertig sind, in dem wir null mit gl.TEXTURE_2D verknüpfen.

- -

Textur auf die Flächen legen

- -

Nun ist die Textur geladen und bereit eingesetzt zu werden. Bevor wir die Textur aber verwenden können, müssen wir die Texturkoordinaten auf die Vertices der Flächen des Würfels legen. Das ersetzt den vorherigen Code in initBuffers(), der die Farben für jede Fläche festgelegt hat.

- -
  cubeVerticesTextureCoordBuffer = gl.createBuffer();
-  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
-
-  var textureCoordinates = [
-    // vorne
-    0.0,  0.0,
-    1.0,  0.0,
-    1.0,  1.0,
-    0.0,  1.0,
-    // hinten
-    0.0,  0.0,
-    1.0,  0.0,
-    1.0,  1.0,
-    0.0,  1.0,
-    // oben
-    0.0,  0.0,
-    1.0,  0.0,
-    1.0,  1.0,
-    0.0,  1.0,
-    // unten
-    0.0,  0.0,
-    1.0,  0.0,
-    1.0,  1.0,
-    0.0,  1.0,
-    // rechts
-    0.0,  0.0,
-    1.0,  0.0,
-    1.0,  1.0,
-    0.0,  1.0,
-    // links
-    0.0,  0.0,
-    1.0,  0.0,
-    1.0,  1.0,
-    0.0,  1.0
-  ];
-
-  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(textureCoordinates),
-                gl.STATIC_DRAW);
-
- -

Zuerst erstellt dieser Code einen GL-Buffer in welchen wir die Texturkoordinaten für jede Fläche speichern werden, dann verknüpfen wir diesen Buffer als das Array in welchen wir schreiben werden.

- -

Das textureCoordinates Array definiert die Texturkoordinaten entsprechend jedem Vertex von jeder Fläche. Beachten Sie, dass die Texturkoordinaten sich im Bereich von 0.0 bis 1.0 befinden. Beim Texturmapping werden die Dimensionen von Texturen immer auf einen Bereich von 0.0 bis 1.0 normiert, egal welche Größe die Textur wirklich hat.

- -

Sobald wir das Texturmapping-Array erstellt haben, speichern wir das Array in den Buffer, sodass GL die Daten zur Verfügung hat.

- -

Die Shader aktualisieren

- -

Das Shader-Programm - und der Code, der die Shader initialisiert - muss aktualisiert werden, damit die Textur anstatt der Farben verwendet wird.

- -

Werfen wir zunächst einen Blick auf die einfache Änderung, die in initShaders() benötigt wird:

- -
  textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
-  gl.enableVertexAttribArray(textureCoordAttribute);
-
- -

Das ersetzt den Code, der die Vertex Farbattribute enthielt, mit dem, der nun die Texturkoordinaten für jeden Vertex enthält.

- -

Der Vertex-Shader

- -

Als Nächstes müssen wir den Vertex-Shader ersetzen, sodass statt der Farbdaten die Texturkoordinaten abgerufen werden.

- -
    <script id="shader-vs" type="x-shader/x-vertex">
-      attribute vec3 aVertexPosition;
-      attribute vec2 aTextureCoord;
-
-      uniform mat4 uMVMatrix;
-      uniform mat4 uPMatrix;
-
-      varying vec2 vTextureCoord;
-
-      void main(void) {
-        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
-        vTextureCoord = aTextureCoord;
-      }
-    </script>
-
- -

Die wichtigste Änderung ist hier, dass anstatt die Vertex-Farbe abzurufen, die Texturkoordinaten gesetzt werden. Das gibt den Ort der Textur entsprechend zum Vertex an.

- -

Der Fragment-Shader

- -

Der Fragment-Shader muss in ähnlicher Weise geändert werden:

- -
    <script id="shader-fs" type="x-shader/x-fragment">
-      varying vec2 vTextureCoord;
-
-      uniform sampler2D uSampler;
-
-      void main(void) {
-        gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
-      }
-    </script>
-
- -

Anstatt einen Farbwert auf die Fragment-Farbe zu legen, wird die Fragment-Farbe berechnet in dem der texel (der Pixel innerhalb der Textur) abgerufen wird, der am Besten auf die Fragement-Position laut dem Sampler passt.

- -

Zeichnen des textuierten Würfels

- -

Die Änderungen an der drawScene() Funktion sind einfach (mit der Ausnahme, dass ich nun zur besseren Anschaulichkeit die Verschiebungen entfernt habe und der Würfel nur noch rotiert wird).

- -

Der Code, der die Farben auf die Textur legt ist weg und wurde ersetzt:

- -
  gl.activeTexture(gl.TEXTURE0);
-  gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
-  gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);
-
- -

GL ermöglicht 32 Textur-Register; der Erste davon ist gl.TEXTURE0. Wir verknüpfen unsere geladene Textur zu diesem Register, dann wird der Shader-Sampler uSampler gesetzt (im Shader-Program festgelegt), um die Textur zu benutzen.

- -

Jetzt sollte der rotierende Würfel gut anzuschauen zu sein. Wenn Ihr Browser WebGL unterstützt, können Sie das Live-Beispiel ausprobieren.

- -

{{PreviousNext("Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen", "Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

diff --git a/files/de/web/api/webgl_api/tutorial/using_shaders_to_apply_color_in_webgl/index.html b/files/de/web/api/webgl_api/tutorial/using_shaders_to_apply_color_in_webgl/index.html new file mode 100644 index 0000000000..94068822d5 --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/using_shaders_to_apply_color_in_webgl/index.html @@ -0,0 +1,97 @@ +--- +title: Farben mittels Shader in einen WebGL-Kontext hinzufügen +slug: >- + Web/API/WebGL_API/Tutorial/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext", "Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren")}}

+ +

Wir haben ein simples Quadrat im vorherigen Teil erstellt, im nächsten Schritt wollen wir ein bisschen Farbe ins Spiel bringen. Dafür sind Änderungen an den Shadern erforderlich.

+ +

Farben zu den Eckpunkten hinzufügen

+ +

In der Computergrafik werden Objekte mit einer Reihe von Punkten erstellt. Jeder Punkt hat eine Position und eine Farbe. Standardmäßig werden alle anderen Pixelfarben (und alle weiteren Attribute, darunter die Position) über eine lineare Interpolation berechnet, die automatisch glatte Verläufe erstellt. Vorher hat unser Vertex-Shader keine festgelegten Farben auf die Punkte (Vertices) angewendet und der Fragment-Shader legte die feste Farbe weiß für jeden Pixel fest, sodass das gesamte Quadrat komplett weiß gezeichnet wurde.

+ +

Nun wollen wir in jeder Ecke des Quadrats einen Verlauf in einer unterschiedlichen Farbe rendern: rot, blau, grün und weiß. Als erstes sollten wir daher diese Farben in den vier Eckpunkten einrichten. Um das zu machen, müssen wir zunächst einen Array der Vertex-Farben erstellen und diesen dann in einen WebGL Buffer speichern. Das erreichen wir durch die folgenden Zeilen in unserer initBuffers() Funktion:

+ +
var colors = [
+  1.0,  1.0,  1.0,  1.0,    // weiß
+  1.0,  0.0,  0.0,  1.0,    // rot
+  0.0,  1.0,  0.0,  1.0,    // grün
+  0.0,  0.0,  1.0,  1.0     // blau
+];
+
+squareVerticesColorBuffer = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
+}
+
+ +

Dieser Code definiert zuerst einen JavaScript Array, welcher die vier Farb-Vektoren mit jeweils vier Werten für die jeweilige Farbe enthält. Dann wird ein neuer WebGL Buffer angewiesen diese Farben zu speichern und der Array wird in WebGL Floats konvertiert und im Buffer gespeichert.

+ +

Um die Farben schließlich zu verwenden, muss der der Vertex-Shader aktualisiert werden, um die entsprechende Farbe vom Farb-Buffer zu erhalten:

+ +
<script id="shader-vs" type="x-shader/x-vertex">
+  attribute vec3 aVertexPosition;
+  attribute vec4 aVertexColor;
+
+  uniform mat4 uMVMatrix;
+  uniform mat4 uPMatrix;
+
+  varying vec4 vColor;
+
+  void main(void) {
+    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+    vColor = aVertexColor;
+  }
+</script>
+
+ +

Der wichtigste Unterschied ist hier, dass wir für jeden Punkt (Vertex) die Farbe entsprechend des Farb-Arrays setzen.

+ +

Die Fragmente mit Farben versehen

+ +

Als Erinnerung, so sah unser Fragment-Shader vorher aus:

+ +
<script id="shader-fs" type="x-shader/x-fragment">
+  void main(void) {
+    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
+  }
+</script>
+
+ +

Um nun die interpolierte Farbe für jeden Pixel aufzugreifen, müssen wir dies einfach so ändern, dass wir den Wert der vColor Variable erhalten:

+ +
<script id="shader-fs" type="x-shader/x-fragment">
+  varying vec4 vColor;
+
+  void main(void) {
+    gl_FragColor = vColor;
+  }
+</script>
+
+ +

Das ist eine sehr einfache Änderung. Anstatt des festen Wertes vorher, erhält jedes Fragment jetzt einfach die interpolierte Farbe, basierend auf der Position relativ zu den Kontenpunkten (Vertices).

+ +

Mit den Farben zeichnen

+ +

Als nächstes ist es nötig, Code zur initShaders() Routine hinzuzufügen, um das Farbattribut für das Shader-Programm zu initialisieren:

+ +
  vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
+  gl.enableVertexAttribArray(vertexColorAttribute);
+
+ +

Dann wird drawScene() abgeändert, um schließlich die Farben zu verwenden, wenn das Quadrat gezeichnet wird:

+ +
  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
+  gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
+
+ +

Jetzt sollten Sie das Beispiel in einem WebGL kompatiblen Browser sehen und Ihre Ausgabe sollte so wie im Bild unten gezeichnet werden (das Quadrat ist innerhalb eines schwarzen Felds zu sehen):

+ +

screenshot.png

+ +

{{PreviousNext("Web/API/WebGL_API/Tutorial/Hinzufügen_von_2D_Inhalten_in_einen_WebGL-Kontext", "Web/API/WebGL_API/Tutorial/Objekte_mit_WebGL_animieren")}}

diff --git a/files/de/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html b/files/de/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html new file mode 100644 index 0000000000..eb59417694 --- /dev/null +++ b/files/de/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html @@ -0,0 +1,159 @@ +--- +title: Texturen in WebGL verwenden +slug: Web/API/WebGL_API/Tutorial/Texturen_in_WebGL_verwenden +tags: + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen", "Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

+ +

Jetzt wo unser Beispielprogramm über einen rotierenden 3D-Würfel verfügt, wollen wir darauf eine Textur legen, statt der bisher verwendeten, einfachen Farben.

+ +

Texturen laden

+ +

Als Erstes müssen wir ein paar Zeilen Code hinzufügen, um die Texturen zu laden. In unserem Fall werden wir eine einzige Textur verwenden, die auf alle sechs Seiten des rotierenden Würfels gelegt wird, aber die gleiche Technik kann verwendet werden, um jede beliebig viele Texturen auf ein Objekt zu legen.

+ +

Der Code, der die Textur lädt, sieht so aus:

+ +
function initTextures() {
+  gl.enable(gl.TEXTURE_2D);
+  cubeTexture = gl.createTexture();
+  cubeImage = new Image();
+  cubeImage.onload = function() { handleTextureLoaded(cubeImage, cubeTexture); }
+  cubeImage.src = "cubetexture.png";
+}
+
+function handleTextureLoaded(image, texture) {
+  gl.bindTexture(gl.TEXTURE_2D, texture);
+  gl.texImage2D(gl.TEXTURE_2D, 0, image, true);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
+  gl.generateMipmap(gl.TEXTURE_2D);
+  gl.bindTexture(gl.TEXTURE_2D, null);
+}
+
+ +

Die Routine initTextures() aktiviert zunächst die Unterstützung von Texturen, dann wird das GL Textur-Objekt cubeTexture durch Aufruf der GL createTexture() Funktion erstellt. Um die Textur von der Bilddatei zu laden, wird dann ein Image-Objekt erstellt und in die Grafikdatei geladen, die wir als unsere Textur verwenden wollen. Die handleTextureLoaded() Callback-Routine wird ausgeführt, wenn das Bild geladen wurde.

+ +

Um schließlich die Textur zu erstellen, legen wir fest, dass die neue Textur die aktuelle Textur ist, mit welcher wir arbeiten wollen und verbinden diese mit gl.TEXTURE_2D. Danach wird das geladene Bild mit texImage2D() die Bilddaten in die Textur schreiben.

+ +

Die nächsten zwei Zeilen legen Filter für die Textur fest, die steuern, wie das Bild gefiltert wird, wenn es skaliert wird. In diesem Fall verwenden wir lineare Filter, wenn das Bild hoch skaliert wird und Mip-Mapping wenn wir herunter skalieren. Dann wird die Mip-Map generiert, indem generateMipMap() aufgerufen wird. Schließlich teilen wir WebGL mit, dass wir mit der Arbeit an der Textur fertig sind, in dem wir null mit gl.TEXTURE_2D verknüpfen.

+ +

Textur auf die Flächen legen

+ +

Nun ist die Textur geladen und bereit eingesetzt zu werden. Bevor wir die Textur aber verwenden können, müssen wir die Texturkoordinaten auf die Vertices der Flächen des Würfels legen. Das ersetzt den vorherigen Code in initBuffers(), der die Farben für jede Fläche festgelegt hat.

+ +
  cubeVerticesTextureCoordBuffer = gl.createBuffer();
+  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
+
+  var textureCoordinates = [
+    // vorne
+    0.0,  0.0,
+    1.0,  0.0,
+    1.0,  1.0,
+    0.0,  1.0,
+    // hinten
+    0.0,  0.0,
+    1.0,  0.0,
+    1.0,  1.0,
+    0.0,  1.0,
+    // oben
+    0.0,  0.0,
+    1.0,  0.0,
+    1.0,  1.0,
+    0.0,  1.0,
+    // unten
+    0.0,  0.0,
+    1.0,  0.0,
+    1.0,  1.0,
+    0.0,  1.0,
+    // rechts
+    0.0,  0.0,
+    1.0,  0.0,
+    1.0,  1.0,
+    0.0,  1.0,
+    // links
+    0.0,  0.0,
+    1.0,  0.0,
+    1.0,  1.0,
+    0.0,  1.0
+  ];
+
+  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(textureCoordinates),
+                gl.STATIC_DRAW);
+
+ +

Zuerst erstellt dieser Code einen GL-Buffer in welchen wir die Texturkoordinaten für jede Fläche speichern werden, dann verknüpfen wir diesen Buffer als das Array in welchen wir schreiben werden.

+ +

Das textureCoordinates Array definiert die Texturkoordinaten entsprechend jedem Vertex von jeder Fläche. Beachten Sie, dass die Texturkoordinaten sich im Bereich von 0.0 bis 1.0 befinden. Beim Texturmapping werden die Dimensionen von Texturen immer auf einen Bereich von 0.0 bis 1.0 normiert, egal welche Größe die Textur wirklich hat.

+ +

Sobald wir das Texturmapping-Array erstellt haben, speichern wir das Array in den Buffer, sodass GL die Daten zur Verfügung hat.

+ +

Die Shader aktualisieren

+ +

Das Shader-Programm - und der Code, der die Shader initialisiert - muss aktualisiert werden, damit die Textur anstatt der Farben verwendet wird.

+ +

Werfen wir zunächst einen Blick auf die einfache Änderung, die in initShaders() benötigt wird:

+ +
  textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
+  gl.enableVertexAttribArray(textureCoordAttribute);
+
+ +

Das ersetzt den Code, der die Vertex Farbattribute enthielt, mit dem, der nun die Texturkoordinaten für jeden Vertex enthält.

+ +

Der Vertex-Shader

+ +

Als Nächstes müssen wir den Vertex-Shader ersetzen, sodass statt der Farbdaten die Texturkoordinaten abgerufen werden.

+ +
    <script id="shader-vs" type="x-shader/x-vertex">
+      attribute vec3 aVertexPosition;
+      attribute vec2 aTextureCoord;
+
+      uniform mat4 uMVMatrix;
+      uniform mat4 uPMatrix;
+
+      varying vec2 vTextureCoord;
+
+      void main(void) {
+        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+        vTextureCoord = aTextureCoord;
+      }
+    </script>
+
+ +

Die wichtigste Änderung ist hier, dass anstatt die Vertex-Farbe abzurufen, die Texturkoordinaten gesetzt werden. Das gibt den Ort der Textur entsprechend zum Vertex an.

+ +

Der Fragment-Shader

+ +

Der Fragment-Shader muss in ähnlicher Weise geändert werden:

+ +
    <script id="shader-fs" type="x-shader/x-fragment">
+      varying vec2 vTextureCoord;
+
+      uniform sampler2D uSampler;
+
+      void main(void) {
+        gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
+      }
+    </script>
+
+ +

Anstatt einen Farbwert auf die Fragment-Farbe zu legen, wird die Fragment-Farbe berechnet in dem der texel (der Pixel innerhalb der Textur) abgerufen wird, der am Besten auf die Fragement-Position laut dem Sampler passt.

+ +

Zeichnen des textuierten Würfels

+ +

Die Änderungen an der drawScene() Funktion sind einfach (mit der Ausnahme, dass ich nun zur besseren Anschaulichkeit die Verschiebungen entfernt habe und der Würfel nur noch rotiert wird).

+ +

Der Code, der die Farben auf die Textur legt ist weg und wurde ersetzt:

+ +
  gl.activeTexture(gl.TEXTURE0);
+  gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
+  gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);
+
+ +

GL ermöglicht 32 Textur-Register; der Erste davon ist gl.TEXTURE0. Wir verknüpfen unsere geladene Textur zu diesem Register, dann wird der Shader-Sampler uSampler gesetzt (im Shader-Program festgelegt), um die Textur zu benutzen.

+ +

Jetzt sollte der rotierende Würfel gut anzuschauen zu sein. Wenn Ihr Browser WebGL unterstützt, können Sie das Live-Beispiel ausprobieren.

+ +

{{PreviousNext("Web/API/WebGL_API/Tutorial/3D-Objekte_mit_WebGL_erstellen", "Web/API/WebGL_API/Tutorial/Beleuchtung_in_WebGL")}}

diff --git a/files/de/web/api/websockets_api/index.html b/files/de/web/api/websockets_api/index.html new file mode 100644 index 0000000000..1e3cefa7d9 --- /dev/null +++ b/files/de/web/api/websockets_api/index.html @@ -0,0 +1,193 @@ +--- +title: WebSockets +slug: WebSockets +tags: + - Referenz + - WebSockets +translation_of: Web/API/WebSockets_API +--- +

WebSockets ist eine fortschrittliche Technologie welche es möglich macht eine interaktive Kommunikations-Session zwischen dem Browser des Benutzers und dem Server herzustellen. Mit dieser API können Sie Nachrichten zum Server senden und ereignisorientierte Antworten erhalten ohne beim Server die Antwort abzufragen.

+ +
+
+

Dokumentation

+ +
+
Websocket Client-Anwendungen schreiben
+
Eine Anleitung um WebSocket-Clients im Browser laufen zu lassen.
+
WebSockets Referenz
+
Eine Referenz für die Client-seitige WebSocket API.
+
Websocket-Server schreiben (Benötigt Inhalt)
+
Eine Anleitung zum schreiben von Server-seitigem Code um das WebSocket-Protokoll zu handhaben.
+
+ +

Alle zeigen...

+
+ +
+

Tools

+ + + + + + +
+
+ +

Siehe auch

+ + + +

Browser-Kompatibilität

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Version -76  {{obsolete_inline}}6{{CompatGeckoDesktop("2.0")}}{{CompatNo}}11.00 (disabled)5.0.1
Protokol-Version 7 {{obsolete_inline}}{{CompatNo}}{{CompatGeckoDesktop("6.0")}}
+ {{property_prefix("Moz")}}
{{CompatNo}}{{CompatNo}}{{CompatNo}}
Protokol-Version 10 {{obsolete_inline}}14{{CompatGeckoDesktop("7.0")}}
+ {{property_prefix("Moz")}}
HTML5 Labs{{CompatUnknown}}{{CompatUnknown}}
Standard - RFC 6455 Support16{{CompatGeckoDesktop("11.0")}}1012.106.0
Benutzbarkeit in Workern{{CompatVersionUnknown}}{{CompatGeckoDesktop("37.0")}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Version -76
+ {{obsolete_inline}}
{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
Protokol-Version 7
+ {{obsolete_inline}}
{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
Protokol-Version 8 (IETF-Entwurf 10) {{obsolete_inline}}{{CompatUnknown}}{{CompatGeckoMobile("7.0")}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
Standard - RFC 6455 Support4.4{{CompatGeckoDesktop("11.0")}}{{CompatUnknown}}12.106.0
Benutzbarkeit in Workern{{CompatVersionUnknown}}{{CompatGeckoMobile("37.0")}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
+ +

Hinweise zu Gecko

+ +

Die WebSocket-Unterstützung in Firefox wird weiterhin die Entwicklungen in der entstehenden WebSocket-Spezifikation verfolgen. Firefox 6 implementiert Version 7 des darunterliegenden Protokolls, währen Firefox 7 Version 8 implementiert (lt. IETF-Vorlage 10). Firefox mobile erhielt in Version 7.0 unterstützung für WebSockets.

+ +

Gecko 6.0

+ +

Vor Gecko 6.0 {{geckoRelease("6.0")}} gab es, fälschlicherweise, ein WebSocket-Objekt. Mane Seiten schlossen daraus, dass WebSocket-Dienste keine Präfixe haben; dieses Objekt wurde mittlerweile zu MozWebSocket umbenannt.

+ +

Gecko 7.0

+ +

Beginnend mit Gecko 7.0 {{geckoRelease("7.0")}} wird die Einstellung network.websocket.max-connections genutzt, um die maximale Anzahl von WebSockets festzustellen, die gleichzeitig genutzt werden können. Der Standardwert ist 200.

+ +

Gecko 8.0

+ +

Beginnend mit Gecko 8.0 {{geckoRelease("8.0")}} wird die Deflate-Stream-Erweiterung des WebSocket-Protokolls abgeschaltet, da es von den Spezifikations-Vorlagen überholt wurde. Dies löst Inkompatibilitäten mit manchen Seiten.

+ +

Gecko 11.0

+ +

Vor Gecko 11.0 waren sowohl eingehende als auch ausgehende Nachrichten auf 16 MB begrenzt. Sie können jetzt bis zu 2 GB groß sein. Beachten Sie, dass Speicherbegrenzungen (insbesondere auf mobilen Geräten) ein theoretisches Maximum darstellen, jedoch kein praktisches. In Wirklichkeit werden Übertragungen dieser Größe auf Geräten, die nicht über genügend Speicher verfügen, fehlschlagen.

+ +

Zusätzlich wurde eine ArrayBuffer Sende- und Empfangsunterstützung für Binärdaten implementiert.

+ +

Ab Gecko 11.0 steht die WebSocket-API ohne Präfixe zur Verfügung.

diff --git a/files/de/web/api/websockets_api/writing_websocket_servers/index.html b/files/de/web/api/websockets_api/writing_websocket_servers/index.html new file mode 100644 index 0000000000..031a2b832a --- /dev/null +++ b/files/de/web/api/websockets_api/writing_websocket_servers/index.html @@ -0,0 +1,250 @@ +--- +title: Writing WebSocket servers +slug: WebSockets/Writing_WebSocket_servers +translation_of: Web/API/WebSockets_API/Writing_WebSocket_servers +--- +
{{APIRef("Websockets API")}}
+ +

Ein WebSocket-Server ist nichts anderes als eine Anwendung, die einen Port eines TCP-Servers überwacht, der einem bestimmten Protokoll folgt. Die Aufgabe, einen benutzerdefinierten Server zu erstellen, macht den Leuten Angst. Es kann jedoch unkompliziert sein, einen einfachen WebSocket-Server auf einer Plattform Ihrer Wahl zu implementieren.

+ +

Ein WebSocket-Server kann in jeder serverseitigen Programmiersprache geschrieben werden, die dazu in der Lage ist: Berkeley sockets, siehe auch C(++), Python, PHP, oder Serverseitiges JavaScript.

+ +

Dies ist kein Tutorial in einer bestimmten Sprache, sondern dient als Leitfaden, um das Schreiben Ihres eigenen Servers zu erleichtern.

+ +

In diesem Artikel wird davon ausgegangen, dass Sie bereits mit der Funktionsweise von {{Glossary ("HTTP")}} vertraut sind und über ein moderates Programmiererlebnis verfügen. Abhängig von der Sprachunterstützung sind möglicherweise Kenntnisse über TCP-Sockets erforderlich. In diesem Handbuch wird das Mindestwissen dargestellt, das Sie zum Schreiben eines WebSocket-Servers benötigen.

+ +
+

Notiz: Lesen Sie die neueste offizielle WebSockets-Spezifikation, RFC 6455. Die Abschnitte 1 und 4-7 sind für Server-Implementierer besonders interessant. In Abschnitt 10 wird die Sicherheit erläutert, und Sie sollten sie unbedingt lesen, bevor Sie Ihren Server verfügbar machen.

+
+ +

Ein WebSocket-Server wird hier auf sehr niedriger Ebene erklärt. WebSocket-Server sind häufig separate und spezialisierte Server (aus Gründen des Lastenausgleichs oder aus anderen praktischen Gründen). Daher verwenden Sie häufig einen Reverse-Proxy (z. B. einen normalen HTTP-Server), um WebSocket-Handshakes zu erkennen, vorzuverarbeiten und an diese Clients zu senden ein echter WebSocket-Server. Dies bedeutet, dass Sie Ihren Servercode nicht mit Cookie- und Authentifizierungshandlern (zum Beispiel) aufblähen müssen.

+ +

Der WebSocket-Handshake

+ +

Zunächst muss der Server mithilfe eines Standard-TCP-Sockets auf eingehende Socket-Verbindungen warten. Abhängig von Ihrer Plattform kann dies automatisch für Sie erledigt werden. Angenommen, Ihr Server überwacht example.com, Port 8000, und Ihr Socket-Server antwortet auf {{HTTPMethod ("GET")}} -Anfragen unter example.com/chat.

+ +
+

Warnung: Der Server überwacht möglicherweise jeden von ihm ausgewählten Port. Wenn er jedoch einen anderen Port als 80 oder 443 auswählt, kann es zu Problemen mit Firewalls und / oder Proxys kommen. Browser benötigen im Allgemeinen eine sichere Verbindung für WebSockets, obwohl sie möglicherweise eine Ausnahme für lokale Geräte bieten.

+
+ +

Der Handshake ist das "Web" in WebSockets. Es ist die Brücke von HTTP zu WebSockets. Beim Handshake werden Details der Verbindung ausgehandelt, und jede Partei kann vor Abschluss zurücktreten, wenn die Bedingungen ungünstig sind. Der Server muss darauf achten, alles zu verstehen, was der Client verlangt, da sonst Sicherheitsprobleme auftreten können.

+ +
+

Tipp: Die Anfrage-URL (/ Chat hier) hat in der Spezifikation keine definierte Bedeutung. Viele Benutzer verwenden es daher, damit ein Server mehrere WebSocket-Anwendungen verarbeiten kann. Zum Beispiel könnte example.com/chat eine Mehrbenutzer-Chat-App aufrufen, während /game auf demselben Server möglicherweise ein Multiplayer-Spiel aufruft.

+
+ +

Client handshake Anfrage

+ +

Auch wenn Sie einen Server erstellen, muss ein Client den WebSocket-Handshake-Prozess starten, indem er den Server kontaktiert und eine WebSocket-Verbindung anfordert. Sie müssen also wissen, wie Sie die Anfrage des Kunden interpretieren. Der Client sendet eine ziemlich normale HTTP-Anfrage mit Headern, die so aussehen (die HTTP-Version muss 1.1 oder höher sein und die Methode muss GET sein):

+ +
GET /chat HTTP/1.1
+Host: example.com:8000
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Sec-WebSocket-Version: 13
+
+
+ +

Der Client kann hier Erweiterungen und / oder Unterprotokolle anfordern. Einzelheiten finden Sie unter Sonstiges. Es können auch allgemeine Header wie {{HTTPHeader ("User-Agent")}}, {{HTTPHeader ("Referer")}}, {{HTTPHeader ("Cookie")}} oder Authentifizierungsheader vorhanden sein. Mach mit denen, was du willst; Sie beziehen sich nicht direkt auf das WebSocket. Es ist auch sicher, sie zu ignorieren. In vielen gängigen Setups hat sich bereits ein Reverse-Proxy mit ihnen befasst.

+ +
+

Tipp: Alle Browser senden einen Origin-Header. Sie können diesen Header aus Sicherheitsgründen verwenden (nach demselben Ursprung suchen, automatisch zulassen oder ablehnen usw.) und eine 403 Forbidden senden, wenn Ihnen das, was Sie sehen, nicht gefällt. Seien Sie jedoch gewarnt, dass Nicht-Browser-Agenten einen gefälschten Ursprung senden können. Die meisten Anwendungen lehnen Anforderungen ohne diesen Header ab.

+
+ +

Wenn ein Header nicht verstanden wird oder einen falschen Wert hat, sollte der Server eine {{HTTPStatus ("400")}} ("Bad Request")} Antwort senden und den Socket sofort schließen. Wie üblich wird möglicherweise auch der Grund angegeben, warum der Handshake im HTTP-Antworttext fehlgeschlagen ist, die Nachricht wird jedoch möglicherweise nie angezeigt (Browser zeigen sie nicht an). Wenn der Server diese Version von WebSockets nicht versteht, sollte er einen {{HTTPHeader ("Sec-WebSocket-Version")}} Header zurücksenden, der die Version (en) enthält, die er versteht. Im obigen Beispiel wird Version 13 des WebSocket-Protokolls angegeben.
+
+ Der interessanteste Header hier ist {{HTTPHeader ("Sec-WebSocket-Key")}}. Schauen wir uns das also als nächstes an.

+ +
+

Hinweis: Normale HTTP-Statuscodes können nur vor dem Handshake verwendet werden. Nach erfolgreichem Handshake müssen Sie einen anderen Satz von Codes verwenden (definiert in Abschnitt 7.4 der Spezifikation).

+
+ +

Server handshake Antwort

+ +

Wenn der Server die Handshake-Anforderung empfängt, sollte er eine spezielle Antwort zurücksenden, die angibt, dass das Protokoll von HTTP zu WebSocket geändert wird. Dieser Header sieht ungefähr so aus (denken Sie daran, dass jede Headerzeile mit \ r \ n endet, und setzen Sie nach dem letzten ein zusätzliches \ r \ n, um das Ende des Headers anzuzeigen):

+ +
HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+
+
+ +

Darüber hinaus kann der Server hier über Erweiterungs- / Unterprotokollanforderungen entscheiden. Einzelheiten finden Sie unter Sonstiges. Der Sec-WebSocket-Accept-Header ist wichtig, da der Server ihn von dem {{HTTPHeader ("Sec-WebSocket-Key")}} ableiten muss, den der Client an ihn gesendet hat. Verketten Sie dazu den Sec-WebSocket-Key des Clients und die Zeichenfolge "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (es handelt sich um eine "magische Zeichenfolge"), nehmen Sie den SHA-1-Hash des Ergebnisses und geben Sie den base64 zurück Codierung dieses Hashs.

+ +
+

Hinweis: Dieser scheinbar überkomplizierte Prozess ist vorhanden, sodass für den Client offensichtlich ist, ob der Server WebSockets unterstützt. Dies ist wichtig, da Sicherheitsprobleme auftreten können, wenn der Server eine WebSockets-Verbindung akzeptiert, die Daten jedoch als HTTP-Anforderung interpretiert.

+
+ +

Wenn der Schlüssel also "dGhlIHNhbXBsZSBub25jZQ ==" war, lautet der Wert des Sec-WebSocket-Accept-Headers "s3pPLMBiTxaQ9kYGzzhZRbK + xOo =". Sobald der Server diese Header sendet, ist der Handshake abgeschlossen und Sie können mit dem Datenaustausch beginnen !

+ +
+

Hinweis: Der Server kann andere Header wie {{HTTPHeader ("Set-Cookie")}} senden oder über andere Statuscodes nach Authentifizierung oder Weiterleitung fragen, bevor er den Antwort-Handshake sendet.

+
+ +

  Clients im Auge behalten

+ +

Dies bezieht sich nicht direkt auf das WebSocket-Protokoll, ist jedoch hier erwähnenswert: Ihr Server muss die Sockets der Clients verfolgen, damit Sie bei Clients, die den Handshake bereits abgeschlossen haben, nicht erneut Handshakes durchführen. Dieselbe Client-IP-Adresse kann mehrmals versuchen, eine Verbindung herzustellen. Der Server kann sie jedoch ablehnen, wenn sie zu viele Verbindungen versuchen, um sich vor Denial-of-Service-Angriffen zu schützen.
+
+ Beispielsweise können Sie eine Tabelle mit Benutzernamen oder ID-Nummern zusammen mit den entsprechenden {{domxref ("WebSocket")}} und anderen Daten führen, die Sie dieser Verbindung zuordnen müssen.

+ +

Exchanging data frames

+ +

Either the client or the server can choose to send a message at any time — that's the magic of WebSockets. However, extracting information from these so-called "frames" of data is a not-so-magical experience. Although all frames follow the same specific format, data going from the client to the server is masked using XOR encryption (with a 32-bit key). Section 5 of the specification describes this in detail.

+ +

Format

+ +

Each data frame (from the client to the server or vice-versa) follows this same format:

+ +
Frame format:
+​​
+      0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-------+-+-------------+-------------------------------+
+     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
+     | |1|2|3|       |K|             |                               |
+     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+     |     Extended payload length continued, if payload len == 127  |
+     + - - - - - - - - - - - - - - - +-------------------------------+
+     |                               |Masking-key, if MASK set to 1  |
+     +-------------------------------+-------------------------------+
+     | Masking-key (continued)       |          Payload Data         |
+     +-------------------------------- - - - - - - - - - - - - - - - +
+     :                     Payload Data continued ...                :
+     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+     |                     Payload Data continued ...                |
+     +---------------------------------------------------------------+
+ +

The MASK bit tells whether the message is encoded. Messages from the client must be masked, so your server must expect this to be 1. (In fact, section 5.1 of the spec says that your server must disconnect from a client if that client sends an unmasked message.) When sending a frame back to the client, do not mask it and do not set the mask bit. We'll explain masking later. Note: You must mask messages even when using a secure socket. RSV1-3 can be ignored, they are for extensions.

+ +

The opcode field defines how to interpret the payload data: 0x0 for continuation, 0x1 for text (which is always encoded in UTF-8), 0x2 for binary, and other so-called "control codes" that will be discussed later. In this version of WebSockets, 0x3 to 0x7 and 0xB to 0xF have no meaning.

+ +

The FIN bit tells whether this is the last message in a series. If it's 0, then the server keeps listening for more parts of the message; otherwise, the server should consider the message delivered. More on this later.

+ +

Decoding Payload Length

+ +

To read the payload data, you must know when to stop reading. That's why the payload length is important to know. Unfortunately, this is somewhat complicated. To read it, follow these steps:

+ +
    +
  1. Read bits 9-15 (inclusive) and interpret that as an unsigned integer. If it's 125 or less, then that's the length; you're done. If it's 126, go to step 2. If it's 127, go to step 3.
  2. +
  3. Read the next 16 bits and interpret those as an unsigned integer. You're done.
  4. +
  5. Read the next 64 bits and interpret those as an unsigned integer. (The most significant bit must be 0.) You're done.
  6. +
+ +

Reading and Unmasking the Data

+ +

If the MASK bit was set (and it should be, for client-to-server messages), read the next 4 octets (32 bits); this is the masking key. Once the payload length and masking key is decoded, you can read that number of bytes from the socket. Let's call the data ENCODED, and the key MASK. To get DECODED, loop through the octets (bytes a.k.a. characters for text data) of ENCODED and XOR the octet with the (i modulo 4)th octet of MASK. In pseudo-code (that happens to be valid JavaScript):

+ +
var DECODED = "";
+for (var i = 0; i < ENCODED.length; i++) {
+    DECODED[i] = ENCODED[i] ^ MASK[i % 4];
+}
+ +

Now you can figure out what DECODED means depending on your application.

+ +

Message Fragmentation

+ +

The FIN and opcode fields work together to send a message split up into separate frames.  This is called message fragmentation. Fragmentation is only available on opcodes 0x0 to 0x2.

+ +

Recall that the opcode tells what a frame is meant to do. If it's 0x1, the payload is text. If it's 0x2, the payload is binary data. However, if it's 0x0, the frame is a continuation frame; this means the server should concatenate the frame's payload to the last frame it received from that client. Here is a rough sketch, in which a server reacts to a client sending text messages. The first message is sent in a single frame, while the second message is sent across three frames. FIN and opcode details are shown only for the client:

+ +
Client: FIN=1, opcode=0x1, msg="hello"
+Server: (process complete message immediately) Hi.
+Client: FIN=0, opcode=0x1, msg="and a"
+Server: (listening, new message containing text started)
+Client: FIN=0, opcode=0x0, msg="happy new"
+Server: (listening, payload concatenated to previous message)
+Client: FIN=1, opcode=0x0, msg="year!"
+Server: (process complete message) Happy new year to you too!
+ +

Notice the first frame contains an entire message (has FIN=1 and opcode!=0x0), so the server can process or respond as it sees fit. The second frame sent by the client has a text payload (opcode=0x1), but the entire message has not arrived yet (FIN=0). All remaining parts of that message are sent with continuation frames (opcode=0x0), and the final frame of the message is marked by FIN=1. Section 5.4 of the spec describes message fragmentation.

+ +

Pings and Pongs: The Heartbeat of WebSockets

+ +

At any point after the handshake, either the client or the server can choose to send a ping to the other party. When the ping is received, the recipient must send back a pong as soon as possible. You can use this to make sure that the client is still connected, for example.

+ +

A ping or pong is just a regular frame, but it's a control frame. Pings have an opcode of 0x9, and pongs have an opcode of 0xA. When you get a ping, send back a pong with the exact same Payload Data as the ping (for pings and pongs, the max payload length is 125). You might also get a pong without ever sending a ping; ignore this if it happens.

+ +
+

If you have gotten more than one ping before you get the chance to send a pong, you only send one pong.

+
+ +

Closing the connection

+ +

To close a connection either the client or server can send a control frame with data containing a specified control sequence to begin the closing handshake (detailed in Section 5.5.1). Upon receiving such a frame, the other peer sends a Close frame in response. The first peer then closes the connection. Any further data received after closing of connection is then discarded. 

+ +

Miscellaneous

+ +
+

WebSocket codes, extensions, subprotocols, etc. are registered at the IANA WebSocket Protocol Registry.

+
+ +

WebSocket extensions and subprotocols are negotiated via headers during the handshake. Sometimes extensions and subprotocols very similar, but there is a clear distinction. Extensions control the WebSocket frame and modify the payload, while subprotocols structure the WebSocket payload and never modify anything. Extensions are optional and generalized (like compression); subprotocols are mandatory and localized (like ones for chat and for MMORPG games).

+ +

Extensions

+ +
+

This section needs expansion. Please edit if you are equipped to do so.

+
+ +

Think of an extension as compressing a file before e-mailing it to someone. Whatever you do, you're sending the same data in different forms. The recipient will eventually be able to get the same data as your local copy, but it is sent differently. That's what an extension does. WebSockets defines a protocol and a simple way to send data, but an extension such as compression could allow sending the same data but in a shorter format.

+ +
+

Extensions are explained in sections 5.8, 9, 11.3.2, and 11.4 of the spec.

+
+ +

TODO

+ +

Subprotocols

+ +

Think of a subprotocol as a custom XML schema or doctype declaration. You're still using XML and its syntax, but you're additionally restricted by a structure you agreed on. WebSocket subprotocols are just like that. They do not introduce anything fancy, they just establish structure. Like a doctype or schema, both parties must agree on the subprotocol; unlike a doctype or schema, the subprotocol is implemented on the server and cannot be externally refered to by the client.

+ +
+

Subprotocols are explained in sections 1.9, 4.2, 11.3.4, and 11.5 of the spec.

+
+ +

A client has to ask for a specific subprotocol. To do so, it will send something like this as part of the original handshake:

+ +
GET /chat HTTP/1.1
+...
+Sec-WebSocket-Protocol: soap, wamp
+
+
+ +

or, equivalently:

+ +
...
+Sec-WebSocket-Protocol: soap
+Sec-WebSocket-Protocol: wamp
+
+
+ +

Now the server must pick one of the protocols that the client suggested and it supports. If there is more than one, send the first one the client sent. Imagine our server can use both soap and wamp. Then, in the response handshake, it sends:

+ +
Sec-WebSocket-Protocol: soap
+
+
+ +
+

The server can't send more than one Sec-Websocket-Protocol header.
+ If the server doesn't want to use any subprotocol, it shouldn't send any Sec-WebSocket-Protocol header. Sending a blank header is incorrect. The client may close the connection if it doesn't get the subprotocol it wants.

+
+ +

If you want your server to obey certain subprotocols, then naturally you'll need extra code on the server. Let's imagine we're using a subprotocol json. In this subprotocol, all data is passed as JSON. If the client solicits this protocol and the server wants to use it, the server needs to have a JSON parser. Practically speaking, this will be part of a library, but the server needs to pass the data around.

+ +
+

Tip: To avoid name conflict, it's recommended to make your subprotocol name part of a domain string. If you are building a custom chat app that uses a proprietary format exclusive to Example Inc., then you might use this: Sec-WebSocket-Protocol: chat.example.com. Note that this isn't required, it's just an optional convention, and you can use any string you wish.

+
+ + + + diff --git a/files/de/web/api/window/domcontentloaded_event/index.html b/files/de/web/api/window/domcontentloaded_event/index.html new file mode 100644 index 0000000000..d6841f774a --- /dev/null +++ b/files/de/web/api/window/domcontentloaded_event/index.html @@ -0,0 +1,156 @@ +--- +title: DOMContentLoaded +slug: Web/Events/DOMContentLoaded +tags: + - Referenz + - Web + - events +translation_of: Web/API/Window/DOMContentLoaded_event +--- +

Das DOMContentLoaded-Event wird ausgelöst, wenn das initiale HTML-Dokument vollständig geladen und geparst ist. Es wird dabei nicht auf Stylesheets, Bilder und Frames gewartet. Das load-Event sollte im Gegensatz dazu nur benutzt werden um eine komplett geladene Seite zu erkennen. Es ist ein weit verbreiteter Fehler das load-Event zu benutzen, obwohl DOMContentLoaded wesentlich besser geeignet wäre.

+ +

{{Note("Synchrones JavaScript pausiert das Parsen des DOM.")}}

+ +

{{Note("Es gibt viele Bibliotheken, die Methoden bereitstellen, um bei verschiedenen Browsern zu erkennen, ob das DOM bereit ist.")}}

+ +

Performanz

+ +

Wenn DOM so schnell wie möglich geladen werden soll, nachdem der Benutzer die Seite angefragt hat, sollte zum einen das JavaScript asynchron ausgeführt und das Laden der Stylesheets optimiert werden, was bei zu häufigem Einsatz den Aufbau der Seite durch das parallele Laden verlangsamen kann.

+ +

Allgemeine Informationen

+ +
+
Specification
+
HTML5
+
Interface
+
Event
+
Bubbles
+
Ja
+
Abbrechbar
+
Ja (Auch wenn es als ein einfaches Event spezifiziert ist, das nicht abgebrochen werden kann)
+
Ziel
+
Dokument
+
Standardaktion
+
Keine
+
+ +

Eigenschaften

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}{{domxref("EventTarget")}}Das Zielelement des Events (das oberste Ziel im DOM Baum).
type {{readonlyInline}}{{domxref("DOMString")}}Der Typ des Events.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Gibt an, ob das Event weiter nach oben wandert (bubble).
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Gibt and, ob das Event abbrechbar ist.
+ +

Beispiel

+ +
<script>
+  document.addEventListener("DOMContentLoaded", function(event) {
+    console.log("DOM fully loaded and parsed");
+  });
+</script>
+
+ +
<script>
+  document.addEventListener("DOMContentLoaded", function(event) {
+    console.log("DOM fully loaded and parsed");
+  });
+
+for(var i=0; i<1000000000; i++)
+{} // Dieses synchrone Script wird das Parsen des DOMs verzögern. Dadurch wird das DOMContentLoaded-Event erst später ausgelöst.
+</script>
+
+ +

Browserkompatibilität

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + + + +
FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support1.0[1]{{CompatVersionUnknown}}{{CompatGeckoDesktop("1")}}[1]9.0[2]9.03.1[1]
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{CompatVersionUnknown}}[1]{{CompatVersionUnknown}}{{CompatGeckoMobile("1")}}[1]{{CompatUnknown}}[2]{{CompatVersionUnknown}}{{CompatVersionUnknown}}[1]
+
+ +

[1] Bubbling für dieses Event benötigt mindestens Gecko 1.9.2, Chrome 6, and Safari 4.

+ +

[2] Internet Explorer 8 unterstützt das  readystatechange-Event, welches genutzt werden kann um festzustellen, wenn das DOM fertig ist. In früheren Versionen des Internet Explorers kann dieser Status festgestellt werden, indem wiederholt versucht wird, document.documentElement.doScroll("left"); auszuführen. Dieser Befehl gibt einen Error zurück, bis das DOM bereit ist.

+ +

Verwandte Events

+ + diff --git a/files/de/web/api/window/load_event/index.html b/files/de/web/api/window/load_event/index.html new file mode 100644 index 0000000000..066a2bea25 --- /dev/null +++ b/files/de/web/api/window/load_event/index.html @@ -0,0 +1,88 @@ +--- +title: load +slug: Web/Events/load +translation_of: Web/API/Window/load_event +--- +

Das load Ereignis wird ausgelöst, sobald eine Ressource und die von ihr abhängigen Ressourcen das Laden beendet haben.

+ +

General info

+ +
+
Spezifikation
+
DOM L3
+
Schnittstelle
+
UIEvent
+
bubbles
+
Nein
+
cancelable
+
Nein
+
Ziel
+
Window,Document,Element
+
Default Action
+
None.
+
+ +

Properties

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}EventTargetThe event target (the topmost target in the DOM tree).
type {{readonlyInline}}DOMStringThe type of event.
bubbles {{readonlyInline}}BooleanWhether the event normally bubbles or not.
cancelable {{readonlyInline}}BooleanWhether the event is cancellable or not.
view {{readonlyInline}}WindowProxydocument.defaultView (window of the document)
detail {{readonlyInline}}long (float)0.
+ +

Example

+ +
<script>
+  window.addEventListener("load", function(event) {
+    console.log("Alle Ressourcen haben das Laden beendet!");
+  });
+</script>
+
+ +

 

+ + + + diff --git a/files/de/web/api/windowbase64/btoa/index.html b/files/de/web/api/windowbase64/btoa/index.html deleted file mode 100644 index 9d980c340a..0000000000 --- a/files/de/web/api/windowbase64/btoa/index.html +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: WindowBase64.btoa() -slug: Web/API/WindowBase64/btoa -tags: - - API - - Méthode - - Referenz -translation_of: Web/API/WindowOrWorkerGlobalScope/btoa ---- -
{{APIRef("HTML DOM")}}
- -

Erzeugt eine Base-64-kodierten ASCII-Zeichenkette aus einer "Zeichenkette" von Binärdaten.

- -

Hinweis: diese Funktion ist nicht für Raw-Unicode-Zeichenketten geeignet (siehe Abschnitt "Unicode-Zeichenketten" unten).

- -

Syntax

- -
var encodedData = window.btoa(stringToEncode);
- -

Example

- -
var encodedData = window.btoa("Hello, world"); // Zeichenkette kodieren
-var decodedData = window.atob(encodedData); // Zeichenkette dekodieren
-
- -

Hinweise

- -

Diese Methode kann verwendet werden, um Daten zu kodieren, übertragen, und mittels {{domxref("WindowBase64.atob","window.atob()")}} wieder zu dekodieren, welche andernfalls Übertragungsprobleme bereiten würden. Beispielsweise ist es möglich, die Steuerzeichen mit den ASCII-Werten 0 bis 31 zu kodieren.

- -

btoa() steht auch in JavaScript implementierten XPCOM-Komponenten zur Verfügung, auch wenn window in solchen Komponenten nicht das globale Objekt ist.

- -

Unicode-Zeichenketten

- -

In den meisten Browsern verursacht ein Aufruf von window.btoa() mit einer Unicode-Zeichenkette eine "Character Out Of Range"-Exception ("Zeichen außerhalb des zulässigen Wertebereichs").

- -

Das kann mithilfe eines solchen Code-Schemas vermieden werden (beigesteuert von Johan Sundström):

- -
function utf8_to_b64(str) {
-    return window.btoa(unescape(encodeURIComponent(str)));
-}
-
-function b64_to_utf8(str) {
-    return decodeURIComponent(escape(window.atob(str)));
-}
-
-// Usage:
-utf8_to_b64('✓ à la mode'); // JTI1dTI3MTMlMjUyMCUyNUUwJTI1MjBsYSUyNTIwbW9kZQ==
-b64_to_utf8('JTI1dTI3MTMlMjUyMCUyNUUwJTI1MjBsYSUyNTIwbW9kZQ=='); // "✓ à la mode"
-
-utf8_to_b64('I \u2661 Unicode!'); // SSUyNTIwJTI1dTI2NjElMjUyMFVuaWNvZGUlMjUyMQ==
-b64_to_utf8('SSUyNTIwJTI1dTI2NjElMjUyMFVuaWNvZGUlMjUyMQ=='); // "I ♡ Unicode!"
-
-
- -

Eine günstigere, zuverlässigere und effizientere Lösung ist, DOMString zunächst in eine UTF-8-kodierte Zeichenkette zu konvertieren, die sich für typed arrays eignet. Eine Anleitung bietet dieser Abschnitt.

- -

Specifications

- - - - - - - - - - - - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#dom-windowbase64-btoa', 'WindowBase64.btoa()')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#dom-windowbase64-btoa', 'WindowBase64.btoa()')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#dom-windowbase64-btoa", "WindowBase64.btoa()")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
- -

Browser compatibility

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatVersionUnknown}}{{CompatGeckoDesktop(1)}}[1]10{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{CompatVersionUnknown}}{{CompatGeckoMobile(1)}}{{CompatNo}}{{CompatUnknown}}{{CompatVersionUnknown}}
-
- -

[1] btoa() is also available to XPCOM components implemented in JavaScript, even though window is not the global object in components.

- -

See also

- - diff --git a/files/de/web/api/windowbase64/index.html b/files/de/web/api/windowbase64/index.html deleted file mode 100644 index f51b72c102..0000000000 --- a/files/de/web/api/windowbase64/index.html +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: WindowBase64 -slug: Web/API/WindowBase64 -tags: - - API - - HTML-DOM - - Helper - - NeedsTranslation - - TopicStub - - WindowBase64 -translation_of: Web/API/WindowOrWorkerGlobalScope -translation_of_original: Web/API/WindowBase64 ---- -

{{APIRef("HTML DOM")}}

- -

The WindowBase64 helper contains utility methods to convert data to and from base64, a binary-to-text encoding scheme. For example it is used in data URIs.

- -

There is no object of this type, though the context object, either the {{domxref("Window")}} for regular browsing scope, or the {{domxref("WorkerGlobalScope")}}  for workers, implements it.

- -

Properties

- -

This helper neither defines nor inherits any properties.

- -

Methods

- -

This helper does not inherit any methods.

- -
-
{{domxref("WindowBase64.atob()")}}
-
Decodes a string of data which has been encoded using base-64 encoding.
-
{{domxref("WindowBase64.btoa()")}}
-
Creates a base-64 encoded ASCII string from a string of binary data.
-
- -

Specifications

- - - - - - - - - - - - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#windowbase64', 'WindowBase64')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#windowbase64', 'WindowBase64')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#windowbase64", "WindowBase64")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
- -

Browser compatibility

- -

{{CompatibilityTable}}

- -
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support{{CompatGeckoDesktop(1)}} [1]{{CompatVersionUnknown}}10.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox Mobile (Gecko)AndroidIE MobileOpera MobileSafari Mobile
Basic support{{CompatGeckoMobile(1)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
- -

[1]  atob() is also available to XPCOM components implemented in JavaScript, even though {{domxref("Window")}} is not the global object in components.

- -

See also

- - diff --git a/files/de/web/api/windoworworkerglobalscope/btoa/index.html b/files/de/web/api/windoworworkerglobalscope/btoa/index.html new file mode 100644 index 0000000000..9d980c340a --- /dev/null +++ b/files/de/web/api/windoworworkerglobalscope/btoa/index.html @@ -0,0 +1,145 @@ +--- +title: WindowBase64.btoa() +slug: Web/API/WindowBase64/btoa +tags: + - API + - Méthode + - Referenz +translation_of: Web/API/WindowOrWorkerGlobalScope/btoa +--- +
{{APIRef("HTML DOM")}}
+ +

Erzeugt eine Base-64-kodierten ASCII-Zeichenkette aus einer "Zeichenkette" von Binärdaten.

+ +

Hinweis: diese Funktion ist nicht für Raw-Unicode-Zeichenketten geeignet (siehe Abschnitt "Unicode-Zeichenketten" unten).

+ +

Syntax

+ +
var encodedData = window.btoa(stringToEncode);
+ +

Example

+ +
var encodedData = window.btoa("Hello, world"); // Zeichenkette kodieren
+var decodedData = window.atob(encodedData); // Zeichenkette dekodieren
+
+ +

Hinweise

+ +

Diese Methode kann verwendet werden, um Daten zu kodieren, übertragen, und mittels {{domxref("WindowBase64.atob","window.atob()")}} wieder zu dekodieren, welche andernfalls Übertragungsprobleme bereiten würden. Beispielsweise ist es möglich, die Steuerzeichen mit den ASCII-Werten 0 bis 31 zu kodieren.

+ +

btoa() steht auch in JavaScript implementierten XPCOM-Komponenten zur Verfügung, auch wenn window in solchen Komponenten nicht das globale Objekt ist.

+ +

Unicode-Zeichenketten

+ +

In den meisten Browsern verursacht ein Aufruf von window.btoa() mit einer Unicode-Zeichenkette eine "Character Out Of Range"-Exception ("Zeichen außerhalb des zulässigen Wertebereichs").

+ +

Das kann mithilfe eines solchen Code-Schemas vermieden werden (beigesteuert von Johan Sundström):

+ +
function utf8_to_b64(str) {
+    return window.btoa(unescape(encodeURIComponent(str)));
+}
+
+function b64_to_utf8(str) {
+    return decodeURIComponent(escape(window.atob(str)));
+}
+
+// Usage:
+utf8_to_b64('✓ à la mode'); // JTI1dTI3MTMlMjUyMCUyNUUwJTI1MjBsYSUyNTIwbW9kZQ==
+b64_to_utf8('JTI1dTI3MTMlMjUyMCUyNUUwJTI1MjBsYSUyNTIwbW9kZQ=='); // "✓ à la mode"
+
+utf8_to_b64('I \u2661 Unicode!'); // SSUyNTIwJTI1dTI2NjElMjUyMFVuaWNvZGUlMjUyMQ==
+b64_to_utf8('SSUyNTIwJTI1dTI2NjElMjUyMFVuaWNvZGUlMjUyMQ=='); // "I ♡ Unicode!"
+
+
+ +

Eine günstigere, zuverlässigere und effizientere Lösung ist, DOMString zunächst in eine UTF-8-kodierte Zeichenkette zu konvertieren, die sich für typed arrays eignet. Eine Anleitung bietet dieser Abschnitt.

+ +

Specifications

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#dom-windowbase64-btoa', 'WindowBase64.btoa()')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#dom-windowbase64-btoa', 'WindowBase64.btoa()')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#dom-windowbase64-btoa", "WindowBase64.btoa()")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
+ +

Browser compatibility

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatVersionUnknown}}{{CompatGeckoDesktop(1)}}[1]10{{CompatVersionUnknown}}{{CompatVersionUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{CompatVersionUnknown}}{{CompatGeckoMobile(1)}}{{CompatNo}}{{CompatUnknown}}{{CompatVersionUnknown}}
+
+ +

[1] btoa() is also available to XPCOM components implemented in JavaScript, even though window is not the global object in components.

+ +

See also

+ + diff --git a/files/de/web/api/windoworworkerglobalscope/index.html b/files/de/web/api/windoworworkerglobalscope/index.html new file mode 100644 index 0000000000..f51b72c102 --- /dev/null +++ b/files/de/web/api/windoworworkerglobalscope/index.html @@ -0,0 +1,121 @@ +--- +title: WindowBase64 +slug: Web/API/WindowBase64 +tags: + - API + - HTML-DOM + - Helper + - NeedsTranslation + - TopicStub + - WindowBase64 +translation_of: Web/API/WindowOrWorkerGlobalScope +translation_of_original: Web/API/WindowBase64 +--- +

{{APIRef("HTML DOM")}}

+ +

The WindowBase64 helper contains utility methods to convert data to and from base64, a binary-to-text encoding scheme. For example it is used in data URIs.

+ +

There is no object of this type, though the context object, either the {{domxref("Window")}} for regular browsing scope, or the {{domxref("WorkerGlobalScope")}}  for workers, implements it.

+ +

Properties

+ +

This helper neither defines nor inherits any properties.

+ +

Methods

+ +

This helper does not inherit any methods.

+ +
+
{{domxref("WindowBase64.atob()")}}
+
Decodes a string of data which has been encoded using base-64 encoding.
+
{{domxref("WindowBase64.btoa()")}}
+
Creates a base-64 encoded ASCII string from a string of binary data.
+
+ +

Specifications

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#windowbase64', 'WindowBase64')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#windowbase64', 'WindowBase64')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#windowbase64", "WindowBase64")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
+ +

Browser compatibility

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support{{CompatGeckoDesktop(1)}} [1]{{CompatVersionUnknown}}10.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureFirefox Mobile (Gecko)AndroidIE MobileOpera MobileSafari Mobile
Basic support{{CompatGeckoMobile(1)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
+
+ +

[1]  atob() is also available to XPCOM components implemented in JavaScript, even though {{domxref("Window")}} is not the global object in components.

+ +

See also

+ + diff --git a/files/de/web/api/windoworworkerglobalscope/settimeout/index.html b/files/de/web/api/windoworworkerglobalscope/settimeout/index.html new file mode 100644 index 0000000000..9f13870d78 --- /dev/null +++ b/files/de/web/api/windoworworkerglobalscope/settimeout/index.html @@ -0,0 +1,329 @@ +--- +title: WindowTimers.setTimeout() +slug: Web/API/WindowTimers/setTimeout +translation_of: Web/API/WindowOrWorkerGlobalScope/setTimeout +--- +
{{APIRef("HTML DOM")}}
+ +

Die Funktion setTimeout() der {{domxref("WindowOrWorkerGlobalScope")}}-Schnittstelle ruft nach der gegebenen Zeitspanne eine Funktion oder direkt angebenen Code auf.

+ +

Syntax

+ +
var timeoutID = window.setTimeout(funktion, zeitspanne, [parameter1, parameter2, ...]);
+var timeoutID = window.setTimeout(code, zeitspanne);
+
+ +

 

+ +

Parameter

+ +

 

+ +
+
funktion
+
Die {{jsxref("function", "Funktion")}}, die nach zeitspanne Millisekunden ausgeführt werden soll.
+
code
+
code in der alternativen Syntax ist eine Zeichenkette, die Code enthält, der nach zeitspanne Millisekunden ausgeführt werden soll.  code sollte aus den gleichen Gründen, die auch eval() zum Sicherheitsrisiko machen, nicht verwendet werden.
+
zeitspanne {{optional_inline}}
+
zeitspanne ist die Wartezeit in Millisekunden (ein Tausendstel einer Sekunde), nach der funktion bzw. code ausgeführt werden soll. Dieser Wert ist ein Mindestwert, die tatsächlich abgelaufene Zeit kann länger sein, siehe {{anch("Gründe für längere als gegebene Wartezeiten")}} weiter unten.
+
parameter1, ..., parameterN {{optional_inline}}
+
Diese Parameter werden an die funktion oder den code übergeben.
+
+ +
Anmerkung: Der Internet Explorer unterstützt bis einschließlich Version 9 die Übergabe von zusätzlichen Parametern nicht.
+ +

Rückgabewert

+ +

setTimeout() gibt eine ID zurück, die den eingerichteten Zeitgeber identifiziert; um die Wartezeit abzubrechen, kann sie an {{domxref("WindowOrWorkerGlobalScope.clearTimeout","clearTimeout()")}}  übergeben werden.

+ +

Jede ID wird von setTimeout() und setInterval() nur einmalig je Objekt (window oder Worker) verwendet.

+ +

Beispiel

+ +

HTML

+ +
<p>Beispiel</p>
+<button onclick="delayedAlert();">Zeige nach zwei Sekunden einen Alarm.</button>
+<p></p>
+<button onclick="clearAlert();">Alarm vorzeitig abbrechen.</button>
+ +

JavaScript

+ +
var timeoutID;
+
+function delayedAlert() {
+  timeoutID = window.setTimeout(slowAlert, 2000);
+}
+
+function slowAlert() {
+  alert('Das hat gedauert!');
+}
+
+function clearAlert() {
+  window.clearTimeout(timeoutID);
+}
+ +

Ergebnis

+ +

{{EmbedLiveSample('Example')}}

+ +

Siehe auch clearTimeout()

+ +

Polyfill

+ +

Der nachfolgende Polyfill-Code ermöglicht es, der aufzurufenden Funktion auch im Internet Explorer Parameter zu übergeben:

+ +
/*\
+|*|
+|*|  Polyfill which enables the passage of arbitrary arguments to the
+|*|  callback functions of JavaScript timers (HTML5 standard syntax).
+|*|
+|*|  https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
+|*|
+|*|  Syntax:
+|*|  var timeoutID = window.setTimeout(func, delay[, param1, param2, ...]);
+|*|  var timeoutID = window.setTimeout(code, delay);
+|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
+|*|  var intervalID = window.setInterval(code, delay);
+|*|
+\*/
+
+(function() {
+  setTimeout(function(arg1) {
+    if (arg1 === 'test') {
+      // feature test is passed, no need for polyfill
+      return;
+    }
+    var __nativeST__ = window.setTimeout;
+    window.setTimeout = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
+      var aArgs = Array.prototype.slice.call(arguments, 2);
+      return __nativeST__(vCallback instanceof Function ? function() {
+        vCallback.apply(null, aArgs);
+      } : vCallback, nDelay);
+    };
+  }, 0, 'test');
+
+  var interval = setInterval(function(arg1) {
+    clearInterval(interval);
+    if (arg1 === 'test') {
+      // feature test is passed, no need for polyfill
+      return;
+    }
+    var __nativeSI__ = window.setInterval;
+    window.setInterval = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
+      var aArgs = Array.prototype.slice.call(arguments, 2);
+      return __nativeSI__(vCallback instanceof Function ? function() {
+        vCallback.apply(null, aArgs);
+      } : vCallback, nDelay);
+    };
+  }, 0, 'test');
+}())
+ +

Eine andere Möglichkeit ist, die Funktion innerhalb einer anonymen Funktion aufzurufen:

+ +
setTimeout(function() {
+    funktion("eins", "zwei", "drei");
+}, 1000);
+
+ +

Ebenfalls kann die Funktion bind() genutzt werden:

+ +

 

+ +
setTimeout(function(arg1, arg2, arg3){}.bind(undefined, "eins", "zwei", "drei"), 1000);
+ +

 

+ +

Das "this"-Problem

+ +

In der an setTimeout() übergebenen Funktion wird this bei Aufruf einen falschen Wert enthalten. Dieses Problem wird detailliert in der JavaScript-Referenz beschrieben.

+ +

Erklärung

+ +

Von setTimeout() ausgeführter Code wird in einem anderen Kontext ausgeführt, als in dem setTimeout() aufgerufen wurde. this wird deshalb in der aufgerufenen Funktion window oder global entsprechen, nicht dem this des Bereichs, in dem  setTimeout() aufgerufen wurde. Beispiel:

+ +
myArray = ["null", "eins", "drei"];
+myArray.myMethod = function (sProperty) {
+    alert(arguments.length > 0 ? this[sProperty] : this);
+};
+
+myArray.myMethod(); // Ausgabe: "null,eins,zwei"
+myArray.myMethod(1); // Ausgabe: "eins"
+setTimeout(myArray.myMethod, 1000); // Ausgabe: "[object Window]" nach 1 Sekunde
+setTimeout(myArray.myMethod, 1500, "1"); // Ausgabe: "undefined" nach 1,5 Sekunden
+// Versuchen wir, 'this' zu übergeben
+setTimeout.call(myArray, myArray.myMethod, 2000); // Fehler: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
+setTimeout.call(myArray, myArray.myMethod, 2500, 2); // Der gleiche Fehler
+ +

Es gibt hier keine Möglichkeit, this an die aufzurufende Funktion durchzureichen.

+ +

Eine Lösungsmöglichkeit

+ +

Eine Möglichkeit, das Problem zu umgehen, ist es, die beiden eingebauten Funktionen setTimeout() und setInterval() durch zwei eigene zu ersetzen, die sich Function.prototype.call bedienen:

+ +
// Enable the passage of the 'this' object through the JavaScript timers
+
+var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
+
+window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeST__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};
+
+window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeSI__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};
+ +
Hinweis: Diese beiden Ersatzfunktionen ermöglichen es auch, im Internet Explorer Parameter an die aufzurufende Funktion zu übergeben, wie es HTML5 vorsieht. Sie können daher auch als {{anch("Polyfill")}} benutzt werden.
+ +

Anmerkungen

+ +

Eine Wartezeit kann mit window.clearTimeout() abgebrochen werden.

+ +

Soll eine Funktion wiederholt alle n Millisekunden aufgerufen werden, ist window.setInterval() die bessere Wahl.

+ +

Passing string literals

+ +

Passing a string instead of a function to setTimeout() suffers from the same hazards as using eval.

+ +
// Correct
+window.setTimeout(function() {
+    alert("Hello World!");
+}, 500);
+
+// Incorrect
+window.setTimeout("alert(\"Hello World!\");", 500);
+
+
+ +

String literals are evaluated in the global context, so local symbols in the context where setTimeout() was called will not be available when the string is evaluated as code.

+ +

Gründe für längere als gegebene Wartezeiten

+ +

Aus verschiedenen Gründen kann die tatsächliche Zeit bis zum Aufruf der Funktion länger sein als mit setTimeout() angegeben wurde. Dieser Abschnitt beschreibt die beiden häufigsten.

+ +

Wartezeiten betragen 4ms oder mehr

+ +

Um das System nicht zu überlasten, begrenzen moderne Browser die Häufigkeit der von setTimeout() und setInterval() ausgelösten Ereignisse ab einer bestimmten Aufruftiefe oder -häufigkeit. Beispiel:

+ +
function cb() { f(); setTimeout(cb, 0); }
+setTimeout(cb, 0);
+ +
setInterval(f, 0);
+ +

Chrome und Firefox drosseln nach dem fünften Aufruf, Safari nach dem sechsten, Edge bereits nach dem dritten.

+ +

Eine Wartezeit von 0 ms kann mit {{domxref("window.postMessage()")}} erreicht werden, wie hier beschrieben.

+ +

 

+ +
+

Hinweis: Die Mindestwartezeit, DOM_MIN_TIMEOUT_VALUE, beträgt 4 ms (Firefox-Einstellung dom.min_timeout_value), die Verschachtelungstiefe DOM_CLAMP_TIMEOUT_NESTING_LEVEL beträgt 5.

+
+ +
+

Hinweis: Der Wert 4 ms wurde mit HTML5 festgelegt und wird von allen Browsern umgesetzt, die nach 2010 erschienen sind. Vor {{geckoRelease("5.0")}} war der Mindestwert 10 ms.

+
+ +

 

+ +

 

+ +

Wartezeiten in inaktiven Tabs  größer als 1s

+ +

Um die Last noch weiter zu verringern, beträgt die Mindestwartezeit in sich im Hintergrund befindlichen Tabs grundsätzlich mindestens 1 s.

+ +

Firefox setzt diese Beschränkung seit Version 5 um (siehe {{bug(633421)}}). Der Wert von 1000 ms kann in der Einstellungen dom.min_background_timeout_value geändert werden.
+ Chrome setzt diese Beschränkung seit Version 11 um (crbug.com/66078).

+ +

Bei Firefox für Android beträgt seit Version 14 die Mindestwartezeit in sich im Hintergrund befindlichen Tabs 15 Minuten (siehe {{bug(736602)}}). Jegliche Aktivität in diesen Tabs kann auch vollständig angehalten werden.

+ +
+

Seit Version 50 gelten in Firefox keine Beschränkungen mehr, falls sich die Web Audio API {{domxref("AudioContext")}} im Wiedergabemodus befindet. Seit Firefox 51 gilt dies bereits für jegliche Dokumente mit einem {{domxref("AudioContext")}}, unabhängig von dessen Status.
+ Apps, die Musik Note für Note abspielen, konnten wegen der Beschränkungen den Takt nicht halten, sobald sie in den Hintergrund gelegt wurden.

+
+ +

Lastbedingte Verzögerungen

+ +

Zusätzlich zum aktiven Drosseln kann es zu verlängerten Wartezeiten kommen, falls das Dokument, der Browser oder das Betriebssystem anderweitig beschäftigt sind. Ein wichtiger Punkt in diesem Zusammenhang ist, dass die aufzurufende Funktion nicht ausgeführt werden kann, solange der Thread, der setTimeout() aufgerufen hat, noch aktiv ist:

+ +
function bla() {
+  console.log('bla wurde aufgerufen');
+}
+setTimeout(bla, 0);
+console.log('hinter setTimeout');
+ +

Ergebnis:

+ +
hinter setTimeout
+bla wurde aufgerufen
+ +

Obwohl setTimeout die sofortige Ausführung angewiesen wurde, wird der Aufruf von bla() in einer Warteschlange bis zur nächsten Gelegenheit zurückgestellt. Erst wenn der aktuell laufende Code ausgeführt wurde, werden die in der Warteschlange gesammelten Aufrufe abgearbeitet. In der Folge ist der Programmablauf nicht, wie eventuell erwartet.

+ +

Browserkompatibilität

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + +
EigenschaftChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basis1.0{{ CompatGeckoDesktop("1") }}4.04.01.0
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
EigenschaftAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic1.01.0{{ CompatGeckoMobile("1") }}6.06.01.0
+
+ +

Spezifikation

+ +

Gehört zu DOM-Level 0, wie spezifiziert in HTML5.

+ +

Siehe auch

+ + diff --git a/files/de/web/api/windowtimers/index.html b/files/de/web/api/windowtimers/index.html deleted file mode 100644 index 67f9f76863..0000000000 --- a/files/de/web/api/windowtimers/index.html +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: WindowTimers -slug: Web/API/WindowTimers -tags: - - API - - HTML-DOM - - Interface - - NeedsTranslation - - Reference - - TopicStub - - Workers -translation_of: Web/API/WindowOrWorkerGlobalScope -translation_of_original: Web/API/WindowTimers ---- -
{{APIRef("HTML DOM")}}
- -

WindowTimers contains utility methods to set and clear timers.

- -

There is no object of this type, though the context object, either the {{domxref("Window")}} for regular browsing scope, or the {{domxref("WorkerGlobalScope")}}  for workers, implements it.

- -

Properties

- -

This interface do not define any property, nor inherit any.

- -

Methods

- -

This interface do not inherit any method.

- -
-
{{domxref("WindowTimers.clearInterval()")}}
-
Cancels the repeated execution set using {{domxref("WindowTimers.setInterval()")}}.
-
{{domxref("WindowTimers.clearTimeout()")}}
-
Cancels the repeated execution set using {{domxref("WindowTimers.setTimeout()")}}.
-
{{domxref("WindowTimers.setInterval()")}}
-
Schedules the execution of a function each X milliseconds.
-
{{domxref("WindowTimers.setTimeout()")}}
-
Sets a delay for executing a function.
-
- -

Specifications

- - - - - - - - - - - - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#windowtimers', 'WindowTimers')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#windowtimers', 'WindowTimers')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#windowtimers", "WindowTimers")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
- -

Browser compatibility

- -

{{CompatibilityTable}}

- -
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support{{CompatGeckoDesktop(1)}}1.04.04.01.0
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox Mobile (Gecko)AndroidIE MobileOpera MobileSafari Mobile
Basic support{{CompatGeckoMobile(1)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
- -

 

- -

See also

- - diff --git a/files/de/web/api/windowtimers/settimeout/index.html b/files/de/web/api/windowtimers/settimeout/index.html deleted file mode 100644 index 9f13870d78..0000000000 --- a/files/de/web/api/windowtimers/settimeout/index.html +++ /dev/null @@ -1,329 +0,0 @@ ---- -title: WindowTimers.setTimeout() -slug: Web/API/WindowTimers/setTimeout -translation_of: Web/API/WindowOrWorkerGlobalScope/setTimeout ---- -
{{APIRef("HTML DOM")}}
- -

Die Funktion setTimeout() der {{domxref("WindowOrWorkerGlobalScope")}}-Schnittstelle ruft nach der gegebenen Zeitspanne eine Funktion oder direkt angebenen Code auf.

- -

Syntax

- -
var timeoutID = window.setTimeout(funktion, zeitspanne, [parameter1, parameter2, ...]);
-var timeoutID = window.setTimeout(code, zeitspanne);
-
- -

 

- -

Parameter

- -

 

- -
-
funktion
-
Die {{jsxref("function", "Funktion")}}, die nach zeitspanne Millisekunden ausgeführt werden soll.
-
code
-
code in der alternativen Syntax ist eine Zeichenkette, die Code enthält, der nach zeitspanne Millisekunden ausgeführt werden soll.  code sollte aus den gleichen Gründen, die auch eval() zum Sicherheitsrisiko machen, nicht verwendet werden.
-
zeitspanne {{optional_inline}}
-
zeitspanne ist die Wartezeit in Millisekunden (ein Tausendstel einer Sekunde), nach der funktion bzw. code ausgeführt werden soll. Dieser Wert ist ein Mindestwert, die tatsächlich abgelaufene Zeit kann länger sein, siehe {{anch("Gründe für längere als gegebene Wartezeiten")}} weiter unten.
-
parameter1, ..., parameterN {{optional_inline}}
-
Diese Parameter werden an die funktion oder den code übergeben.
-
- -
Anmerkung: Der Internet Explorer unterstützt bis einschließlich Version 9 die Übergabe von zusätzlichen Parametern nicht.
- -

Rückgabewert

- -

setTimeout() gibt eine ID zurück, die den eingerichteten Zeitgeber identifiziert; um die Wartezeit abzubrechen, kann sie an {{domxref("WindowOrWorkerGlobalScope.clearTimeout","clearTimeout()")}}  übergeben werden.

- -

Jede ID wird von setTimeout() und setInterval() nur einmalig je Objekt (window oder Worker) verwendet.

- -

Beispiel

- -

HTML

- -
<p>Beispiel</p>
-<button onclick="delayedAlert();">Zeige nach zwei Sekunden einen Alarm.</button>
-<p></p>
-<button onclick="clearAlert();">Alarm vorzeitig abbrechen.</button>
- -

JavaScript

- -
var timeoutID;
-
-function delayedAlert() {
-  timeoutID = window.setTimeout(slowAlert, 2000);
-}
-
-function slowAlert() {
-  alert('Das hat gedauert!');
-}
-
-function clearAlert() {
-  window.clearTimeout(timeoutID);
-}
- -

Ergebnis

- -

{{EmbedLiveSample('Example')}}

- -

Siehe auch clearTimeout()

- -

Polyfill

- -

Der nachfolgende Polyfill-Code ermöglicht es, der aufzurufenden Funktion auch im Internet Explorer Parameter zu übergeben:

- -
/*\
-|*|
-|*|  Polyfill which enables the passage of arbitrary arguments to the
-|*|  callback functions of JavaScript timers (HTML5 standard syntax).
-|*|
-|*|  https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
-|*|
-|*|  Syntax:
-|*|  var timeoutID = window.setTimeout(func, delay[, param1, param2, ...]);
-|*|  var timeoutID = window.setTimeout(code, delay);
-|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
-|*|  var intervalID = window.setInterval(code, delay);
-|*|
-\*/
-
-(function() {
-  setTimeout(function(arg1) {
-    if (arg1 === 'test') {
-      // feature test is passed, no need for polyfill
-      return;
-    }
-    var __nativeST__ = window.setTimeout;
-    window.setTimeout = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
-      var aArgs = Array.prototype.slice.call(arguments, 2);
-      return __nativeST__(vCallback instanceof Function ? function() {
-        vCallback.apply(null, aArgs);
-      } : vCallback, nDelay);
-    };
-  }, 0, 'test');
-
-  var interval = setInterval(function(arg1) {
-    clearInterval(interval);
-    if (arg1 === 'test') {
-      // feature test is passed, no need for polyfill
-      return;
-    }
-    var __nativeSI__ = window.setInterval;
-    window.setInterval = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
-      var aArgs = Array.prototype.slice.call(arguments, 2);
-      return __nativeSI__(vCallback instanceof Function ? function() {
-        vCallback.apply(null, aArgs);
-      } : vCallback, nDelay);
-    };
-  }, 0, 'test');
-}())
- -

Eine andere Möglichkeit ist, die Funktion innerhalb einer anonymen Funktion aufzurufen:

- -
setTimeout(function() {
-    funktion("eins", "zwei", "drei");
-}, 1000);
-
- -

Ebenfalls kann die Funktion bind() genutzt werden:

- -

 

- -
setTimeout(function(arg1, arg2, arg3){}.bind(undefined, "eins", "zwei", "drei"), 1000);
- -

 

- -

Das "this"-Problem

- -

In der an setTimeout() übergebenen Funktion wird this bei Aufruf einen falschen Wert enthalten. Dieses Problem wird detailliert in der JavaScript-Referenz beschrieben.

- -

Erklärung

- -

Von setTimeout() ausgeführter Code wird in einem anderen Kontext ausgeführt, als in dem setTimeout() aufgerufen wurde. this wird deshalb in der aufgerufenen Funktion window oder global entsprechen, nicht dem this des Bereichs, in dem  setTimeout() aufgerufen wurde. Beispiel:

- -
myArray = ["null", "eins", "drei"];
-myArray.myMethod = function (sProperty) {
-    alert(arguments.length > 0 ? this[sProperty] : this);
-};
-
-myArray.myMethod(); // Ausgabe: "null,eins,zwei"
-myArray.myMethod(1); // Ausgabe: "eins"
-setTimeout(myArray.myMethod, 1000); // Ausgabe: "[object Window]" nach 1 Sekunde
-setTimeout(myArray.myMethod, 1500, "1"); // Ausgabe: "undefined" nach 1,5 Sekunden
-// Versuchen wir, 'this' zu übergeben
-setTimeout.call(myArray, myArray.myMethod, 2000); // Fehler: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
-setTimeout.call(myArray, myArray.myMethod, 2500, 2); // Der gleiche Fehler
- -

Es gibt hier keine Möglichkeit, this an die aufzurufende Funktion durchzureichen.

- -

Eine Lösungsmöglichkeit

- -

Eine Möglichkeit, das Problem zu umgehen, ist es, die beiden eingebauten Funktionen setTimeout() und setInterval() durch zwei eigene zu ersetzen, die sich Function.prototype.call bedienen:

- -
// Enable the passage of the 'this' object through the JavaScript timers
-
-var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
-
-window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
-  return __nativeST__(vCallback instanceof Function ? function () {
-    vCallback.apply(oThis, aArgs);
-  } : vCallback, nDelay);
-};
-
-window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
-  return __nativeSI__(vCallback instanceof Function ? function () {
-    vCallback.apply(oThis, aArgs);
-  } : vCallback, nDelay);
-};
- -
Hinweis: Diese beiden Ersatzfunktionen ermöglichen es auch, im Internet Explorer Parameter an die aufzurufende Funktion zu übergeben, wie es HTML5 vorsieht. Sie können daher auch als {{anch("Polyfill")}} benutzt werden.
- -

Anmerkungen

- -

Eine Wartezeit kann mit window.clearTimeout() abgebrochen werden.

- -

Soll eine Funktion wiederholt alle n Millisekunden aufgerufen werden, ist window.setInterval() die bessere Wahl.

- -

Passing string literals

- -

Passing a string instead of a function to setTimeout() suffers from the same hazards as using eval.

- -
// Correct
-window.setTimeout(function() {
-    alert("Hello World!");
-}, 500);
-
-// Incorrect
-window.setTimeout("alert(\"Hello World!\");", 500);
-
-
- -

String literals are evaluated in the global context, so local symbols in the context where setTimeout() was called will not be available when the string is evaluated as code.

- -

Gründe für längere als gegebene Wartezeiten

- -

Aus verschiedenen Gründen kann die tatsächliche Zeit bis zum Aufruf der Funktion länger sein als mit setTimeout() angegeben wurde. Dieser Abschnitt beschreibt die beiden häufigsten.

- -

Wartezeiten betragen 4ms oder mehr

- -

Um das System nicht zu überlasten, begrenzen moderne Browser die Häufigkeit der von setTimeout() und setInterval() ausgelösten Ereignisse ab einer bestimmten Aufruftiefe oder -häufigkeit. Beispiel:

- -
function cb() { f(); setTimeout(cb, 0); }
-setTimeout(cb, 0);
- -
setInterval(f, 0);
- -

Chrome und Firefox drosseln nach dem fünften Aufruf, Safari nach dem sechsten, Edge bereits nach dem dritten.

- -

Eine Wartezeit von 0 ms kann mit {{domxref("window.postMessage()")}} erreicht werden, wie hier beschrieben.

- -

 

- -
-

Hinweis: Die Mindestwartezeit, DOM_MIN_TIMEOUT_VALUE, beträgt 4 ms (Firefox-Einstellung dom.min_timeout_value), die Verschachtelungstiefe DOM_CLAMP_TIMEOUT_NESTING_LEVEL beträgt 5.

-
- -
-

Hinweis: Der Wert 4 ms wurde mit HTML5 festgelegt und wird von allen Browsern umgesetzt, die nach 2010 erschienen sind. Vor {{geckoRelease("5.0")}} war der Mindestwert 10 ms.

-
- -

 

- -

 

- -

Wartezeiten in inaktiven Tabs  größer als 1s

- -

Um die Last noch weiter zu verringern, beträgt die Mindestwartezeit in sich im Hintergrund befindlichen Tabs grundsätzlich mindestens 1 s.

- -

Firefox setzt diese Beschränkung seit Version 5 um (siehe {{bug(633421)}}). Der Wert von 1000 ms kann in der Einstellungen dom.min_background_timeout_value geändert werden.
- Chrome setzt diese Beschränkung seit Version 11 um (crbug.com/66078).

- -

Bei Firefox für Android beträgt seit Version 14 die Mindestwartezeit in sich im Hintergrund befindlichen Tabs 15 Minuten (siehe {{bug(736602)}}). Jegliche Aktivität in diesen Tabs kann auch vollständig angehalten werden.

- -
-

Seit Version 50 gelten in Firefox keine Beschränkungen mehr, falls sich die Web Audio API {{domxref("AudioContext")}} im Wiedergabemodus befindet. Seit Firefox 51 gilt dies bereits für jegliche Dokumente mit einem {{domxref("AudioContext")}}, unabhängig von dessen Status.
- Apps, die Musik Note für Note abspielen, konnten wegen der Beschränkungen den Takt nicht halten, sobald sie in den Hintergrund gelegt wurden.

-
- -

Lastbedingte Verzögerungen

- -

Zusätzlich zum aktiven Drosseln kann es zu verlängerten Wartezeiten kommen, falls das Dokument, der Browser oder das Betriebssystem anderweitig beschäftigt sind. Ein wichtiger Punkt in diesem Zusammenhang ist, dass die aufzurufende Funktion nicht ausgeführt werden kann, solange der Thread, der setTimeout() aufgerufen hat, noch aktiv ist:

- -
function bla() {
-  console.log('bla wurde aufgerufen');
-}
-setTimeout(bla, 0);
-console.log('hinter setTimeout');
- -

Ergebnis:

- -
hinter setTimeout
-bla wurde aufgerufen
- -

Obwohl setTimeout die sofortige Ausführung angewiesen wurde, wird der Aufruf von bla() in einer Warteschlange bis zur nächsten Gelegenheit zurückgestellt. Erst wenn der aktuell laufende Code ausgeführt wurde, werden die in der Warteschlange gesammelten Aufrufe abgearbeitet. In der Folge ist der Programmablauf nicht, wie eventuell erwartet.

- -

Browserkompatibilität

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - -
EigenschaftChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basis1.0{{ CompatGeckoDesktop("1") }}4.04.01.0
-
- -
- - - - - - - - - - - - - - - - - - - - - -
EigenschaftAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic1.01.0{{ CompatGeckoMobile("1") }}6.06.01.0
-
- -

Spezifikation

- -

Gehört zu DOM-Level 0, wie spezifiziert in HTML5.

- -

Siehe auch

- - -- cgit v1.2.3-54-g00ecf