#javascript #android #google-chrome
#javascript #Android #google-chrome
Вопрос:
Я создал систему сканирования штрих-кодов, используя встроенную в Chrome BarcodeDetector
. Я основывался на сканере QR-кода Пола Кинлана, который отлично работает на моем телефоне, но когда я запускаю свой собственный код на своем телефоне, это часто приводит к зависанию Chrome или всего системного пользовательского интерфейса. Иногда это становится настолько плохо, что мне нужно перезагрузить телефон, удерживая нажатой кнопку питания.
Я пробовал отладку в консоли разработчика Chrome, но когда телефон зависает, консоль разработчика тоже.
Когда я комментирую фактическое обнаружение QR-кода, я могу оставить страницу открытой в Chrome на 10 минут, и она просто продолжает работать. При запущенном обнаружении QR-кода телефон зависает в любом месте от немедленного до 3 минут спустя.
Я помещаю файлы поддержки (js и HTML) в суть — я не думаю, что это проблема, потому что все работает, когда сканирование штрих-кода закомментировано.
Моя первая попытка:
// Inspired by/based on on https://github.com/PaulKinlan/qrcode/blob/production/app/scripts/main.mjs
import WebCamManager from './scan/WebCamManager.js';
(function () {
'use strict';
var QRCodeCamera = function (element) {
var root = document.getElementById(element);
var cameraRoot = root.querySelector('.CameraRealtime');
var cameraManager = new WebCamManager(cameraRoot);
var cameraVideo = root.querySelector('.Camera-video');
// Offscreen canvas is supposed to help with processing speed
var cameraCanvas = new OffscreenCanvas(1,1);
var context = cameraCanvas.getContext('2d');
const detector = new BarcodeDetector({
formats: ['qr_code'],
});
cameraManager.onframeready = async function (frameData) {
cameraCanvas.width = cameraVideo.videoWidth;
cameraCanvas.height = cameraVideo.videoHeight;
context.drawImage(frameData, 0, 0, cameraVideo.videoWidth, cameraVideo.videoHeight);
if (self.onframe) {
// Comment out the line below to stop processing QR codes
await self.onframe(cameraCanvas);
}
};
var processingFrame = false;
self.onframe = async function (cameraCanvas) {
// There is a frame in the camera, what should we do with it?
if (processingFrame == false) {
processingFrame = true;
let result = await detector.detect(cameraCanvas);
processingFrame = false;
if (result === undefined || result === null || result.length === 0) {
return
};
if ('vibrate' in navigator) {
navigator.vibrate([200]);
}
cameraManager.stop();
var currentURL = new URL(window.location.href);
var newURL;
if (result[0].rawValue
amp;amp; (newURL = new URL(result[0].rawValue))
amp;amp; newURL.hostname == currentURL.hostname
amp;amp; newURL.pathname.startsWith('/pickup/qr/')
) {
window.location.href = newURL;
} else {
alert('Unsupported QR Code: ' result[0].rawValue);
}
cameraManager.start();
}
};
};
window.addEventListener('load', function () {
var camera = new QRCodeCamera('camera');
});
})();
Я также попытался разделить обнаружение QR на worker (рабочий код в Gist), но у меня такая же проблема:
const worker = new Worker('/js/scan-worker.js');
worker.addEventListener('message', async (e) => {
if (Array.isArray(e.data)) {
var result = e.data;
if (result === undefined || result === null || result.length === 0) {
processingFrame = false;
return
};
if ('vibrate' in navigator) {
navigator.vibrate([200]);
}
var currentURL = new URL(window.location.href);
var newURL;
if (result[0].rawValue
amp;amp; (newURL = new URL(result[0].rawValue))
amp;amp; newURL.hostname == currentURL.hostname
amp;amp; newURL.pathname.startsWith('/pickup/qr/')
) {
worker.terminate();
window.location.href = newURL;
} else {
alert('Unsupported QR Code: ' result[0].rawValue);
}
} else {
var newError = document.createElement('div');
newError.classList.add('alert', 'alert-danger');
newError.innerHTML = e.data;
errorContainer.prepend(newError);
worker.terminate();
}
processingFrame = false;
});
cameraManager.onframeready = async function (frameData) {
if (processingFrame == false) {
cameraCanvas.width = cameraVideo.videoWidth;
cameraCanvas.height = cameraVideo.videoHeight;
context.drawImage(frameData, 0, 0, cameraVideo.videoWidth, cameraVideo.videoHeight);
if (self.onframe) {
await self.onframe(cameraCanvas, context);
}
}
};
self.onframe = async function (cameraCanvas, context) {
// There is a frame in the camera, what should we do with it?
if (processingFrame == false) {
processingFrame = true;
worker.postMessage(context.getImageData(0, 0, cameraCanvas.width, cameraCanvas.height));
}
};
Не уверен, что это что-то изменит — весь код JS выполняется через Laravel Mix.
Комментарии:
1. Он может застрять
onframeready
?2. @MartinZeitler Нет, он застревает
detector.detect
. Если я закомментирую эту одну строку, цикл будет выполняться вечно.3. Взглянув на документацию
BarcodeDetector
, вам не нужно передавать контекст canvas вdetector.detect
, и я также думаю, что этот метод не следует вызывать в функции обновления фрейма. Попробуйте изолировать и запуститьdetector.detect
onframe
функции4. @DDomen В верхней части этой страницы говорится, что она не завершена. Если вы посмотрите на саму спецификацию , вы увидите, что
ImageBitmapSource
там необходимо указать, например, холст. Кроме того, как бы вы получили поток изображений с холста (т. Е. С Камеры), не используя этотonframeready
метод?5. Да, извините, я неправильно прочитал документы для части источника изображения. Я хочу сказать
onframe
, что вы не должны ожидать интенсивной или длительной работы с процессором внутри обратного вызова API, особенно если он выполняется несколько раз в секунду, напримерonframeready