Aufnahme eines Bilds des Nutzers

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

Viele Browser haben jetzt die Möglichkeit, auf Video- und Audioeingaben des Nutzers zuzugreifen. Je nach Browser kann es sich jedoch um eine vollständig dynamische und Inline-Ansicht handeln oder die Funktion wird an eine andere App auf dem Gerät des Nutzers delegiert. Außerdem hat nicht jedes Gerät eine Kamera. Wie können Sie also eine Umgebung schaffen, in der ein von Nutzern erstelltes Bild überall gut funktioniert?

Einfach anfangen und nach und nach steigern

Wenn Sie Ihre Website nach und nach verbessern möchten, müssen Sie mit etwas beginnen, das überall funktioniert. Am einfachsten ist es, den Nutzer nach einer vorab aufgezeichneten Datei zu fragen.

URL anfordern

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

Wenn Sie das Bild jedoch in irgendeiner Weise bearbeiten möchten, ist das etwas komplizierter. CORS verhindert den Zugriff auf die tatsächlichen Pixel, es sei denn, der Server setzt die entsprechenden Header und Sie kennzeichnen das Bild als crossorigin. 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 nur Bilddateien zulässig sind.

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

Diese Methode funktioniert auf allen Plattformen. Auf dem Desktop 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 Möglichkeit, ein Foto direkt mit der Kamera aufzunehmen oder eine vorhandene Bilddatei auszuwählen.

Ein Android-Menü mit zwei Optionen: „Bild und Dateien aufnehmen“ Ein iOS-Menü mit drei Optionen: Foto aufnehmen, Fotomediathek, iCloud

Die Daten können dann an ein <form> angehängt oder mit JavaScript manipuliert werden. Dazu wird auf ein onchange-Ereignis am Eingabeelement gewartet und dann die files-Eigenschaft 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 noch näher eingehen werde.

Optional können Sie dem Element das Attribut capture hinzufügen, um dem Browser zu signalisieren, dass Sie ein Bild von der Kamera bevorzugen.

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

Wenn Sie das Attribut capture ohne Wert hinzufügen, entscheidet der Browser, welche Kamera verwendet werden soll. Mit den Werten "user" und "environment" wird dem Browser dagegen mitgeteilt, dass die Front- bzw. Rückkamera bevorzugt werden soll.

Das capture-Attribut funktioniert auf Android- und iOS-Geräten, wird aber auf dem Computer ignoriert. Beachten Sie jedoch, dass Nutzer unter Android dann keine vorhandenen Bilder mehr auswählen können. Die Systemkamera-App wird stattdessen direkt gestartet.

Drag-and-Drop

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

Die erste Möglichkeit besteht darin, Ihrer Seite ein Drop-Ziel hinzuzufügen, über das Nutzer eine Datei vom Desktop oder aus einer anderen Anwendung hineinziehen 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 der dataTransfer.files-Property des drop-Ereignisses abrufen.

Mit dem Event-Handler dragover kannst du dem Nutzer mithilfe des Attributs dropEffect signalisieren, was passiert, wenn er die Datei löscht.

Drag-and-drop gibt es schon lange und wird von den wichtigsten Browsern gut unterstützt.

Aus Zwischenablage einfügen

Eine weitere Möglichkeit, eine vorhandene Bilddatei abzurufen, ist die Zwischenablage. Der Code dafür ist sehr einfach, die Nutzerfreundlichkeit ist jedoch etwas schwieriger zu erreichen.

<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 an der Zwischenablage-API ist, dass das Zielelement für eine vollständige browserübergreifende Unterstützung sowohl auswählbar als auch bearbeitbar sein muss. Sowohl <textarea> als auch <input type="text"> eignen sich dafür, ebenso wie Elemente mit dem contenteditable-Attribut. Sie sind aber auch offensichtlich für die Textbearbeitung konzipiert.

Es kann schwierig sein, dies reibungslos zu gestalten, wenn Nutzer keinen Text eingeben sollen. Tricks wie eine versteckte Eingabe, die beim Klicken auf ein anderes Element ausgewählt wird, können die Barrierefreiheit erschweren.

FileList-Objekte verarbeiten

Da die meisten der oben genannten Methoden ein FileList erzeugen, sollte ich näher darauf eingehen.

Ein FileList ähnelt einem Array. Es hat numerische Schlüssel und eine length-Eigenschaft, ist aber technisch gesehen kein Array. Es gibt keine Arraymethoden wie forEach() oder pop() und es ist nicht iterierbar. Natürlich können Sie auch ein echtes Array mit Array.from(fileList) erhalten.

Die Einträge der FileList sind File-Objekte. Sie sind mit Blob-Objekten identisch, mit der Ausnahme, dass sie zusätzliche schreibgeschützte Eigenschaften name und lastModified 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 Bild-MIME-Typ gefunden. Es können aber auch mehrere Bilder gleichzeitig ausgewählt, eingefügt oder abgelegt werden.

