Kullanıcının resmini kaydetme

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

Mat Ölçekler

Artık birçok tarayıcı, kullanıcının video ve ses girişine erişebilir. Ancak, tarayıcıya bağlı olarak bu tam dinamik ve satır içi bir deneyim olabilir veya kullanıcının cihazındaki başka bir uygulamaya devredilebilir. Üstelik her cihazın kamerası bile yoktur. Peki, kullanıcı tarafından oluşturulan ve her yerde işe yarayan resmin kullanıldığı bir deneyimi nasıl oluşturabilirsiniz?

Basit ve kademeli bir şekilde başlayın

Deneyiminizi kademeli olarak geliştirmek istiyorsanız, her yerde geçerli olan bir şeyle başlamanız gerekir. Yapılacak en kolay şey kullanıcıdan önceden kaydedilmiş bir dosya istemektir.

URL isteyin

Bu, en iyi desteklenen, ancak kullanıcıyı en az memnun eden seçenektir. Kullanıcıdan size bir URL vermesini sağlayın ve sonra bunu kullanın. Yalnızca resmin görüntülenmesi için bu işlem her yerde çalışır. Bir img öğesi oluşturup src öğesini ayarlayın. Hepsi bu kadar.

Resmi herhangi bir şekilde değiştirmek istiyorsanız işler biraz daha karmaşıktır. CORS, sunucu uygun üst bilgileri ayarlamadığı ve siz resmi çapraz kaynak olarak işaretlemediğiniz sürece gerçek piksellere erişmenizi engeller. Bunun tek pratik yolu proxy sunucu çalıştırmaktır.

Dosya girişi

Yalnızca resim dosyaları istediğinizi belirten accept filtresi dahil olmak üzere basit bir dosya giriş öğesi de kullanabilirsiniz.

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

Bu yöntem tüm platformlarda çalışır. 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ğına dair bir seçenek sunar. Örneğin, doğrudan kamerayla fotoğraf çekme veya mevcut bir resim dosyasını seçme imkanı sunulur.

İki seçenek içeren bir Android menüsü: Resim yakalama ve dosya alma iOS menüsünde üç seçenek var: Fotoğraf çek, fotoğraf kitaplığı, iCloud

Daha sonra veriler, giriş öğesinde onchange etkinliği dinlenip target etkinliğinin files özelliği 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 özelliği, daha sonra ayrıntılı olarak ele alacağım bir FileList nesnesidir.

Ayrıca, isteğe bağlı olarak öğeye capture özelliğini de ekleyebilirsiniz. Bu özellik, tarayıcıya kameradan resim almayı tercih ettiğinizi belirtir.

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

capture özelliğinin değer olmadan eklenmesi, hangi kameranın kullanılacağına tarayıcının karar vermesine olanak tanırken "user" ve "environment" değerleri, tarayıcıya sırasıyla ön ve arka kameraları tercih etmesini bildirir.

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

Sürükle ve bırak

Zaten dosya yükleme özelliği ekliyorsanız kullanıcı deneyimini biraz daha zengin hale getirmenin birkaç kolay yolu vardır.

İlki sayfanıza, kullanıcının masaüstünden veya başka bir uygulamadan bir 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 özelliğinden bir FileList nesnesi alabilirsiniz;

dragover etkinlik işleyici, dropEffect özelliğini kullanarak kullanıcıya dosyayı bıraktığında ne olacağını bildirmenizi sağlar.

Sürükle ve bırak işlevi uzun zamandır kullanılmaktadır ve başlıca tarayıcılar tarafından iyi desteklenmektedir.

Panodan yapıştır

Mevcut bir resim dosyasını almanın son yolu panodan kullanmaktır. Bunun kodu çok basittir, ancak kullanıcı deneyimini doğru almak 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 yine bir başka FileList nesnesidir.)

Pano API'sinin zorlu yanı, tarayıcılar arası tam destek için hedef öğenin hem seçilebilir hem de düzenlenebilir olması gerekir. Hem <textarea> hem de <input type="text">, contenteditable özelliğine sahip öğeler gibi bu işleve uyum sağlar. Ancak bunlar açıkça metin düzenlemek için tasarlanmıştır.

Kullanıcının metin girebilmesini istemiyorsanız bu işlemin sorunsuz şekilde yapılması zor olabilir. Başka bir öğeyi tıkladığınızda seçilen gizli bir girişe sahip olmak gibi püf noktaları, erişilebilirliğin korunmasını zorlaştırabilir.

FileList nesnesini işleme

Yukarıdaki yöntemlerin çoğu FileList ürettiğinden, bunun ne olduğundan biraz bahsetmem gerekir.

FileList, Array ile benzerdir. Sayısal anahtarları ve length özelliği vardır ancak aslında bir dizi değildir. forEach() veya pop() gibi dizi yöntemleri yoktur ve yinelenemez. Elbette, Array.from(fileList) kullanarak gerçek bir Dizi elde edebilirsiniz.

