گرفتن تصویر از کاربر

اکثر مرورگرها می توانند به دوربین کاربر دسترسی داشته باشند.

اکنون بسیاری از مرورگرها این امکان را دارند که به ورودی های صوتی و تصویری کاربر دسترسی داشته باشند. با این حال، بسته به مرورگر، ممکن است یک تجربه کامل پویا و درون خطی باشد، یا می‌تواند به برنامه دیگری در دستگاه کاربر واگذار شود. علاوه بر این، هر دستگاهی حتی دوربین ندارد. بنابراین چگونه می توانید تجربه ای ایجاد کنید که از یک تصویر تولید شده توسط کاربر استفاده می کند که در همه جا به خوبی کار می کند؟

ساده و به تدریج شروع کنید

اگر می خواهید تجربه خود را به تدریج افزایش دهید، باید با چیزی شروع کنید که در همه جا کار می کند. ساده ترین کار این است که به سادگی از کاربر یک فایل از پیش ضبط شده بخواهید.

یک URL بخواهید

این بهترین گزینه پشتیبانی شده اما کمتر رضایت بخش است. از کاربر بخواهید یک URL به شما بدهد و سپس از آن استفاده کنید. فقط برای نمایش تصویر این کار در همه جا کار می کند. یک عنصر img ایجاد کنید، src تنظیم کنید و کارتان تمام شد.

اگرچه، اگر بخواهید تصویر را به هر طریقی دستکاری کنید، همه چیز کمی پیچیده تر است. CORS از دسترسی شما به پیکسل های واقعی جلوگیری می کند مگر اینکه سرور هدرهای مناسب را تنظیم کند و شما تصویر را به عنوان منبع متقاطع علامت گذاری کنید . تنها راه عملی این است که یک سرور پروکسی را اجرا کنید.

ورودی فایل

همچنین می توانید از یک عنصر ورودی فایل ساده استفاده کنید، از جمله فیلتر accept که نشان می دهد شما فقط فایل های تصویری را می خواهید.

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

این روش روی همه پلتفرم ها کار می کند. در دسکتاپ از کاربر می خواهد که یک فایل تصویری را از سیستم فایل آپلود کند. در کروم و سافاری در iOS و اندروید، این روش به کاربر این امکان را می دهد که از کدام برنامه برای گرفتن تصویر استفاده کند، از جمله گزینه عکس گرفتن مستقیم با دوربین یا انتخاب یک فایل تصویر موجود.

منوی اندروید، با دو گزینه: گرفتن تصویر و فایلیک منوی iOS، با سه گزینه: گرفتن عکس، کتابخانه عکس، iCloud

سپس داده‌ها را می‌توان به <form> متصل کرد یا با گوش دادن به رویداد onchange در عنصر ورودی و سپس خواندن ویژگی files 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>

ویژگی files یک شی FileList است که در ادامه بیشتر در مورد آن صحبت خواهم کرد.

همچنین می‌توانید به صورت اختیاری ویژگی capture را به عنصر اضافه کنید، که به مرورگر نشان می‌دهد که ترجیح می‌دهید تصویری از دوربین دریافت کنید.

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

با افزودن ویژگی capture بدون مقدار، مرورگر تصمیم می‌گیرد از کدام دوربین استفاده کند، در حالی که مقادیر "user" و "environment" به مرورگر می‌گویند که به ترتیب دوربین‌های جلو و عقب را ترجیح دهد.

ویژگی capture در اندروید و iOS کار می کند، اما در دسکتاپ نادیده گرفته می شود. اما توجه داشته باشید که در اندروید این به این معنی است که کاربر دیگر گزینه انتخاب تصویر موجود را نخواهد داشت. در عوض، برنامه دوربین سیستم مستقیماً راه اندازی می شود.

کشیدن و انداختن

اگر از قبل قابلیت آپلود فایل را اضافه کرده اید، چند راه آسان وجود دارد که می توانید تجربه کاربری را کمی غنی تر کنید.

اولین مورد این است که یک هدف دراپ را به صفحه خود اضافه کنید که به کاربر اجازه می دهد یک فایل را از دسکتاپ یا برنامه دیگری بکشد.

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

