Mengambil gambar dari pengguna

Sebagian besar browser dapat mengakses kamera pengguna.

Banyak browser kini memiliki kemampuan untuk mengakses input video dan audio dari pengguna. Namun, bergantung pada browser, pengalaman ini mungkin berupa pengalaman inline dan dinamis penuh, atau dapat didelegasikan ke aplikasi lain di perangkat pengguna. Selain itu, tidak semua perangkat memiliki kamera. Jadi, bagaimana Anda dapat membuat pengalaman yang menggunakan gambar buatan pengguna yang berfungsi dengan baik di mana saja?

Mulai dengan sederhana dan bertahap

Jika ingin meningkatkan pengalaman secara bertahap, Anda harus memulai dengan sesuatu yang berfungsi di mana saja. Cara termudah untuk melakukannya adalah dengan meminta file yang telah direkam sebelumnya kepada pengguna.

Meminta URL

Ini adalah opsi yang paling didukung, tetapi paling tidak memuaskan. Minta pengguna untuk memberikan URL kepada Anda, lalu gunakan URL tersebut. Untuk hanya menampilkan gambar, ini berfungsi di mana saja. Buat elemen img, tetapkan src, dan Anda selesai.

Namun, jika Anda ingin memanipulasi gambar dengan cara apa pun, prosesnya akan sedikit lebih rumit. CORS mencegah Anda mengakses piksel sebenarnya, kecuali jika server menetapkan header yang sesuai dan Anda menandai gambar sebagai crossorigin; satu-satunya cara praktis untuk mengatasinya adalah dengan menjalankan server proxy.

Input file

Anda juga dapat menggunakan elemen input file sederhana, termasuk filter accept yang menunjukkan bahwa Anda hanya ingin file gambar.

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

Metode ini berfungsi di semua platform. Di desktop, pengguna akan diminta untuk mengupload file gambar dari sistem file. Di Chrome dan Safari di iOS dan Android, metode ini akan memberi pengguna pilihan aplikasi yang akan digunakan untuk mengambil gambar, termasuk opsi mengambil foto langsung dengan kamera atau memilih file gambar yang ada.

Menu Android, dengan dua opsi: mengambil gambar dan file Menu iOS, dengan tiga opsi: ambil foto, galeri foto, iCloud

Data kemudian dapat dilampirkan ke <form> atau dimanipulasi dengan JavaScript dengan memproses peristiwa onchange pada elemen input, lalu membaca properti files dari peristiwa target.

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

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

Properti files adalah objek FileList, yang akan saya bahas lebih lanjut nanti.

Secara opsional, Anda juga dapat menambahkan atribut capture ke elemen, yang menunjukkan kepada browser bahwa Anda lebih memilih untuk mendapatkan gambar dari kamera.

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

Menambahkan atribut capture tanpa nilai memungkinkan browser menentukan kamera mana yang akan digunakan, sedangkan nilai "user" dan "environment" memberi tahu browser untuk memilih kamera depan dan belakang, masing-masing.

Atribut capture berfungsi di Android dan iOS, tetapi diabaikan di desktop. Namun, perlu diperhatikan bahwa di Android, hal ini berarti pengguna tidak akan lagi memiliki opsi untuk memilih gambar yang ada. Sebagai gantinya, aplikasi kamera sistem akan dimulai secara langsung.

Tarik lalu lepas

Jika Anda sudah menambahkan kemampuan untuk mengupload file, ada beberapa cara mudah untuk membuat pengalaman pengguna sedikit lebih kaya.

Yang pertama adalah menambahkan target drop ke halaman Anda yang memungkinkan pengguna menarik file dari desktop atau aplikasi lain.

<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>

Serupa dengan input file, Anda bisa mendapatkan objek FileList dari properti dataTransfer.files dari peristiwa drop;

Pengendali peristiwa dragover memungkinkan Anda memberi tahu pengguna apa yang akan terjadi saat mereka melepas file dengan menggunakan properti dropEffect.

Tarik lalu lepas sudah ada sejak lama dan didukung dengan baik oleh browser utama.

Menempelkan dari papan klip

Cara terakhir untuk mendapatkan file gambar yang ada adalah dari papan klip. Kode untuk ini sangat sederhana, tetapi pengalaman pengguna sedikit lebih sulit untuk dilakukan dengan benar.

<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 adalah objek FileList lainnya.)

Bagian yang sulit dengan API papan klip adalah, untuk dukungan lintas browser penuh, elemen target harus dapat dipilih dan diedit. <textarea> dan <input type="text"> cocok di sini, begitu juga elemen dengan atribut contenteditable. Namun, alat ini juga jelas dirancang untuk mengedit teks.

Mungkin sulit untuk membuat ini berfungsi dengan lancar jika Anda tidak ingin pengguna dapat memasukkan teks. Trik seperti memiliki input tersembunyi yang dipilih saat Anda mengklik beberapa elemen lain dapat mempersulit pemeliharaan aksesibilitas.

Menangani objek FileList

Karena sebagian besar metode di atas menghasilkan FileList, saya harus menjelaskan sedikit tentang FileList.

FileList mirip dengan Array. Array ini memiliki kunci numerik dan properti length, tetapi sebenarnya bukan array. Tidak ada metode array, seperti forEach() atau pop(), dan tidak dapat di-iterasi. Tentu saja, Anda bisa mendapatkan Array yang sebenarnya menggunakan Array.from(fileList).

