Aufnahme eines Bilds des Nutzers

Die meisten Browser können auf die Kamera des Nutzers zugreifen.

Mattenwaagen

Viele Browser haben jetzt die Möglichkeit, auf Video- und Audioeingaben des Nutzers zuzugreifen. Je nach Browser kann dies jedoch vollständig dynamisch und inline sein oder an eine andere App auf dem Gerät des Nutzers delegiert werden. Außerdem hat nicht jedes Gerät einmal eine Kamera. Wie können Sie also eine User Experience mit einem von Nutzern erstellten Bild schaffen, die überall gut funktioniert?

Einfach und schrittweise beginnen

Wenn Sie Ihre Umgebung schrittweise verbessern möchten, müssen Sie mit einer Lösung beginnen, die überall funktioniert. Am einfachsten ist es, wenn Sie den Nutzer um eine vorab aufgezeichnete Datei bitten.

URL anfordern

Das ist die am besten unterstützte, aber am wenigsten zufriedenstellende Option. Bitten Sie den Nutzer, Ihnen eine URL zu geben, und verwenden Sie diese. Wenn Sie nur das Bild anzeigen möchten, funktioniert dies überall. Erstellen Sie ein img-Element, legen Sie src fest und schon sind Sie fertig.

Wenn Sie das Bild jedoch auf irgendeine Weise bearbeiten möchten, ist die Sache etwas komplizierter. CORS verhindert, dass Sie auf die tatsächlichen Pixel zugreifen, es sei denn, der Server legt die entsprechenden Header fest und Sie markieren das Bild als ursprungsübergreifend. Die einzige praktische Möglichkeit, dies zu umgehen, besteht darin, einen Proxyserver auszuführen.

Dateieingabe

Sie können auch ein einfaches Dateieingabeelement mit einem accept-Filter verwenden, der angibt, dass Sie nur Bilddateien benötigen.

<input type="file" accept="image/*" />

Diese Methode funktioniert auf allen Plattformen. Auf dem Computer wird der Nutzer aufgefordert, eine Bilddatei aus dem Dateisystem hochzuladen. In Chrome und Safari auf iOS- und Android-Geräten kann der Nutzer mit dieser Methode auswählen, mit welcher App das Bild aufgenommen werden soll. Dazu gehört auch die Option, ein Foto direkt mit der Kamera aufzunehmen oder eine vorhandene Bilddatei auszuwählen.

Ein Android-Menü mit zwei Optionen: Bild und Dateien erfassen Ein iOS-Menü mit drei Optionen: Foto aufnehmen, Fotogalerie, iCloud

Die Daten können dann an eine <form> angehängt oder mit JavaScript bearbeitet werden. Dazu wird auf ein onchange-Ereignis für das Eingabeelement gewartet und dann das files-Attribut des Ereignisses target gelesen.

<input type="file" accept="image/*" id="file-input" />
<script>
  const fileInput = document.getElementById('file-input');

  fileInput.addEventListener('change', (e) =>
    doSomethingWithFiles(e.target.files),
  );
</script>

Die files-Eigenschaft ist ein FileList-Objekt, auf das ich später näher zurückkomme.

Optional können Sie dem Element auch das Attribut capture hinzufügen, um dem Browser mitzuteilen, dass Sie lieber ein Bild von der Kamera erhalten möchten.

<input type="file" accept="image/*" capture />
<input type="file" accept="image/*" capture="user" />
<input type="file" accept="image/*" capture="environment" />

Wenn das Attribut capture ohne Wert hinzugefügt wird, entscheidet der Browser, welche Kamera verwendet werden soll. Die Werte "user" und "environment" hingegen weisen den Browser darauf hin, dass er die Front- bzw. Rückkamera bevorzugt.

Das Attribut capture funktioniert unter Android und iOS, wird auf Computern aber ignoriert. Beachten Sie jedoch, dass der Nutzer auf Android nicht mehr die Möglichkeit hat, ein vorhandenes Bild auszuwählen. Stattdessen wird die Systemkamera-App direkt gestartet.

Drag-and-Drop

Wenn Sie bereits die Möglichkeit zum Hochladen einer Datei hinzufügen, gibt es einige einfache Möglichkeiten, die Nutzererfahrung zu verbessern.

Die erste besteht darin, Ihrer Seite ein Drop-Ziel hinzuzufügen, über das Nutzer eine Datei vom Computer oder aus einer anderen Anwendung per Drag-and-drop hinzufügen können.

<div id="target">You can drag an image file here</div>
<script>
  const target = document.getElementById('target');

  target.addEventListener('drop', (e) => {
    e.stopPropagation();
    e.preventDefault();

    doSomethingWithFiles(e.dataTransfer.files);
  });

  target.addEventListener('dragover', (e) => {
    e.stopPropagation();
    e.preventDefault();

    e.dataTransfer.dropEffect = 'copy';
  });
</script>