مشابه ورودی فایل، می‌توانید یک شی FileList را از ویژگی dataTransfer.files رویداد drop دریافت کنید.

کنترل کننده رویداد dragover به شما امکان می دهد با استفاده از ویژگی dropEffect به کاربر سیگنال دهید که چه اتفاقی می افتد زمانی که فایل را رها کند.

کشیدن و رها کردن برای مدت طولانی وجود داشته است و به خوبی توسط مرورگرهای اصلی پشتیبانی می شود.

چسباندن از کلیپ بورد

راه نهایی برای دریافت فایل تصویر موجود از کلیپ بورد است. کد برای این بسیار ساده است، اما تجربه کاربری کمی سخت تر است.

<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 یک شی FileList دیگر است.)

بخش مشکل با API کلیپ بورد این است که برای پشتیبانی کامل از مرورگر متقابل، عنصر هدف باید هم قابل انتخاب و هم قابل ویرایش باشد. هر دو <textarea> و <input type="text"> در اینجا مطابقت دارند، همانطور که عناصر با ویژگی contenteditable مناسب هستند. اما بدیهی است که اینها برای ویرایش متن نیز طراحی شده اند.

اگر نمی خواهید کاربر قادر به وارد کردن متن نباشد، انجام این کار به راحتی ممکن است دشوار باشد. ترفندهایی مانند داشتن ورودی مخفی که با کلیک بر روی عنصر دیگری انتخاب می‌شود، ممکن است حفظ دسترسی را سخت‌تر کند.

مدیریت یک شی FileList

از آنجایی که بیشتر روش‌های بالا یک FileList تولید می‌کنند، باید کمی در مورد چیستی آن صحبت کنم.

یک FileList شبیه یک Array است. دارای کلیدهای عددی و ویژگی length است، اما در واقع یک آرایه نیست. هیچ روش آرایه ای مانند forEach() یا pop() وجود ندارد و قابل تکرار نیست. البته، با استفاده از Array.from(fileList) می توانید یک آرایه واقعی دریافت کنید.

ورودی های FileList اشیاء File هستند. اینها دقیقاً مشابه اشیاء Blob هستند با این تفاوت که دارای name اضافی و ویژگی های فقط خواندنی lastModified هستند.

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

این مثال اولین فایلی را پیدا می‌کند که دارای نوع MIME تصویری است، اما همچنین می‌تواند چندین تصویر را که همزمان انتخاب، چسبانده یا رها می‌شوند، مدیریت کند.

پس از دسترسی به فایل می توانید هر کاری که می خواهید با آن انجام دهید. به عنوان مثال، شما می توانید:

  • آن را در یک عنصر <canvas> بکشید تا بتوانید آن را دستکاری کنید
  • آن را در دستگاه کاربر دانلود کنید
  • آن را با fetch() در سرور آپلود کنید

به صورت تعاملی به دوربین دسترسی داشته باشید

اکنون که پایه های خود را پوشانده اید، زمان آن رسیده است که به تدریج تقویت کنید!

مرورگرهای مدرن می توانند به دوربین ها دسترسی مستقیم داشته باشند و به شما امکان می دهند تجربیاتی ایجاد کنید که به طور کامل با صفحه وب یکپارچه شده است، بنابراین کاربر هرگز نیازی به ترک مرورگر ندارد.

به دوربین دسترسی پیدا کنید

با استفاده از یک API در مشخصات WebRTC به نام getUserMedia() می توانید مستقیماً به دوربین و میکروفون دسترسی داشته باشید. این کار باعث می شود که کاربر به میکروفون و دوربین متصل خود دسترسی پیدا کند.

پشتیبانی از getUserMedia() بسیار خوب است، اما هنوز در همه جا وجود ندارد. به طور خاص، در سافاری 10 یا پایین تر، که در زمان نگارش هنوز آخرین نسخه پایدار است، در دسترس نیست. با این حال، اپل اعلام کرده است که در سافاری 11 در دسترس خواهد بود.

با این حال، تشخیص پشتیبانی بسیار ساده است.

const supported = 'mediaDevices' in navigator;

