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 aşamalı bir şekilde başlayın

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

URL isteyin

Bu, en iyi desteklenen ancak en az tatmin edici seçenektir. Kullanıcıdan size bir URL vermesini isteyin, sonra bunu kullanın. Yalnızca resmi görüntülemek için bu yöntem her yerde işe yarar. Bir img öğesi oluşturup src değerini ayarlayın. Hepsi bu kadar.

Ancak resim üzerinde herhangi bir şekilde değişiklik yapmak isterseniz işler biraz daha karmaşık hale gelir. CORS, sunucu uygun başlıkları ayarlamadığı ve resmi çapraz kaynak olarak işaretlemediğiniz sürece gerçek piksellere erişmenizi engeller. Bunun için tek pratik yol 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 kamera uygulaması doğrudan başlatılacak.

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 süredir kullanılıyor ve önde gelen 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, 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öntemi yoktur ve yinelenemez. Elbette Array.from(fileList) kullanarak gerçek bir dizi de alabilirsiniz.

FileList girişleri File nesnedir. 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şlemi yapabilirsiniz. Örneğin, şunları yapabilirsiniz:

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

Kameraya etkileşimli olarak erişme

Temelleri ele aldığınıza göre artık onu daha da geliştirmenin zamanı geldi.

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 kamera ve mikrofona doğrudan erişebilirsiniz. Bu işlem, kullanıcıdan bağlı mikrofon 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.

Bununla birlikte, desteğin tespit edilmesi çok basittir.

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 adı verilir. Ö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>

Tek başına bu pek faydalı değildir. Tek yapmanız gereken video verilerini alıp oynatmaktır. 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 da olsa bilgisayar korsanlığı 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. Hassas bir kare yakalamak istediğinizde video öğesindeki verileri drawImage() kullanarak bir tuval 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 gelen verileri tuvalde depoladıktan sonra bu verilerle birçok işlem yapabilirsiniz. Şunları yapabilirsiniz:

  • Doğrudan sunucuya yükleyin
  • Yerel olarak depolayın
  • Resme eğlenceli efektler uygulama

İpuçları

Gerektiğinde kameradan akışı durdurun

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 isteyin

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 istenmesinden nefret eder ve isteği sık sık engeller veya istemin oluşturulduğu bağlamı anlamadıklarında isteği yoksayar. Kameraya erişimi yalnızca ilk kez ihtiyaç duyulduğunda 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