У меня есть немного Javascript, который работает на планшете Android под управлением Opera Mobile 12. Планшет прикреплен к стене в офисе, поэтому он используется всеми, кто работает в этом офисе, в качестве системы хронометража (т. Е. Они используют его для того, чтобы приходить / приходить на работу). Этот javascript делает фотографию пользователя при возникновении определенного события, а затем преобразует эту фотографию в URL-адрес данных, чтобы ее можно было отправить на сервер. Однако все это выполняется в фоновом режиме - для элементов video
и canvas
установлено значение display:none;
.
Вот код, который обрабатывает взаимодействие с веб-камерой:
var Webcam = function() {
var video = document.getElementById('video');
var stream_webcam = function(stream) {
video.addEventListener('loadedmetadata', function(){
video.play();
}, false);
video.src = window.URL.createObjectURL(stream);
}
// start recording
if (navigator.getUserMedia)
navigator.getUserMedia({video: true}, stream_webcam);
else
_error("No navigator.getUserMedia support detected!");
var w = {};
w.snap = function(callback) {
var canvas = document.getElementById('canvas');
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
// delay is incurred in writing to canvas on opera mobile
setTimeout(function() {
callback(canvas.toDataURL());
}, 1);
};
return w;
}
w.snap(callback)
вызывается, когда пользователь нажимает определенную кнопку. Проблема в том, что существует значительная задержка между вызовом drawImage
и изображением, фактически нарисованным на холсте. Возникает следующая проблема:
- Пользователь 1 пользуется планшетом, нажимает кнопку, его фото "сфотографировано" и отправлено на сервер. Сервер получает пустое прозрачное изображение. (Это не то же самое, что холст html5 toDataURL возвращает пустое изображение - данные отправка на сервер существенно меньше для первого изображения, потому что оно пустое)
- Через несколько минут пользователь 2 использует планшет и нажимает кнопку, и его фотография «сфотографирована». На сервер отправляется фотография пользователя 1.
- Позже пользователь 3 делает то же самое, и сервер получает фотографию пользователя 2.
Итак, я предполагаю, что вызов toDataURL()
возвращает старые данные, потому что drawImage()
- нет. (Я где-то читал, что это асинхронно, но я не могу подтвердить и не могу найти способ прикрепить событие к моменту завершения рисования.)
Я пробовал:
- изменение тайм-аута в
snap()
на различные значения. Самый высокий, который я пробовал, был 2000, но возникла та же проблема. (Если кто-то предлагает более высокие числа, я рад попробовать их, но я не хочу подниматься намного выше, потому что, если я перейду слишком высоко, у пользователя 3 есть возможность сфотографироваться до того, как я обработал фотографии пользователя 2 фото, а значит, я могу его полностью потерять!) - когда холст рисовал что-то при первой загрузке страницы, но это не исправляло - когда пользователь 1 был сфотографирован, сервер получал любую фотографию, сделанную при загрузке страницы, вместо фотографии пользователя 1.
- "получение" элемента холста снова, используя
getElementById
, перед вызовомtoDataURL
.
Еще несколько примечаний:
- Это отлично работает на моем компьютере (macbook air) в Chrome и Opera.
- Не могу надежно реплицировать с помощью отладчика (привязка к планшету с помощью Opera Dragonfly).
- AFAIK Opera Mobile - единственный браузер Android, который поддерживает
getUserMedia
и, таким образом, может делать фотографии. - Если убрать
display:none;
изvideo
иcanvas
, работает корректно (на планшете). Проблема возникает только в том случае, если для видео и холста установленоdisplay:none;
.
Поскольку страница представляет собой одностраничное приложение, не требующее прокрутки или масштабирования, возможный обходной путь - переместить видео и холст ниже складки страницы, а затем отключить прокрутку с помощью javascript (т. Е. Прокручивать вверх всякий раз, когда пользователь прокручивает).
Но я бы предпочел правильное исправление, чем обходной путь, если это возможно!