هنگامی که getUserMedia() را فرا می‌خوانید، باید یک شی را ارسال کنید که نوع رسانه‌ای را که می‌خواهید توضیح دهد. به این انتخاب ها محدودیت می گویند. چندین محدودیت احتمالی وجود دارد، مواردی مانند اینکه آیا دوربین جلو یا عقب را ترجیح می‌دهید، صدا می‌خواهید و وضوح دلخواه شما برای پخش جریانی را پوشش می‌دهد.

با این حال، برای دریافت داده از دوربین، فقط به یک محدودیت نیاز دارید و آن video: true .

در صورت موفقیت آمیز بودن API، MediaStream حاوی داده‌های دوربین را برمی‌گرداند، و سپس می‌توانید آن را به عنصر <video> متصل کنید و آن را پخش کنید تا یک پیش‌نمایش در زمان واقعی نشان داده شود، یا آن را به <canvas> وصل کنید تا یک عکس فوری دریافت کنید. .

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

به خودی خود، این چندان مفید نیست. تنها کاری که می توانید انجام دهید این است که داده های ویدئویی را بردارید و آن را پخش کنید. اگر می خواهید تصویری به دست آورید، باید کمی کار اضافی انجام دهید.

یک عکس فوری بگیرید

بهترین گزینه پشتیبانی شده شما برای گرفتن یک تصویر کشیدن یک فریم از ویدیو روی بوم است.

برخلاف Web Audio API، یک API اختصاصی پردازش جریانی برای ویدیو در وب وجود ندارد، بنابراین برای گرفتن عکس فوری از دوربین کاربر باید به کمی هکر متوسل شوید.

این فرایند به شرح زیر است:

  1. یک شی بوم ایجاد کنید که کادر را از دوربین نگه دارد
  2. به جریان دوربین دسترسی پیدا کنید
  3. آن را به یک عنصر ویدیو وصل کنید
  4. وقتی می‌خواهید یک فریم دقیق بگیرید، داده‌های عنصر ویدیو را با استفاده از 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>

هنگامی که داده های دوربین را در بوم ذخیره کردید، می توانید کارهای زیادی را با آن انجام دهید. شما می توانید:

  • آن را مستقیماً در سرور آپلود کنید
  • آن را به صورت محلی ذخیره کنید
  • افکت های بد بو روی تصویر اعمال کنید

نکات

در صورت عدم نیاز، پخش جریانی از دوربین را متوقف کنید

تمرین خوبی است که وقتی دیگر به دوربین نیاز ندارید، استفاده از آن را متوقف کنید. این نه تنها باعث صرفه جویی در مصرف باتری و قدرت پردازش می شود، بلکه به کاربران نسبت به برنامه شما اطمینان می دهد.

برای متوقف کردن دسترسی به دوربین، می‌توانید به سادگی stop() در هر آهنگ ویدیویی برای جریانی که توسط 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>

برای استفاده مسئولانه از دوربین اجازه بگیرید

اگر کاربر قبلاً به سایت شما اجازه دسترسی به دوربین را نداده باشد، لحظه ای که مرورگر getUserMedia() فراخوانی می کنید، از کاربر می خواهد که مجوز سایت شما را به دوربین بدهد.

کاربران از دریافت درخواست برای دسترسی به دستگاه‌های قدرتمند در دستگاه خود متنفرند و اغلب این درخواست را مسدود می‌کنند، یا اگر زمینه ایجاد درخواست را درک نکنند، آن را نادیده می‌گیرند. بهترین تمرین این است که فقط در صورت نیاز، درخواست دسترسی به دوربین را داشته باشید. هنگامی که کاربر اجازه دسترسی را داد، دیگر از او درخواست نخواهد شد. با این حال، اگر کاربر دسترسی را رد کند، نمی‌توانید دوباره دسترسی داشته باشید، مگر اینکه تنظیمات مجوز دوربین را به صورت دستی تغییر دهد.

سازگاری

اطلاعات بیشتر در مورد پیاده سازی مرورگر موبایل و دسکتاپ:

ما همچنین استفاده از adapter.js shim را برای محافظت از برنامه‌ها در برابر تغییرات مشخصات WebRTC و تفاوت‌های پیشوندی توصیه می‌کنیم.

بازخورد