Entri FileList adalah objek File. Objek ini sama persis dengan objek Blob, kecuali bahwa objek ini memiliki properti hanya baca name dan lastModified tambahan.

<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>

Contoh ini menemukan file pertama yang memiliki jenis MIME gambar, tetapi juga dapat menangani beberapa gambar yang dipilih/disalin/di-drop sekaligus.

Setelah memiliki akses ke file, Anda dapat melakukan apa pun yang Anda inginkan dengan file tersebut. Misalnya, Anda dapat:

  • Gambarkan ke dalam elemen <canvas> sehingga Anda dapat memanipulasinya
  • Mendownloadnya ke perangkat pengguna
  • Upload ke server dengan fetch()

Mengakses kamera secara interaktif

Setelah Anda memahami dasar-dasarnya, saatnya untuk meningkatkan kualitas secara bertahap.

Browser modern dapat mendapatkan akses langsung ke kamera, sehingga Anda dapat membuat pengalaman yang terintegrasi sepenuhnya dengan halaman web, sehingga pengguna tidak perlu keluar dari browser.

Mendapatkan akses ke kamera

Anda dapat langsung mengakses kamera dan mikrofon menggunakan API dalam spesifikasi WebRTC yang disebut getUserMedia(). Tindakan ini akan meminta akses ke mikrofon dan kamera yang terhubung.

Dukungan untuk getUserMedia() cukup baik, tetapi belum tersedia di mana-mana. Secara khusus, fitur ini tidak tersedia di Safari 10 atau yang lebih lama, yang pada saat panduan ini ditulis masih merupakan versi stabil terbaru. Namun, Apple telah mengumumkan bahwa fitur ini akan tersedia di Safari 11.

Namun, dukungan sangat mudah dideteksi.

const supported = 'mediaDevices' in navigator;

Saat memanggil getUserMedia(), Anda harus meneruskan objek yang menjelaskan jenis media yang Anda inginkan. Pilihan ini disebut batasan. Ada beberapa kemungkinan batasan, yang mencakup hal-hal seperti apakah Anda lebih memilih kamera depan atau belakang, apakah Anda menginginkan audio, dan resolusi yang Anda inginkan untuk streaming.

Namun, untuk mendapatkan data dari kamera, Anda hanya memerlukan satu batasan, yaitu video: true.

Jika berhasil, API akan menampilkan MediaStream yang berisi data dari kamera, lalu Anda dapat melampirkan MediaStream ke elemen <video> dan memutarnya untuk menampilkan pratinjau real time, atau melampirkan MediaStream ke <canvas> untuk mendapatkan snapshot.

<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>

Hal ini tidak terlalu berguna. Yang dapat Anda lakukan hanyalah mengambil data video dan memainkannya. Jika ingin mendapatkan gambar, Anda harus melakukan sedikit pekerjaan tambahan.

Mengambil snapshot

Opsi terbaik yang didukung untuk mendapatkan gambar adalah menggambar frame dari video ke kanvas.

Tidak seperti Web Audio API, tidak ada API pemrosesan streaming khusus untuk video di web sehingga Anda harus menggunakan sedikit peretasan untuk mengambil snapshot dari kamera pengguna.

Prosesnya adalah sebagai berikut:

  1. Membuat objek kanvas yang akan menyimpan frame dari kamera
  2. Mendapatkan akses ke streaming kamera
  3. Melampirkannya ke elemen video
  4. Jika Anda ingin mengambil frame yang akurat, tambahkan data dari elemen video ke objek kanvas menggunakan drawImage().
<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>

Setelah memiliki data dari kamera yang disimpan di kanvas, Anda dapat melakukan banyak hal dengannya. Anda dapat:

  • Menguploadnya langsung ke server
  • Menyimpan secara lokal
  • Menerapkan efek funky ke gambar

Tips

Menghentikan streaming dari kamera jika tidak diperlukan

Sebaiknya berhenti menggunakan kamera jika Anda tidak lagi membutuhkannya. Hal ini tidak hanya akan menghemat baterai dan daya pemrosesan, tetapi juga akan memberikan kepercayaan kepada pengguna terhadap aplikasi Anda.

Untuk menghentikan akses ke kamera, Anda cukup memanggil stop() di setiap trek video untuk streaming yang ditampilkan oleh getUserMedia().

<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>

Meminta izin untuk menggunakan kamera secara bertanggung jawab

Jika pengguna belum pernah memberikan akses situs Anda ke kamera, browser akan meminta pengguna untuk memberikan izin situs Anda ke kamera saat Anda memanggil getUserMedia().

Pengguna tidak suka dimintai akses ke perangkat canggih di komputer mereka dan mereka akan sering memblokir permintaan tersebut, atau mereka akan mengabaikannya jika tidak memahami konteks pembuatan perintah. Sebaiknya hanya minta akses ke kamera saat pertama kali diperlukan. Setelah pengguna memberikan akses, mereka tidak akan ditanya lagi. Namun, jika pengguna menolak akses, Anda tidak dapat mendapatkan akses lagi, kecuali jika mereka mengubah setelan izin kamera secara manual.

Kompatibilitas

Informasi selengkapnya tentang penerapan browser seluler dan desktop:

Sebaiknya gunakan shim adapter.js untuk melindungi aplikasi dari perubahan spesifikasi WebRTC dan perbedaan awalan.

Masukan