Sobald Sie Zugriff auf die Datei haben, können Sie damit alles tun, was Sie möchten. Beispielsweise können Sie…

  • Zeichnen Sie es in ein <canvas>-Element, damit Sie es bearbeiten können.
  • Sie laden sie auf das Gerät des Nutzers herunter.
  • Mit fetch() auf einen Server hochladen

Interaktiv auf die Kamera zugreifen

Jetzt ist es an der Zeit, die Website kontinuierlich zu verbessern.

Moderne Browser können direkten Zugriff auf Kameras erhalten, sodass Sie Funktionen entwickeln können, die vollständig in die Webseite eingebunden sind und bei denen Nutzer den Browser nicht verlassen müssen.

Zugriff auf die Kamera erhalten

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

getUserMedia() wird zwar recht gut unterstützt, aber noch nicht überall. Insbesondere ist sie nicht in Safari 10 oder niedriger verfügbar, da diese Version zum Zeitpunkt der Erstellung dieser Version noch die aktuelle stabile Version war. Apple hat jedoch angekündigt, dass es in Safari 11 verfügbar sein wird.

Unterstützung zu erkennen, ist jedoch sehr einfach.

const supported = 'mediaDevices' in navigator;

Wenn Sie getUserMedia() aufrufen, müssen Sie ein Objekt übergeben, das beschreibt, welche Art von Medien Sie verwenden möchten. Diese Optionen werden als Einschränkungen bezeichnet. Es gibt verschiedene mögliche Einschränkungen, z. B. ob Sie eine Front- oder Rückkamera bevorzugen, ob Sie Audio möchten und welche Auflösung Sie für den Stream bevorzugen.

Um Daten von der Kamera abzurufen, benötigen Sie jedoch nur eine Einschränkung, und zwar video: true.

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

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

  const constraints = {
    video: true,
  };

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

Das ist an sich nicht besonders nützlich. Sie können nur die Videodaten abspielen. Wenn Sie ein Bild herunterladen möchten, müssen Sie ein wenig mehr Aufwand betreiben.

Snapshot erstellen

Die beste unterstützte Option zum Erstellen eines Bilds ist das Zeichnen eines Frames aus dem Video auf ein Canvas.

Im Gegensatz zur Web Audio API gibt es keine spezielle Stream-Verarbeitungs-API für Video im Web. Sie müssen also ein wenig hacken, um einen Schnappschuss von der Kamera des Nutzers aufzunehmen.

So läuft der Vorgang ab:

  1. Erstellen Sie ein Canvas-Objekt, das den Frame vor der Kamera enthält.
  2. Zugriff auf den Kamerastream erhalten
  3. An ein Videoelement anhängen
  4. Wenn Sie einen genauen Frame erfassen möchten, fügen Sie die Daten aus dem Videoelement mit drawImage() einem Canvas-Objekt hinzu.
<video id="player" controls playsinline 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 damit viele Dinge tun. Sie haben folgende Möglichkeiten:

  • Direkt auf den Server hochladen
  • Lokal speichern
  • Auf das Bild coole Effekte anwenden

Tipps

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

Verwenden Sie die Kamera nicht mehr, wenn Sie sie nicht mehr benötigen. So sparen Sie nicht nur Akku und Rechenleistung, sondern stärken auch das Vertrauen der Nutzer in Ihre App.

Wenn du den Zugriff auf die Kamera beenden möchtest, kannst du einfach stop() für jeden Videotrack des von getUserMedia() zurückgegebenen Streams aufrufen.

<video id="player" controls playsinline 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>

Erlaubnis zur verantwortungsvollen Nutzung der Kamera einholen

Wenn der Nutzer Ihrer Website noch nicht die Berechtigung zur Kameranutzung erteilt hat, wird er vom Browser aufgefordert, dies zu tun, sobald Sie getUserMedia() aufrufen.

Nutzer hassen es, zum Zugriff auf leistungsstarke Geräte auf ihrem Computer aufgefordert zu werden. Sie blockieren die Anfrage häufig oder ignorieren sie, wenn sie den Kontext, für den die Aufforderung erstellt wurde, nicht verstehen. Es wird empfohlen, den Zugriff auf die Kamera nur dann anzufordern, wenn er zum ersten Mal benötigt wird. Sobald der Nutzer den Zugriff gewährt hat, wird er nicht mehr gefragt. Wenn der Nutzer jedoch den Zugriff ablehnt, kannst du erst wieder darauf zugreifen, wenn er die Einstellungen für die Kameraberechtigungen manuell ändert.

Kompatibilität

Weitere Informationen zur Implementierung in mobilen und Desktop-Browsern:

Wir empfehlen außerdem, den Shim adapter.js zu verwenden, um Apps vor Änderungen an der WebRTC-Spezifikation und Präfixabweichungen zu schützen.

Feedback