Mengambil gambar dari pengguna

Sebagian besar browser bisa mendapatkan akses ke kamera pengguna.

Timbangan Mat

Sekarang banyak browser dapat mengakses input video dan audio dari pengguna. Namun, bergantung pada browser, hal ini mungkin berupa pengalaman yang sepenuhnya dinamis dan inline, atau dapat didelegasikan ke aplikasi lain di perangkat pengguna. Selain itu, tidak semua perangkat memiliki kamera. Jadi, bagaimana cara menciptakan pengalaman menggunakan gambar buatan pengguna yang berfungsi dengan baik di mana saja?

Mulai secara sederhana dan bertahap

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

Minta URL

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

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

Input file

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

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

Metode ini berfungsi pada semua platform. Di desktop, perintah ini akan meminta pengguna untuk mengunggah 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 sudah ada.

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

Data tersebut 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 suka 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.

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

Tarik lalu lepas

Jika Anda sudah menambahkan kemampuan untuk mengupload file, ada beberapa cara mudah untuk memperkaya pengalaman pengguna.

Yang pertama adalah menambahkan target lepas ke halaman 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 peristiwa drop;

Pengendali peristiwa dragover memungkinkan Anda memberi tahu pengguna apa yang akan terjadi jika mereka menghapus file menggunakan properti dropEffect.

Seret dan lepas telah ada sejak lama dan didukung dengan baik oleh browser utama.

Tempel dari papan klip

Cara terakhir untuk mendapatkan {i>file<i} gambar yang ada adalah dari {i>clipboard<i}. Kode untuk ini sangat sederhana, tetapi pengalaman pengguna sedikit lebih sulit untuk diselesaikan 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 belum menjadi objek FileList lainnya.)

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

Mungkin sulit untuk membuatnya 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 membuat pemeliharaan aksesibilitas menjadi lebih sulit.

Menangani objek FileList

Karena sebagian besar metode di atas menghasilkan FileList, saya harus sedikit membahas apa itu.

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

Entri FileList adalah objek File. Objek ini sama persis dengan objek Blob, kecuali jika objek tersebut 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/ditempel/dilepas sekaligus.

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

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

Mengakses kamera secara interaktif

Setelah mengetahui dasar-dasarnya, saatnya untuk meningkatkan kualitasnya secara bertahap!

Browser modern dapat memperoleh akses langsung ke kamera, sehingga Anda dapat menciptakan pengalaman yang terintegrasi sepenuhnya dengan halaman web, sehingga pengguna tidak perlu meninggalkan browser.

Mendapatkan akses ke kamera

Anda dapat langsung mengakses kamera dan mikrofon dengan menggunakan API di spesifikasi WebRTC yang disebut getUserMedia(). Tindakan ini akan meminta pengguna untuk mengakses mikrofon dan kamera yang terhubung.

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

Namun, sangat mudah untuk mendeteksi dukungan.

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 batasan yang mungkin, yang mencakup hal-hal seperti apakah Anda lebih memilih kamera depan atau belakang, apakah Anda menginginkan audio, dan resolusi pilihan Anda untuk streaming.

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

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

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

Dengan sendirinya, ini tidak terlalu berguna. Anda hanya dapat mengambil data video dan memutarnya. Jika ingin mendapatkan gambar, Anda harus melakukan sedikit upaya ekstra.

Ambil snapshot

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

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

Prosesnya adalah sebagai berikut:

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

Setelah data dari kamera disimpan di kanvas, Anda dapat melakukan banyak hal dengan kamera tersebut. Anda dapat:

  • Mengupload file langsung ke server
  • Menyimpannya secara lokal
  • Terapkan efek keren pada gambar

Tips

Hentikan streaming dari kamera saat tidak diperlukan

Sebaiknya berhenti menggunakan kamera saat Anda tidak lagi membutuhkannya. Tindakan ini tidak hanya akan menghemat baterai dan daya pemrosesan, tetapi juga akan memberikan kepercayaan kepada pengguna pada 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 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>

Minta izin untuk menggunakan kamera secara bertanggung jawab

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

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 permintaan tersebut dibuat. Praktik terbaik adalah hanya meminta akses ke kamera saat pertama kali diperlukan. Setelah pengguna diberikan akses, mereka tidak akan ditanya lagi. Namun, jika pengguna menolak akses, Anda tidak bisa mendapatkan akses lagi, kecuali jika pengguna mengubah setelan izin kamera secara manual.

Kompatibilitas

Informasi selengkapnya tentang implementasi browser seluler dan desktop:

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

Masukan