Создаем автарки в браузере с помощью вебкамеры

в 15:57, , рубрики: html5 canvas, javascript, Песочница, метки: ,

Создаем автарки в браузере с помощью вебкамеры
Здравствуйте. Не так давно на одном из веб-проектов, мне понадобилась возможность получить фото с веб-камеры. Через некоторое время, на другом проекте, появилась такая же необходимость, и опять же замаячил на горизонте третий проект, с похожей функциональностью. Дабы не ходить каждый раз на html5 Rocks, и смотреть, «как там и чего», решил написать плагин.
Собственно, в данной статье я хочу представить результат своего труда: html5-webcam-avatar — jquery плагин для создания аватарок с веб-камеры.

Что умеет плагин?

  • Делать фото, а потом обрезать его квадратиком «как в контакте».
  • Просто резать любые другие картинки с сайта.

Что не умеет?

  • Работать в браузерах которые не поддерживают usermedia api, и работу с canvas.

То есть, плагин использует только javascript и нативные возможности браузера, никаких fallback-ов не предусмотрено.
Я бы рекомендовал его использовать, как дополнительную фичу на своем на сайте. Проверяем поддерживаются ли соответствующие возможности и подключаем плагин.

Как это работает?
Современные браузеры уже пытаются работать с веб-камерой и микрофоном. В javascript это реализовано через соответствующее api.
Пример кода с сайта html5 Rocks

window.URL = window.URL || window.webkitURL;
navigator.getUserMedia  = navigator.getUserMedia || navigator.webkitGetUserMedia ||
                          navigator.mozGetUserMedia || navigator.msGetUserMedia;
//...
if (navigator.getUserMedia) {
    navigator.getUserMedia({audio: true, video: true}, function(stream) {
        video.src = window.URL.createObjectURL(stream);
    }, onFailSoHard);
} else {
    video.src = 'somevideo.webm'; // fallback.
}

Все просто. Проверяем, определена ли функция navigator.getUserMedia и выполняем соответствующий код. Иначе, выполняем какую-либо fallback функцию. Обо всем этом можно почитать подробнее на вышеописанном сайте. Я же хочу рассказать о некоторых не очевидных и неописанных там вещах.

Во первых, поведение объекта stream, отличается в разных браузерах. В chrome, для того чтобы передать его в параметр src тега video, его необходимо преобразовать в objectURL с помощью window.URL.createObjectURL. В opera, насколько я понял, объект stream преобразуется автоматически, и например свойства window.URL — я просто не нашел. Поэтому пишем напрямую: video.src = stream. Ну и firefox, имеет свойство window.URL и метод createObjectURL. Однако такой код window.URL.createObjectURL(stream) — выдает ошибку, а вот так video.src = stream — работает.

NB
Вообще в firefox 18 мне так и не удалось заставить работать мой плагин, хотя свойство getUserMedia — там уже было, но недавно вышел firefox 19 и мой плагин заработал.

Поэтому у меня код получился таким:

navigator.getUserMedia && navigator.getUserMedia({video: true}, function(stream) {
    try {
        video.src = window.URL.createObjectURL(stream); //for webkit
    } catch (e) {
        video.src = stream; //for opera and firefox
    }
}, function() { alert('веб-камера не найдена на этом устройстве'); });

Второй момент, про который хотелось бы рассказать, это работа с самим плеером. Дело в том, что для удобства, я решил сделать возможность остановить видео, а потом уже сделать snapshot. Так вот, остановка видео в chrome и firefox — происходит нормально. В opera же, вместе с остановкой видео происходит и остановка потока с веб-камеры. То есть, когда мы делаем snapshot в opera во время паузы — получаем вариант произведения известного художника (черный квадрат). Решение данной проблемы в принципе очевидно. Перед тем как поставить видео на паузу — мы сохраняем dataUrl в какое-то временное хранилище, ну а после, при создании снапшота — достаем его из хранилища, если видео на паузе, либо создаем dataUrl заново.
У меня это выглядит как то так:

// play/pause - при клике на само видео
video.addEventListener('click', function() {
    if (video.paused) {
        video.play();
    } else {
        $(video).data('data-url', getSnapshotDataUrl()); // здесь сохраняем данные в data-url
        video.pause();
    }
});
            
...     
var  getDataUrl = function() {
    var data_url = video.paused ? $video.data('data-url') : getSnapshotDataUrl();
    return data_url;
}

Здесь getSnapshotDataUrl — функция которая перекидывает текущий кадр видео в элемент canvas, а затем у canvas вызывается метод toDataURL.

Теперь пожалуй расскажу о самом плагине.
Вообще, тут получается не один, а два плагина. Первый для получения фото с веб-камеры html5WebCam. Второй — для обрезки фото html5Crop. Да их можно использовать отдельно.
Работает все следующим образом — мы навешиваем плагин на какой либо элемент. например button. Теперь элемент слушает событие click и запускает процедуру создания аватара. Когда аватар будет готов, мы получим его в callback функции onCrop, или onsnapshot.

$(document).ready(function() {
    $("#create_snapshot").html5WebCam({
        oncrop: function(cropped_url) { 
            // cropped_url - base64 image 
            var $img = $("<img/>");
            $img.attr('src', cropped_url);
            $('body').append($img);
        },
    });
});

В плагине используются простые модальные окна, которые легко настраиваются через прилагающийся css файл. Если такое решение не устраивает, можно использовать свои модальные окна (на демо сайте есть пример с jquery-ui Dialog).

Есть и другие настраиваемые параметры, вот их список

//html5WebCam
NOT_SUPPORT_FEATURE: 'Этот браузер не поддерживает захват с камеры',
CAMERA_NOT_FOUND: 'Камера не найдена на этом устройстве',
CLICK_TO_PAUSE: 'Нажмите для воспроизведения/остановки',
TAKE_SNAPSHOT: 'Сделать снимок',
CANCEL: 'Отмена',
max_video_size: 600,
modal_class: 'html5-webcam-avatar-modal',
use_native_modal: true,
use_native_button: true,
onDomCreated: function($html) { },
onsnapshot: function(snapshot) {},
use_crop: true,
oncrop: function(cropped_url) {},
oncancel: function() {},
alertFn: function(msg) { alert(msg); }

//html5Crop
CROP_NAME: 'резать',
CANCEL: 'отмена',
MIN_IMG_SIDE_ERROR: 'Слишком маленькое изображение по ширине или выстоте',
CANVAS_NOT_SUPPORTED: 'canvas not supported in this browser',
square_mode: true,
max_crop_side: 400,
min_crop_side: 50,
max_img_side: 600,
min_img_side: 100,
init_crop_side: 100,
dot_side: 10,
use_native_modal: true,
use_native_button: true,
onDomCreated: function($ui) {},
oncancel: function() {},
oncrop: function(cropped_url) {},
alertFn: function(msg) { alert(msg); },

modal_class: 'html5-webcam-avatar-modal'

Думаю из названий понятно, для чего они нужны. Хочу только отметить, все параметры для html5Crop — нужно задавать в html5WebCam — при их совместном использовании, то есть если use_crop: true (по умолчанию именно так).

Демо и плагин — здесь
Жду вопросы, пожелания, критику и советы в комментариях.

Автор: kromxr

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js