В начале давайте разработаем приложение камеры, включая использование html css.
Представление будет настолько простым, что оно будет содержать один снимок внизу по центру, а посередине содержимое камеры будет отображаться с использованием элемента холста.
<canvas class="camera"> </canvas> <div class="capture"> </div>
Теперь в файле/теге css мы начнем украшать наши элементы:
.camera{ position: fixed; left: 0; top: 0; width: 100%; height: 100%; } .capture{ position: fixed; bottom: 50px; left: calc(50% - 25px); width: 50px; height: 50px; border-radius: 50%; z-index: 2; background-color: rgba(255,255,255,.8); }
Совершенно минималистично!
Теперь приступим к настоящей части кодирования!
Подготовка рендерера
Рендерер — это в значительной степени бумага, на которой можно свободно рисовать. В нашем случае мы будем рисовать содержимое камеры на элементе холста. Элементом холста является бумага, и для рисования на бумаге мы используем разные инструменты, также для рисования на холсте нам нужен контекст (среда и инструменты). Итак, давайте инициируем этот контекст:
let canvas = document.querySelector('camera'); let ctx = canvas.getContext('2d');
«2d» означает, что мы будем рисовать простые 2D-рисунки. Мы также можем рисовать 3D-изображения с помощью webgl.
Теперь мы можем рисовать на холсте, используя эти функции:
ctx.moveTo(10,10); // move an imaginary pen to certain // coordinations ctx.lineTo(50,50); // drawing a line from x10,y10 to x50,y50 // OR ctx.drawImage(image,50,50); // to draw the given 'image' on the x50,y50 ....
Доступ к устройству камеры
Чтобы получить доступ к камере, вы сначала запрашиваете разрешение пользователя. Это будет состоять из двух шагов, первый — создать объект видео, который будет представлять контейнер для нашего отображаемого видео:
var video = document.createElement('video');
Затем мы должны запросить разрешение пользователя, и это будет использовать объект 'navigator' 'mediaDevices' в нашем коде, используя метод 'getUserMedia':
navigator.mediaDevices.getUserMedia({ video: true, audio: false}).then( function(rawData){ video.srcObject = rawData; video.volume = 0; video.play(); video.onloadeddata = function(){ render(); } }) .catch( function(err){ alert(err) })
Мы указали в качестве параметров внутри нашего 'getUserMedia' вызов атрибута видео как истинного и атрибута аудио как ложного, что означает, что мы собираемся вычитать видео только с устройства камеры. Затем мы присвоили фрагменты rawData, возвращенные обратным вызовом, нашему видео srcObject, установили громкость на 0, затем дождались загрузки данных с помощью eventListener(onloadeddata), чтобы мы могли выполнить задачу рендеринга, которую мы определили как функцию с именем «рендеринг». Итак, теперь мы должны подготовить нашу функцию рендеринга:
function render(){ ctx.drawImage(video,0,0,canvas.width,canvas.height); }
Что ж, никогда не было так просто, вызов 'drawImage' просто рисует медиаданные, переданные внутри (изображение, видео) по координатам x=0, y=0, width=canvas.width и height=canvas.height, очень просто.
На данный момент код, который мы написали выше, отрисовывает только первый кадр, снятый с помощью камеры, но мы хотим, чтобы камера всегда захватывала входящее видео, поскольку нам нужно снова и снова вызывать функцию рендеринга. Только добавив одну маленькую строку:
function render(){ ctx.drawImage(video,0,0,canvas.width,canvas.height); requestAnimationFrame(render); }
Да… это работает очень хорошо!
Подождите секунду, но мы пока не можем делать снимки! Именно этим мы и займемся в следующей части.
фото
Процесс фотографирования настолько прост, что как только мы нажмем кнопку захвата, видеофрагмент будет нарисован на закадровом холсте, а затем автоматически сохранен на пользовательском устройстве:
let capture = document.querySelector('.capture'); capture.addEventListener('click',function(){ takePic(); }) function takePic(){ const offscreen = new OffscreenCanvas(video.videoWidth, video.videoHeight); let ctx = offscreen.getContext('2d'); ctx.drawImage(video,0,0,video.videoWidth,video.videoHeight); offscreen.convertToBlob().then(blob=> { // generating download let a = document.createElement('a'); a.download = 'picture.png'; a.href = URL.createObjectURL(blob); a.click(); }); }
Так что теперь это будет работать как по волшебству ✨.
Сначала мы получаем кнопку из DOM, затем прослушиваем событие click, поэтому после нажатия на нее создается закадровый холст (так же, как холст, но не отображаемый в DOM), мы рисуем на нем видео, затем конвертируем его в blob (двоичные данные), затем с помощью элемента привязки (тега) генерируем загрузку blob(URL.createObjectURL(blob)).
Доработка
Давайте соберем весь наш код в один блок, используя подход ООП:
function Camera(){ this.video = null; this.canvas = null; this.ctx = null; this.capture = null; this.af = null; this.id = 0; } Camera.prototype = { init : function(){ var _this = this; this.video = document.createElement('video'); this.canvas = document.querySelector('.camera'); this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; this.ctx = this.canvas.getContext('2d'); this.capture = document.querySelector('.capture'); this.capture.addEventListener('click',this.takePic.bind(this)); this.requestAccess(); }, requestAccess : function(){ var _this = this; navigator.mediaDevices.getUserMedia({ video: true, audio: false}).then( function(rawData){ _this.video.srcObject = rawData; _this.video.volume = 0; _this.video.play(); _this.video.onloadeddata = function(){ _this.af = requestAnimationFrame(_this.render.bind(_this)); } }) .catch( function(err){ alert(err) }) }, render : function(){ this.ctx.drawImage(this.video,0,0,this.canvas.width,this.canvas.height); this.af = requestAnimationFrame(this.render.bind(this)); }, takePic : function(){ var _this = this; var canvas = new OffscreenCanvas( this.video.videoWidth, this.video.videoHeight); let ctx = canvas.getContext('2d'); ctx.drawImage(this.video,0,0,this.video.videoWidth,this.video.videoHeight); canvas.convertToBlob().then(blob=>{ let a = document.createElement('a'); _this.id ++; let name = 'new_image' + _this.id + '.png'; let dataUrl = URL.createObjectURL(blob); a.download = name; a.href = dataUrl; a.click(); }); } } var cam = new Camera(); cam.init();
А вот и наша камера… 🎉 тада