Ähnlich wie bei der Dateieingabe können Sie ein FileList-Objekt aus dem Attribut dataTransfer.files des Ereignisses drop abrufen.

Mit dem Event-Handler dragover können Sie dem Nutzer mithilfe des Attributs dropEffect signalisieren, was geschieht, wenn er die Datei löscht.

Drag-and-drop gibt es schon sehr lange und wird von den gängigen Browsern gut unterstützt.

Aus Zwischenablage einfügen

Die letzte Möglichkeit, eine bestehende Bilddatei aus der Zwischenablage abzurufen, Der Code dafür ist sehr einfach, aber die User Experience ist etwas schwieriger.

<textarea id="target">Paste an image here</textarea>
<script>
  const target = document.getElementById('target');

  target.addEventListener('paste', (e) => {
    e.preventDefault();
    doSomethingWithFiles(e.clipboardData.files);
  });
</script>

(e.clipboardData.files ist ein weiteres FileList-Objekt.)

Das Schwierige bei der Zwischenablage API ist, dass das Zielelement für eine vollständige browserübergreifende Unterstützung sowohl auswählbar als auch bearbeitbar sein muss. Hier passen <textarea> und <input type="text"> zusammen, ebenso wie Elemente mit dem Attribut contenteditable. Aber diese sind offensichtlich auch für die Textbearbeitung konzipiert.

Es kann schwierig sein, dies reibungslos zu gestalten, wenn Sie nicht möchten, dass der Nutzer Text eingeben kann. Tricks wie eine versteckte Eingabe, die ausgewählt wird, wenn Sie auf ein anderes Element klicken, können die Aufrechterhaltung der Barrierefreiheit erschweren.

FileList-Objekt verarbeiten

Da die meisten der oben genannten Methoden einen FileList erzeugen, sollte ich etwas darüber sprechen.

Ein FileList ähnelt einem Array. Sie hat numerische Schlüssel und ein length-Attribut, ist aber tatsächlich kein Array. Da es keine Array-Methoden wie forEach() oder pop() gibt, ist sie nicht iterierbar. Natürlich können Sie mit Array.from(fileList) ein reelles Array abrufen.

Die Einträge von FileList sind File-Objekte. Sie sind identisch mit Blob-Objekten, mit der Ausnahme, dass sie zusätzliche schreibgeschützte name- und lastModified-Attribute haben.

<img id="output" />
<script>
  const output = document.getElementById('output');

  function doSomethingWithFiles(fileList) {
    let file = null;

    for (let i = 0; i < fileList.length; i++) {
      if (fileList[i].type.match(/^image\//)) {
        file = fileList[i];
        break;
      }
    }

    if (file !== null) {
      output.src = URL.createObjectURL(file);
    }
  }
</script>

In diesem Beispiel wird die erste Datei mit einem Image-MIME-Typ ermittelt. Es können aber auch mehrere Bilder gleichzeitig ausgewählt, eingefügt und gelöscht werden.

Sobald Sie Zugriff auf die Datei haben, können Sie mit ihr alles tun. Beispielsweise können Sie…

  • Zeichnen Sie sie in ein <canvas>-Element, um es zu bearbeiten.
  • Auf das Gerät des Nutzers herunterladen
  • Laden Sie sie auf einen Server mit fetch() hoch.

Interaktiver Zugriff auf die Kamera

Nachdem du nun deine Basis bewältigt hast, ist es an der Zeit, dein Level nach und nach zu verbessern!

Moderne Browser können direkten Zugriff auf Kameras erhalten. So lassen sich Erlebnisse schaffen, die vollständig in die Webseite integriert sind, sodass der Nutzer niemals den Browser verlassen muss.

Zugriff auf die Kamera erhalten

Über eine API in der WebRTC-Spezifikation namens getUserMedia() können Sie direkt auf eine Kamera und ein Mikrofon zugreifen. Dadurch wird der Nutzer aufgefordert, Zugriff auf seine verbundenen Mikrofone und Kameras zu erhalten.

Die Unterstützung für getUserMedia() ist recht gut, aber sie ist noch nicht überall verfügbar. Insbesondere ist sie in Safari 10 oder früheren Versionen nicht verfügbar, da es sich beim Verfassen dieses Artikels noch immer um die aktuelle stabile Version handelt. Apple hat jedoch angekündigt, dass es in Safari 11 verfügbar sein wird.

Eine Unterstützung kann jedoch ganz einfach erkannt werden.

const supported = 'mediaDevices' in navigator;

Wenn Sie getUserMedia() aufrufen, müssen Sie ein Objekt übergeben, das beschreibt, welche Art von Medien Sie wünschen. Diese Auswahlmöglichkeiten werden als Einschränkungen bezeichnet. Es gibt einige mögliche Einschränkungen, z. B. ob du eine Front- oder Rückkamera bevorzugst, ob du Audio nutzen möchtest und welche Auflösung für den Stream bevorzugt ist.

Um Daten von der Kamera zu erhalten, ist jedoch nur eine Einschränkung erforderlich, und zwar video: true.

Bei Erfolg gibt die API ein MediaStream zurück, das Daten von der Kamera enthält. Sie können das Element dann entweder an ein <video>-Element anhängen und es abspielen, um eine Echtzeitvorschau anzuzeigen, oder es an ein <canvas> anhängen, um einen Snapshot zu erhalten.

<video id="player" controls autoplay></video>
<script>
  const player = document.getElementById('player');

  const constraints = {
    video: true,
  };

  navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
    player.srcObject = stream;
  });
