Kullanıcının resmini kaydetme

Çoğu tarayıcı, kullanıcının kamerasına erişebilir.

Birçok tarayıcı artık kullanıcıdan gelen video ve ses girişine erişebiliyor. Ancak tarayıcıya bağlı olarak tam dinamik ve satır içi bir deneyim olabilir veya kullanıcının cihazındaki başka bir uygulamaya yetkilendirilebilir. Ayrıca her cihazda kamera yoktur. Peki, her yerde iyi çalışan, kullanıcı tarafından oluşturulan bir resim kullanan bir deneyim nasıl oluşturabilirsiniz?

Basit ve kademeli bir başlangıç yapın

Deneyiminizi aşamalı olarak iyileştirmek istiyorsanız her yerde kullanılabilen bir şeyle başlamanız gerekir. En kolay yöntem, kullanıcıdan önceden kaydedilmiş bir dosya istemektir.

URL isteme

Bu, en iyi desteklenen ancak en az tatmin edici seçenektir. Kullanıcıdan size bir URL vermesini isteyin ve bu URL'yi kullanın. Bu yöntem, resmin yalnızca gösterilmesi için her yerde kullanılabilir. Bir img öğesi oluşturun, src değerini ayarlayın ve işlemi tamamlayın.

Ancak resim üzerinde herhangi bir şekilde işlem yapmak istiyorsanız işler biraz daha karmaşıktır. CORS, sunucu uygun başlıkları ayarlamazsa ve resmi crossorigin olarak işaretlemezseniz gerçek piksellere erişmenizi engeller. Bunun tek pratik yolu bir proxy sunucusu çalıştırmaktır.

Dosya girişi

Yalnızca resim dosyaları istediğinizi belirten bir accept filtresi içeren basit bir dosya giriş öğesi de kullanabilirsiniz.

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

Bu yöntem tüm platformlarda kullanılabilir. Masaüstünde, kullanıcıdan dosya sisteminden bir resim dosyası yüklemesi istenir. iOS ve Android'deki Chrome ve Safari'de bu yöntem, kullanıcıya resmi çekmek için hangi uygulamayı kullanacağını seçme seçeneği sunar. Doğrudan kamerayla fotoğraf çekme veya mevcut bir resim dosyasını seçme seçenekleri de buna dahildir.

Resim ve dosya yakalama seçeneklerini içeren bir Android menüsü Fotoğraf çekme, fotoğraf arşivi ve iCloud seçeneklerini içeren iOS menüsü

Ardından veriler, giriş öğesinde bir onchange etkinliği dinlenerek ve ardından etkinliğin files özelliği target okunarak bir <form> öğesine eklenebilir veya JavaScript ile değiştirilebilir.

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

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

files mülkü bir FileList nesnesi olup daha sonra daha ayrıntılı olarak ele alınacaktır.

İsterseniz öğeye capture özelliğini de ekleyebilirsiniz. Bu özellik, tarayıcıya kameradan resim çekmeyi tercih ettiğinizi gösterir.

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

capture özelliğini değer olmadan eklemek, hangi kameranın kullanılacağına tarayıcıya karar vermesine olanak tanır. "user" ve "environment" değerleri ise tarayıcıya sırasıyla ön ve arka kameraları tercih etmesini söyler.

capture özelliği Android ve iOS'te çalışır ancak masaüstünde yoksayılır. Ancak Android'de bu, kullanıcının artık mevcut bir resmi seçme seçeneğinin olmayacağı anlamına gelir. Bunun yerine sistem kamerası uygulaması doğrudan başlatılır.

Sürükle ve bırak

Dosya yükleme özelliğini zaten ekliyorsanız kullanıcı deneyimini biraz daha zenginleştirmenin birkaç kolay yolu vardır.

Bunlardan ilki, sayfanıza kullanıcının masaüstünden veya başka bir uygulamadan dosya sürüklemesine olanak tanıyan bir bırakma hedefi eklemektir.

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

Dosya girişine benzer şekilde, drop etkinliğinin dataTransfer.files mülkünden bir FileList nesnesi alabilirsiniz;

dragover etkinlik işleyicisi, dropEffect mülkünü kullanarak kullanıcıya dosyayı bıraktığında ne olacağını bildirmenize olanak tanır.

Sürükle ve bırak özelliği uzun zamandır kullanılıyor ve büyük tarayıcılar tarafından iyi bir şekilde destekleniyor.

Panodan yapıştırma

Mevcut bir resim dosyasını almanızı sağlayan son yöntem ise panodur. Bunun kodu çok basittir ancak kullanıcı deneyimini doğru şekilde oluşturmak biraz daha zordur.

<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 başka bir FileList nesnesidir.)

Panoya API'sinin zor kısmı, tam tarayıcı desteği için hedef öğenin hem seçilebilir hem de düzenlenebilir olması gerektiğidir. Hem <textarea> hem de <input type="text">, contenteditable özelliğine sahip öğeler gibi bu ölçütlere uyar. Ancak bunlar, metin düzenlemek için de tasarlanmıştır.

Kullanıcının metin girmesini istemiyorsanız bu işlemin sorunsuz şekilde çalışmasını sağlamak zor olabilir. Başka bir öğeyi tıkladığınızda seçilen gizli bir girişe sahip olmak gibi püf noktaları, erişilebilirliği korumayı zorlaştırabilir.

FileList nesnesini işleme

Yukarıdaki yöntemlerin çoğu FileList oluşturduğundan, bunun ne olduğundan biraz bahsetmem gerekiyor.

