Replies: 5 comments 8 replies
|
i figured this discussion may be helpful, you can call native api's. |
|
I had a fiddle with some code proof of concept code. https://stackoverflow.com/a/78089783, which uses the barcode scanner API. This has poor browser support, but there is a polyfill for it. Amazingly it seems to work, at least on Desktop with Camera. Proof of concept code only though. I can't think right now, but wondering:
#[component]
fn Barcode() -> Element {
use_effect(move || {
document::eval(
r#"
console.log("00000");
//WebAssembly polyfill for some browsers
try { window['BarcodeDetector'].getSupportedFormats() }
catch { window['BarcodeDetector'] = barcodeDetectorPolyfill.BarcodeDetectorPolyfill };
console.log("1111");
// Define video as the video element. You can pass the entire element to the barcode detector!
const video = document.querySelector('video');
console.log("2222");
// Get a stream for the rear camera, else the front (or side?) camera, and show it in the video element.
video.srcObject = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
console.log("33333");
// Create a BarcodeDetector for simple retail operations.
const barcodeDetector = new BarcodeDetector({ formats: ["ean_13", "ean_8", "upc_a", "upc_e"] });
console.log("4444");
while(true) {
try {
// Try to detect barcodes in the current video frame.
let barcodes = await barcodeDetector.detect(video);
// Continue loop if no barcode was found.
if (barcodes.length == 0)
{
// Scan interval 50 ms like in other barcode scanner demos.
// The higher the interval the longer the battery lasts.
await new Promise(r => setTimeout(r, 50));
continue;
}
console.log("xxxx", barcodes);
// We expect a single barcode.
// It's possible to compare X/Y coordinates to get the center-most one.
// One can also do "preferred symbology" logic here.
// document.getElementById("barcode").innerText = barcodes[0].rawValue;
// Notify user that a barcode has been found.
// navigator.vibrate(200);
// Give the user time to find another product to scan
await new Promise(r => setTimeout(r, 1000));
}
catch(err) {
console.log("failed", err);
//Wait till video is ready
//barcodeDetector.detect(video) might fail the first time
await new Promise(r => setTimeout(r, 200));
}
}
"#,
);
});
rsx! {
script { src: "https://cdn.jsdelivr.net/npm/@undecaf/zbar-wasm@0.9.15/dist/index.js" }
script { src: "https://cdn.jsdelivr.net/npm/@undecaf/barcode-detector-polyfill@0.9.21/dist/index.js" }
h1 { "Penguins Rule"}
video { autoplay: true, playsinline: true, }
}
} |
|
QR code scanning is a very popular function for mobile apps. I'm also looking for a solution for Dioxus. In React, @yudiel/react-qr-scanner works. But needs hard work to port. |
|
If you're looking for a paid solution this company put out a Dioxus example. |
use dioxus::logger::tracing;
use dioxus::prelude::*;
use dioxus_primitives::accordion::{Accordion, AccordionContent, AccordionItem, AccordionTrigger};
#[derive(PartialEq)]
enum ScanningStatus {
Scanning = 0,
ReadyToScan = 1,
UnableToScan = 2,
}
impl ScanningStatus {
pub fn to_string(&self) -> String {
match self {
ScanningStatus::Scanning => String::from("Scanning"),
ScanningStatus::ReadyToScan => String::from("ReadyToScan"),
ScanningStatus::UnableToScan => String::from("UnableToScan"),
}
}
}
#[component]
pub(crate) fn QRReader() -> Element {
let mut qr_value = use_signal(|| "no qr code scanned yet".to_string());
let mut scanning_status = use_signal(|| ScanningStatus::UnableToScan.to_string());
let mut protocol = use_signal(String::new);
// Initialize NFC status on mount
use_effect(move || {
spawn(async move {
match document::eval(
r#"
if ("BarcodeDetector" in window) {
return true
} else {
return false
}
"#
).await {
Ok(result) => {
if let Some(val) = result.as_bool() {
if val == true {
scanning_status.set(ScanningStatus::ReadyToScan.to_string());
}
}
}
Err(e) => eprintln!("Error checking BarcodeDetector: {}", e),
}
});
});
// check for http or https
use_effect(move || {
spawn(async move {
match document::eval(
r#"return window.location.protocol"#
).await {
Ok(result) => {
protocol.set(result.to_string());
}
Err(e) => eprintln!("Error checking Protocol: {}", e),
}
});
});
rsx!{
button {
id: "scan-btn",
onclick: move |_| async move {
scanning_status.set(ScanningStatus::Scanning.to_string());
match document::eval(
r#"
const camStream = document.getElementById('cam-stream');
const scanBtn = document.getElementById('scan-btn');
const stopStream = (streamToStop) => {
streamToStop.getTracks().forEach((track) => track.stop());
camStream.style.display = "none";
camStream.srcObject = null;
//scanBtn.textContent = "Click me to scan QR Code";
};
try {
camStream.style.display = "block";
// Gets camera stream
stream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: { ideal: "environment" }
},
audio: false
});
camStream.srcObject = stream;
await camStream.play();
//scanBtn.textContent = "Scanning...";
// Creates new barcode detection instance
const barcodeDetector = new BarcodeDetector({ formats: ["code_128", "code_39", "code_93", "qr_code"]});
let detected = false;
// Runs detect() every second till a barcode is captured
const intervalID = setInterval(async () => {
// Detects the contents of the camera stream to capture any visible barcode
const barcodes = await barcodeDetector.detect(camStream);
const lastBarcode = barcodes[barcodes.length -1];
// If barcode detected, stop stream and display result
// If this runs, setTimeout callback doesn't run
if (barcodes.length > 0) {
stopStream(stream);
// display only the last detected barcode
// displayResult(lastBarcode.rawValue, lastBarcode.format);
detected = true;
clearInterval(intervalID);
dioxus.send(lastBarcode.rawValue);
}
}, 1000);
// Stop stream if nothing has been detected after 15 seconds
setTimeout(() => {
if (!detected) {
stopStream(stream);
console.error("Barcode Not Detected or Format Not Supported");
clearInterval(intervalID);
}}, 15000);
} catch (e) {
console.error(e.message);
}
"#
)
.recv::<String>()
.await {
Ok(values) => {
qr_value.set(values);
scanning_status.set(ScanningStatus::ReadyToScan.to_string());
}
Err(error) => {
tracing::error!("Error reading from barcodeDetector: {}", error);
}
}
},
disabled: {
if scanning_status() == ScanningStatus::ReadyToScan.to_string() || scanning_status() == ScanningStatus::Scanning.to_string() {
false
} else {
true
}
},
{
if scanning_status() == ScanningStatus::ReadyToScan.to_string() {
"Click me to read QR Code"
} else if scanning_status() == ScanningStatus::Scanning.to_string() {
"Reading QR Code"
} else if scanning_status() == ScanningStatus::UnableToScan.to_string() {
"Unable to read QR Code"
} else {
"Unknown error"
}
},
}
video {
id: "cam-stream",
width: "100%",
height: "75%",
display: "none",
}
Accordion { allow_multiple_open: false, horizontal: false,
AccordionItem { index: 0,
AccordionTrigger { "Debug Values" }
AccordionContent {
div { padding_bottom: "1rem",
p { "Current protocol: {protocol} <-- For QR Code scanning https is required" }
if scanning_status() == ScanningStatus::ReadyToScan.to_string() || scanning_status() == ScanningStatus::Scanning.to_string() {
p { "BarcodeDetector available: Your phone or your browser probably does support qr code scanning" }
} else {
p { "BarcodeDetector not available: Your phone or your browser probably doesn't support qr code scanning" }
}
if scanning_status() == ScanningStatus::ReadyToScan.to_string() {
p { "Barcodedetector read: {qr_value}" }
}
}
}
}
}
}
}I wrote this small component. Is limited to chromium browsers tho (tested on chrome android). |
Uh oh!
There was an error while loading. Please reload this page.
Hello,
Crazy idea maybe, but I would like a wasm front end written in dioxus to be able to prompt the user to scan a barcode.
Not sure where to start... Or if this is feasible.
I guess there are two distinct steps:
For step 2, It looks like there is on official detection API, https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API
But by the looks of it browser support might be limited. Not sure if Mobile firefox on Android supports it, might be limited to Chrome on Android.
Then somebody has written a component for yew: https://gitlab.com/voltfang-public/yew-scanner
Code here: https://gitlab.com/voltfang-public/yew-scanner/-/blob/main/src/lib.rs?ref_type=heads
It looks like this grabs an image from a
HtmlVideoElementand passes to the bardecoder crate for decoding (not sure I entirely understand how theHtmlVideoElementgets its image.https://github.com/piderman314/bardecoder
But it does certain things which I am not sure of in Dioxus, e.g. grabbing references to rendered html elements.
Then there is some JavaScript code:
https://stackoverflow.com/questions/67078359/barcode-scanning-via-mobile-browser
And I see this library looks like it might have Camera support in the future, but isn't quite there yet:
https://github.com/DioxusLabs/sdk
Any suggestions on which approach I should take?
All reactions