يمكن لمعظم المتصفحات الوصول إلى كاميرا المستخدم.
يمكن الآن للعديد من المتصفّحات الوصول إلى إدخال الفيديو والصوت من العميل. مع ذلك، قد يتم تنفيذ تلك العملية بشكل ديناميكي ومضمّن في المتصفّح، أو قد يتم تفويضها إلى تطبيق آخر على جهاز المستخدم. بالإضافة إلى ذلك، لا يحتوي كل جهاز على كاميرا. إذن كيف يمكنك إنشاء تجربة تستخدم صورة من إنشاء المستخدم تعمل بشكل جيد في كل مكان؟
البدء ببساطة وتدريج
إذا كنت تريد تحسين تجربتك تدريجيًا، عليك البدء بشيء يعمل في كل مكان. أسهل شيء يمكنك القيام به هو أن تطلب من المستخدم تقديم ملف مسجل مسبقًا.
طلب عنوان URL
هذا هو الخيار الأفضل ملاءمةً ولكنّه الأقل إرضاءً. اطلب من المستخدم تقديم عنوان URL، ثم
استخدِمه. يمكن استخدام هذه الطريقة في كل مكان لعرض الصورة فقط. أنشئ عنصر img
، واضبط القيمة
src
، وانتهى الأمر.
ومع ذلك، إذا كنت تريد التلاعب بالصورة بأي شكل من الأشكال، يصبح الأمر أكثر تعقيدًا. يمنع بروتوكول مشاركة الموارد المشتركة المنشأ (CORS) من الوصول إلى وحدات البكسل الفعلية ما لم يضبط الخادم العناوين المناسبة وتضع علامة على الصورة بأنّها crossorigin ، والطريقة العملية الوحيدة للتعامل مع ذلك هي تشغيل خادم وكيل.
إدخال الملف
يمكنك أيضًا استخدام عنصر إدخال ملف بسيط، بما في ذلك فلتر accept
الذي يشير إلى أنّك تريد
ملفّات الصور فقط.
<input type="file" accept="image/*" />
تعمل هذه الطريقة على جميع المنصات. على الكمبيوتر المكتبي، سيُطلب من المستخدم تحميل ملف صورة من نظام الملفات. في Chrome وSafari على أجهزة iOS وAndroid، ستمنح هذه الطريقة المستخدم خيار تحديد التطبيق الذي سيتم استخدامه لالتقاط الصورة، بما في ذلك خيار التقاط صورة مباشرةً باستخدام الكاميرا أو اختيار ملف صورة حالي.
يمكن بعد ذلك إرفاق البيانات بعنصر <form>
أو التلاعب بها باستخدام JavaScript من خلال
الاستماع إلى حدث 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
على أجهزة Android وiOS، ولكن يتم تجاهلها على أجهزة الكمبيوتر المكتبي. يُرجى العِلم أنّه لن يتوفّر للمستخدم على أجهزة Android خيار اختيار
صورة حالية. سيتم تشغيل تطبيق كاميرا النظام مباشرةً بدلاً من ذلك.
السحب والإفلات
إذا كنت تضيف حاليًا إمكانية تحميل ملف، هناك طريقتان سهلتان يمكنك من خلالهما تحسين تجربة المستخدم.
الخيار الأول هو إضافة هدف إسقاط إلى صفحتك يسمح للمستخدم بسحب ملف من سطح المكتب أو تطبيق آخر.
<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
آخر.)
إنّ الجزء الصعب في واجهة برمجة التطبيقات لحافظة النصوص هو أنّه لكي تكون متوافقة مع جميع المتصفّحات، يجب أن يكون العنصر المستهدَف قابلاً للاختيار والتحرير. تتوافق كل من السمتَين <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()
الوصول إلى الكاميرا بشكل تفاعلي
الآن بعد أن كنت قد تناولت القواعد الأساسية، حان وقت التحسين التدريجي!
يمكن للمتصفحات الحديثة الوصول مباشرةً إلى الكاميرات، ما يتيح لك إنشاء تجارب متكاملة تمامًا مع صفحة الويب بحيث لا يحتاج المستخدم إلى مغادرة المتصفح إطلاقًا.
الحصول على إذن الوصول إلى الكاميرا
يمكنك الوصول مباشرةً إلى الكاميرا والميكروفون باستخدام واجهة برمجة تطبيقات في مواصفات WebRTC التي تُسمى getUserMedia()
. سيطلب هذا من المستخدم منح إذن بالوصول إلى الكاميرات والميكروفونات المتصلة.
إنّ ميزة getUserMedia()
متاحة بشكل جيد، ولكنّها ليست متاحة في كل مكان بعد. وعلى وجه التحديد، لا يتوفّر هذا الوضع في Safari 10 أو الإصدارات الأقدم، والذي لا يزال في وقت كتابة هذا الإصدار هو أحدث إصدار ثابت.
ومع ذلك، أعلنت Apple
أنها ستكون متاحة في Safari 11.
ومع ذلك، من السهل جدًا رصد المحتوى الذي يتضمّن دعمًا.
const supported = 'mediaDevices' in navigator;
عند استدعاء getUserMedia()
، عليك تمرير عنصر يصف نوع الوسائط التي تريد
استخدامها. تسمى هذه الخيارات القيود. هناك عدة قيود محتملة، تشمل
أمورًا مثل ما إذا كنت تفضّل استخدام كاميرا أمامية أو خلفية، وما إذا كنت تريد تضمين الصوت، و
درجة الدقة المفضّلة للبث المباشر.
ومع ذلك، ستحتاج إلى قيد واحد فقط للحصول على البيانات من الكاميرا، وهو video: true
.
في حال نجاح العملية، ستُرجع واجهة برمجة التطبيقات MediaStream
يحتوي على بيانات من
الكاميرا، ويمكنك بعد ذلك إرفاقه بعنصر <video>
وتشغيله
لعرض معاينة في الوقت الفعلي، أو إرفاقه بعنصر <canvas>
للحصول على
لقطة شاشة.
<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>
لا يُعدّ هذا الإجراء مفيدًا بحد ذاته. ما عليك سوى أخذ بيانات الفيديو وتشغيله مجددًا. إذا كنت تريد الحصول على صورة، عليك اتخاذ بعض الإجراءات الإضافية.
التقاط لقطة
إنّ أفضل خيار متاح لك للحصول على صورة هو رسم إطار من الفيديو على لوحة.
على عكس Web Audio API، لا تتوفّر واجهة برمجة تطبيقات مخصّصة لمعالجة البث للفيديوهات على الويب، لذا عليك اللجوء إلى بعض أساليب الاختراق لالتقاط لقطة من كاميرا المستخدم.
وتتم العملية على النحو التالي:
- إنشاء كائن لوحة رسم يتماشى مع الإطار من الكاميرا
- الوصول إلى بث الكاميرا
- إرفاقه بعنصر فيديو
- عندما تريد التقاط إطار دقيق، أضِف البيانات من عنصر الفيديو
إلى كائن لوحة باستخدام
drawImage()
.
<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>
بعد تخزين بيانات من الكاميرا في اللوحة، يمكنك تنفيذ العديد من الإجراءات باستخدامها. وفي هذا الإطار، يمكنك تنفيذ الإجراءات التالية:
- تحميلها مباشرةً إلى الخادم
- تخزينها على الجهاز
- تطبيق تأثيرات مميّزة على الصورة
نصائح
إيقاف البث من الكاميرا عندما لا يكون مطلوبًا
من المستحسن إيقاف استخدام الكاميرا عند الاستغناء عنها. لن يؤدي ذلك إلى توفير البطارية وقوة المعالجة فحسب، بل سيمنح المستخدمين الثقة في تطبيقك.
لإيقاف إمكانية الوصول إلى الكاميرا، يمكنك ببساطة استدعاء stop()
في كل مقطع صوتي من الفيديو
للبث الذي يعرضه getUserMedia()
.
<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>
طلب الإذن باستخدام الكاميرا بمسؤولية
إذا لم يسبق للمستخدم أن يمنح موقعك الإلكتروني إذنًا بالوصول إلى الكاميرا،
عندما تطلب getUserMedia()
، سيطلب المتصفّح من المستخدم
منح موقعك الإلكتروني إذنًا للوصول إلى الكاميرا.
يكره المستخدمون تلقّي إشعارات لطلب الوصول إلى الأجهزة القوية على أجهزتهم، وغالبًا ما سيحظرون الطلب أو سيتجاهلونه إذا لم يكن لديهم فهم للسياق الذي تم إنشاء الإشعار من أجله. من أفضل الممارسات أن تطلب الوصول إلى الكاميرا عند الحاجة إليها فقط. بعد أن يمنح المستخدم الإذن بالوصول، لن يُطلب منه ذلك مرة أخرى. أمّا إذا رفض المستخدم منح إذن الوصول، فلن يعود بإمكانك الحصول عليه مرة أخرى إلا إذا غيّر إعدادات أذونات الكاميرا يدويًا.
التوافق
مزيد من المعلومات حول تنفيذ متصفّحات الأجهزة الجوّالة وأجهزة الكمبيوتر المكتبي:
ننصحك أيضًا باستخدام العنصر الوسيط adapter.js لحماية التطبيقات من التغييرات في مواصفات WebRTC والاختلافات في البادئة.