FileList girişleri File nesneleridir. 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 örnekte, resim MIME türüne sahip ilk dosya bulunmuştur. Ancak aynı anda birden fazla resmi seçmek/yapıştırmak/bırakmak da mümkün.

Dosyaya erişim elde ettikten sonra dosyayla istediğiniz her şeyi yapabilirsiniz. Örneğin, şunları yapabilirsiniz:

  • Değiştirebilmek için bir <canvas> öğesinin içine çizin
  • Kullanıcının cihazına indirin
  • fetch() ile bir sunucuya yükleyin

Kameraya etkileşimli olarak erişin

Temel bilgileri edindiğinize göre artık ilerledikçe kendinizi geliştirebilirsiniz.

Modern tarayıcılar kameralara doğrudan erişebilir, bu da web sayfasıyla tamamen entegre edilmiş deneyimler oluşturmanıza olanak tanır. Böylece kullanıcının tarayıcıdan hiçbir zaman ayrılması gerekmez.

Kameraya erişim izni alın

WebRTC spesifikasyonunda getUserMedia() adlı bir API kullanarak kameraya ve mikrofona doğrudan erişebilirsiniz. Kullanıcıdan bağlı mikrofon ve kameralara erişmesi istenir.

getUserMedia() desteği oldukça iyi olsa da henüz her yerde desteklenmiyor. Özellikle, bu yazı yazıldığında hâlâ en yeni kararlı sürüm olan Safari 10 veya önceki sürümlerde kullanılamıyor. Ancak Apple, Safari 11'de kullanıma sunulacağını duyurdu.

Bununla birlikte, desteği algılamak çok kolaydır.

const supported = 'mediaDevices' in navigator;

getUserMedia() yöntemini çağırırken ne tür bir medya istediğinizi açıklayan bir nesne iletmeniz gerekir. Bu seçeneklere kısıtlamalar denir. Tercih ettiğiniz ön veya arka kamera tercihiniz, ses isteyip istemediğiniz ve yayın için tercih ettiğiniz çözünürlük gibi birkaç olası kısıtlama vardır.

Ancak kameradan veri almak için tek bir kısıtlamanız vardır, bu da video: true olur.

Başarılı olursa API, kameradan veri içeren bir MediaStream döndürür. Ardından, bu öğeyi bir <video> öğesine ekleyip gerçek zamanlı bir önizleme göstermek için oynatabilir veya anlık görüntü almak için <canvas> öğesine ekleyebilirsiniz.

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

Bu tek başına pek faydalı değil. Tek yapmanız gereken video verilerini alıp oynatmaktır. Bir resim almak istiyorsanız ekstra bir işlem yapmanız gerekir.

Anlık görüntü al

Resim almak için desteklenen en iyi seçenek, videodan kanvasa bir kare çizmektir.

Web Audio API'nın aksine, web üzerinde video için özel bir akış işleme API'si yoktur. Bu nedenle, kullanıcının kamerasından anlık görüntü yakalamak için küçük bir bilgisayar korsanlığına başvurmanız gerekir.

Süreç aşağıdaki gibidir:

  1. Çerçeveyi kameradan tutacak bir tuval nesnesi oluşturun
  2. Kamera akışına erişin
  3. Bir video öğesine ekleyin
  4. Hassas bir kare yakalamak istediğinizde, video öğesindeki verileri drawImage() kullanarak bir kanvas nesnesine ekleyin.
<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>

Kameradan alınan veriler kanvasta depolandıktan sonra bunlarla birçok işlem yapabilirsiniz. Şunları yapabilirsiniz:

  • Doğrudan sunucuya yükleyin
  • Yerel olarak depolayın
  • Resme ilginç efektler uygulayın

İpuçları

Gerekmediğinde kameradan yayın yapmayı durdurun

Artık ihtiyaç duymadığınızda kamerayı kullanmayı bırakmak iyi bir uygulamadır. Bu, pil ve işlem gücünden tasarruf etmekle kalmaz, 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() komutunu çağırmanız yeterlidir.

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

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

Kullanıcı daha önce sitenizin kameraya erişmesine izin vermediyse getUserMedia() çağrısı yaptığınız anda tarayıcı, kullanıcıdan sitenizin kameraya erişim izni vermesini ister.

Kullanıcılar, makinelerindeki güçlü cihazlara erişim istenmesinden nefret ederler ve bu isteği sık sık engellerler veya istemin oluşturulduğu bağlamı anlamadıkları takdirde bunu göz ardı ederler. Kameraya yalnızca ilk gerektiğinde erişim izni istemek en iyi uygulamadır. Kullanıcı erişim verdikten sonra bu istek bir daha sorulmaz. Ancak kullanıcı erişimi reddederse kamera izni ayarları manuel olarak değiştirilmediği sürece tekrar erişemezsiniz.

Uyumluluk

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

Uygulamaları WebRTC spesifikasyon değişikliklerinden ve önek farklarından korumak için adapter.js dolgusunu da kullanmanızı öneririz.

Geri bildirim