</script>

Das alleine ist nicht sehr nützlich. Du kannst die Videodaten einfach wiederholen. Wenn Sie ein Bild haben möchten, müssen Sie ein wenig zusätzliche Arbeit leisten.

Momentaufnahme erstellen

Am besten zeichnen Sie einen Frame aus dem Video auf einen Canvas.

Im Gegensatz zur Web Audio API gibt es keine spezielle Streamverarbeitungs-API für Videos im Web. Sie müssen also ein kleines Hacking nutzen, um einen Snapshot von der Kamera des Nutzers zu erstellen.

So läuft der Vorgang ab:

  1. Erstellen Sie ein Canvas-Objekt, das den Rahmen von der Kamera hält
  2. Zugriff auf den Kamerastream erhalten
  3. An ein Videoelement anhängen
  4. Wenn Sie einen präzisen Frame erfassen möchten, fügen Sie die Daten aus dem Videoelement mit drawImage() einem Canvas-Objekt hinzu.
<video id="player" controls autoplay></video>
<button id="capture">Capture</button>
<canvas id="canvas" width="320" height="240"></canvas>
<script>
  const player = document.getElementById('player');
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const captureButton = document.getElementById('capture');

  const constraints = {
    video: true,
  };

  captureButton.addEventListener('click', () => {
    // Draw the video frame to the canvas.
    context.drawImage(player, 0, 0, canvas.width, canvas.height);
  });

  // Attach the video stream to the video element and autoplay.
  navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
    player.srcObject = stream;
  });
</script>

Sobald Sie Daten von der Kamera im Canvas gespeichert haben, können Sie viele Dinge damit tun. Sie haben folgende Möglichkeiten:

  • Laden Sie sie direkt auf den Server hoch.
  • Daten lokal speichern
  • Ausgefallene Effekte auf das Bild anwenden

Tipps

Streaming von der Kamera beenden, wenn es nicht benötigt wird

Es hat sich bewährt, die Kamera nicht mehr zu verwenden, wenn Sie sie nicht mehr benötigen. Dies spart nicht nur Akku und Rechenleistung, sondern gibt den Nutzern auch Vertrauen in Ihre Anwendung.

Um den Zugriff auf die Kamera zu beenden, können Sie einfach stop() für jeden Videotrack für den von getUserMedia() zurückgegebenen Stream aufrufen.

<video id="player" controls autoplay></video>
<button id="capture">Capture</button>
<canvas id="canvas" width="320" height="240"></canvas>
<script>
  const player = document.getElementById('player');
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const captureButton = document.getElementById('capture');

  const constraints = {
    video: true,
  };

  captureButton.addEventListener('click', () => {
    context.drawImage(player, 0, 0, canvas.width, canvas.height);

    // Stop all video streams.
    player.srcObject.getVideoTracks().forEach(track => track.stop());
  });

  navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
    // Attach the video stream to the video element and autoplay.
    player.srcObject = stream;
  });
</script>

Berechtigung zur verantwortungsvollen Verwendung der Kamera einholen

Wenn der Nutzer Ihrer Website noch keinen Zugriff auf die Kamera gewährt hat, wird der Nutzer beim Aufrufen von getUserMedia() sofort aufgefordert, der Kamera Zugriff auf Ihre Website zu gewähren.

Nutzer mögen es ungern, wenn sie zum Zugriff auf leistungsstarke Geräte auf ihrem Computer aufgefordert werden, und sie blockieren die Anfrage häufig oder sie ignorieren sie, wenn sie den Kontext, für den die Aufforderung erstellt wurde, nicht verstehen. Als Best Practice sollten Sie den Zugriff auf die Kamera nur dann anfordern, wenn das erforderlich ist. Sobald der Nutzer Zugriff gewährt hat, wird er nicht noch einmal gefragt. Lehnt der Nutzer jedoch den Zugriff ab, können Sie erst wieder Zugriff erhalten, wenn er die Einstellungen für die Kameraberechtigungen manuell ändert.

Kompatibilität

Weitere Informationen zur Implementierung für mobile und Desktop-Browser:

Außerdem empfehlen wir die Verwendung des Shim adapter.js, um Anwendungen vor WebRTC-Spezifikationsänderungen und Präfixunterschieden zu schützen.

Feedback