FileList, Array'a benzer. Sayısal anahtarları ve length mülkü vardır ancak aslında bir dizi değildir. forEach() veya pop() gibi dizi yöntemleri yoktur ve dizi yinelenebilir değildir. Elbette Array.from(fileList) kullanarak gerçek bir dizi de alabilirsiniz.

FileList girişleri File nesnesi şeklindedir. Bunlar, ek name ve lastModified salt okunur özelliklerine sahip olmaları dışında Blob nesneleriyle tamamen aynıdır.

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

Bu örnek, resim MIME türüne sahip ilk dosyayı bulur ancak aynı anda birden fazla resmin seçilmesi/yapıştırılması/bırakılması işlemini de gerçekleştirebilir.

Dosyaya eriştikten sonra istediğiniz işlemleri yapabilirsiniz. Örneğin, şunları yapabilirsiniz:

  • Düzenleyebilmeniz için bir <canvas> öğesine çizin
  • Kullanıcının cihazına indirin
  • fetch() ile bir sunucuya yükleyin

Kameraya etkileşimli olarak erişme

Temel bilgileri öğrendiğinize göre şimdi sıra aşamalı olarak geliştirmede.

Modern tarayıcılar kameralara doğrudan erişebilir. Bu sayede, web sayfasıyla tamamen entegre deneyimler oluşturabilirsiniz. Böylece kullanıcının tarayıcıdan çıkması gerekmez.

Kameraya erişim elde etme

WebRTC spesifikasyonundaki getUserMedia() adlı bir API'yi kullanarak kameraya ve mikrofona doğrudan erişebilirsiniz. Bu işlem, kullanıcıdan bağlı mikrofonlara ve kameralara erişim izni ister.

getUserMedia() için destek oldukça iyidir ancak henüz her yerde kullanılamaz. Özellikle, Safari 10 veya önceki sürümlerde kullanılamaz. Bu sürüm, makalenin yazıldığı sırada en son kararlı sürümdür. Ancak Apple, Safari 11'de kullanılabileceğini duyurdu.

Ancak desteği tespit etmek çok kolaydır.

const supported = 'mediaDevices' in navigator;

getUserMedia() işlevini çağırırken ne tür bir medya istediğinizi açıklayan bir nesne iletmeniz gerekir. Bu seçimlere kısıtlama denir. Ön veya arka kamera tercihiniz, ses ekleme tercihiniz ve yayın için tercih ettiğiniz çözünürlük gibi çeşitli kısıtlamalar olabilir.

Ancak kameradan veri almak için yalnızca bir kısıtlamaya ihtiyacınız vardır: video: true.

İşlem başarılı olursa API, kameradan alınan verileri içeren bir MediaStream döndürür. Ardından bu öğeyi bir <video> öğesine ekleyip oynatarak gerçek zamanlı önizleme yapabilir veya <canvas> öğesine ekleyip anlık görüntü alabilirsiniz.

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

Bu bilgi tek başına çok faydalı değildir. Tek yapmanız gereken video verilerini alıp oynatmak. Resim almak istiyorsanız biraz daha çalışmanız gerekir.

Anlık görüntü alma

Resim almak için en iyi seçenek, videodan kanvas üzerine çerçeve çizmektir.

Web Audio API'nin aksine, web'de video için özel bir akış işleme API'si yoktur. Bu nedenle, kullanıcının kamerasından anlık görüntü almak için biraz kodlama yapmanız gerekir.

Süreç şu şekildedir:

  1. Kameradan gelen kareyi barındıracak bir kanvas nesnesi oluşturun
  2. Kamera akışına erişim
  3. Bir video öğesine ekleyin.
  4. Tam bir kare yakalamak istediğinizde drawImage() kullanarak video öğesindeki verileri bir kanvas nesnesine ekleyin.
<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>

Kameradan alınan verileri kanvasta depoladıktan sonra bu verilerle birçok işlem yapabilirsiniz. Şunları yapabilirsiniz:

  • Doğrudan sunucuya yükleyin
  • Yerel olarak depolama
  • Resme eğlenceli efektler uygulama

İpuçları

Gerekmediği durumlarda kameradan yayını durdurma

Artık ihtiyacınız olmadığında kamerayı kullanmayı bırakmak iyi bir uygulamadır. Bu, yalnızca pil ve işlem gücü tasarrufu sağlamaz, aynı zamanda kullanıcıların uygulamanıza güvenmesini de sağlar.

Kameraya erişimi durdurmak için getUserMedia() tarafından döndürülen akış için her video kanalında stop() işlevini çağırmanız yeterlidir.

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

Kamerayı sorumlu bir şekilde kullanmak için izin isteme

Kullanıcı daha önce sitenize kamera erişimi vermemişse getUserMedia() çağrısını yaptığınız anda tarayıcı, kullanıcıdan sitenize kamera erişimi vermesini ister.

Kullanıcılar, makinelerindeki güçlü cihazlara erişim isteğinde bulunulmasından nefret eder ve isteği sık sık engeller ya da istemin oluşturulma bağlamını anlamazlarsa yoksayar. Kameraya erişmek için yalnızca ilk kez ihtiyaç duyulduğunda izin istemek en iyi uygulamadır. Kullanıcı erişim izni verdikten sonra bu istek tekrar gösterilmez. Ancak kullanıcı erişim iznini reddederse kamera izni ayarlarını manuel olarak değiştirmediği sürece tekrar erişim izni alamazsınız.

Uyumluluk

Mobil ve masaüstü tarayıcı uygulaması hakkında daha fazla bilgi:

Ayrıca, uygulamaları WebRTC spesifikasyonu değişikliklerine ve ön ek farklılıklarına karşı korumak için adapter.js ara yazılımını kullanmanızı öneririz.

Geri bildirim