capacitor-community / barcode-scanner

A fast and efficient (QR) barcode scanner for Capacitor
MIT License
437 stars 172 forks source link

Camera Not Showing Up #169

Closed mendozaarjay closed 1 year ago

mendozaarjay commented 1 year ago

I already added the body.scanner-active { --background: transparent; --ion-background-color: transparent; }

and the permission is granted. Can someone please help me

DannePeyya commented 1 year ago

I have the same issue. But I know that the camera is actually there. But I can't see it. I can scan qr codes and see logs in xcode. I'm guessing that the css is not enough for the rendered html. In the guide, it says you have to add --background: transparent into your css and document.querySelector('body').classList.add('scanner-active'); where you trigger scanner.

Doing this, gave me a camera as a background. But it kept all outher components on top of it. I'm guessing there is a lack of z-index. Since the code according to the guide is

<div style="position: absolute; left: 0px; top: -2px; height: 1px; overflow: hidden; visibility: hidden; width: 1px;">
    <span
      style="position: absolute; font-size: 300px; width: auto; height: auto; margin: 0px; padding: 0px; font-family: Roboto, Arial, sans-serif;"
      >BESbswy</span
    >
  </div>

And what I can see, there is no z-index. Therefore everything is in the back for me.

Question to the dev team. Would it be possible to add a class/id/name or something that can be target with css? So I can play around and see if my own positioning would work?

atans2468 commented 1 year ago

I'm having the exact same experience. Did anyone figure out a solution for this? I noticed that if I swipe to go back/close my app (while in Android ) then I do see the camera open behind the app body (confirming that the camera is indeed opening).

thegnuu commented 1 year ago

This is by definition of the plugin. The plugin will just render a camera-view behind the DOM. Since capacitor can be used with whatever framework you want, if you want to use a framework at all, it is not possible to provide a general solution for this "issue".

You need to check your DOM elements, find the ones that are not transparent, and apply transparency to all the elements in front of the camera-view while scanning. Unfortunately there is no other solution for this without "killing" the possibility to create custom UIs.

@DannePeyya it would be possible to add css classes to the body once the scan starts, but this is currently not done since it is an easy task to do yourself. The view itself won't have a class at all, since it is rendered behind the complete DOM.

JoranLive commented 1 year ago

I make it working by adding this:

this.barcodeScanner.hideBackground(); document.body.style.opacity = '0'; document.body.style.background = 'transparent';

It works fine but I need to add a cancel button. That's something that everybody want.. No?

However, I'm not able to display any overlay... I just opened a ticket few minutes ago but I just read the comment from @thegnuu : "Unfortunately there is no other solution for this without "killing" the possibility to create custom UIs."

It is an absolutely pathetic spectacle!

JoranLive commented 1 year ago

Another question @thegnuu .

By definition, the plugin render the camera view behind and we need to make the application transparent.

Why not doing the opposite ? I mean bring the camera view ahead with a z-index that could be customized ?

thegnuu commented 1 year ago

@JoranLive

It is an absolutely pathetic spectacle!

First of all, nobody is forcing you to use this plugin, if you are not happy with it you are free to find another solution that works for you. All here are doing their best to provide a free plugin to the community, and comments like this will help absolutely nobody.

Why not doing the opposite ? I mean bring the camera view ahead with a z-index that could be customized ?

I guess you are not familiar with how capacitor works, otherwise you would know that there are some technical limitations on its usage. The camera view (native on ios and android) is completely encapsulated from the web content view which is used to display your App, this view is not freely movable within the DOM. It might be possible that there are some sort of fancy workarounds to open a camera stream natively in a different view, intercept it and forward this stream via some capacitor APIs to your DOM, but the technical overhead and complexity would be way to big, there are some security and performance concerns as well.

This is an issue that should not be that kind of a deal for a developer that works with capacitor and has basic knowledge how a DOM structure works, once you know how the plugin works.

I might be wrong and there is a simple way that I just cannot think about now, if so and you know about one, feel free to open a PR and share your knowledge with the rest of the community.

atans2468 commented 1 year ago

@thegnuu I'm sure I speak for the community in letting you know that the rest of us appreciate your work on this and the process of making it work was quite easy once the "issue" was fully understood!

JoranLive commented 1 year ago

Sorry about what I wrote, it was probably a bad day.

I appreciate this plugin and for sure you allowed me to save my time.

You are right, I'm absolutly not familiar with Capacitor for now.

However, I notice that I'm not the only one to face the issue with Ionic even if we followed the readme.

I will think about it and try to come back with suggestions.

Thank you

thegnuu commented 1 year ago

No worries, I understand that things can be frustrating sometimes :)

Unfortunately, as I said, I currently have no great idea how to simplify the setup since it is very individual...

Maybe adding some sort of demo or getting started with ionic and maybe pure js/html might be a good idea to begin with, what do you guys think? So that devs have some sort of starting point or working example to get started with... That would be easily done since I need some sort of dev environment anyways, I currently have an external repo for that.

Updating and simplifying the docs would be great as well I believe.

ayami123 commented 1 year ago

Mine it's just showing white screen weird

.scan-box { border: 2px solid #fff; box-shadow: 0 0 0 100vmax rgb(0, 0, 0, 0.5); content: ""; background: transparent; display: block; left: 50%; height: 300px; position: absolute; top: 50%; transform: translate(-50%, -50%); width: 300px; } .scan-button { background: transparent; margin: 0px; position: absolute; bottom: 100px; width: 100vw; height: 50px; z-index: 11; }

I don't understand why

thegnuu commented 1 year ago

Have you checked the complete DOM structure? There will be some element which still has a background set.

The only other reason might be that you don't have permission to access the camera, but this should be visible in the logs somewhere...

nbarazi commented 1 year ago

I had the same problem with my Ionic 6 app and solved it by adding 'opacity = 0' to the 'ion-content' of the page that calls 'startScan()' function, in addition to the steps and styling that mentioned in the docs. This worked for me and the camera view is now showing up. And this way I was able to add 'ion-header' that is shown above the camera view with a title and a close button that call 'stopScan()' and get all the styling back to normal. And the visibility of the 'ion-header' can be controlled by a parameter, for example 'isScannerOpen', that is toggled true or false according to the status of the BarcodeScanner

ahmad-alk commented 1 year ago

@JoranLive

Actually, your solution worked for me but without document.body.style.opacity = '0'; so only document.body.style.background = 'transparent'; is required to add in addition to await BarcodeScanner.hideBackground();

tveseli commented 1 year ago

Maybe this can help some people. I messed with this issue on Android for a couple days. IOS worked fine based on instructions.

  1. in my scan function I added the document line below before the call to hide background ... document.body.style.background = 'transparent'; await BarcodeScanner.hideBackground(); ..
  2. CSS is what stated in install instructions .scan-box { border: 2px solid rgb(177, 2, 2); box-shadow: 0 0 0 100vmax rgb(0, 0, 0, 0.5); content: ""; display: block; left: 50%; height: 300px; position: absolute; top: 50%; transform: translate(-50%, -50%); width: 300px; } .scan-button { margin: 0px; position: absolute; bottom: 100px; width: 100vw; height: 50px; z-index: 11; }
  3. in html code dont forget the ion-content to add the style stated in install ... <ion-content [style.--background]="scanActive?'#00000000':'#ffffff'"> <div [hidden]="scanActive"> ....
l364cyv1 commented 1 year ago

I had the same problem on iOS with Ionic v6 and just wanted to share the solution. Tag body along with ion-content have black/white background (dark/light mode) so the solution that worked for me was to set the class to body (such as .scanner-active) and based on that to set variables to transparent:

.scanner-active {
  --ion-background-color: transparent !important;
}
.scanner-active ion-content {
  --background: transparent !important;
}
DevDianDankie commented 1 year ago

Here is a service for anyone needing a kickstart. With a mimic function on web so you can style your button and crosshair etc via ionic serve.

`import { Injectable } from '@angular/core'; import { BarcodeScanner, ScanResult } from '@capacitor-community/barcode-scanner'; import { Platform } from '@ionic/angular'; import { ToastServiceService } from './toast-service.service';

@Injectable({ providedIn: 'root' }) export class BarcodeScannerService { crosshairId = "QrCrosshair"; crosshairContainerId = "QrCrossHairContainer"; private crosshairContainer:HTMLElement; constructor( private toasts:ToastServiceService, private platform:Platform ) { this.createScannerElements(); }

async scan(){ try { let result:ScanResult // Check camera permission // This is just a simple example, check out the better checks in plugin repo

  this.hideBackground();
  if(this.platform.is('desktop')){
   result = await this.mimicScan()
  }else{
    const checkResult = await BarcodeScanner.checkPermission({ force: true });
    if(!checkResult.granted){
      return;
    }
    result = await BarcodeScanner.startScan();
  }
  this.showBackground();
  return {result}
}catch(err){
  console.log(err);
  await this.toasts.showToast('Scanner Failed','');
}

}

private async mimicScan(){ let res = await new Promise<{ hasContent:boolean, content:string }>((resolve) => { setTimeout(()=>{ let result={ hasContent:false, content:'Test' } resolve(result) },10000) }) return res }

private async hideBackground(){ if(!this.platform.is('desktop')){ await BarcodeScanner.hideBackground(); } document.body.style.opacity = '0'; document.body.style.background = 'transparent'; this.addCrosshair() }

private async showBackground(){ document.body.style.opacity = ''; document.body.style.background = ''; if(!this.platform.is('desktop')){ await BarcodeScanner.showBackground(); } this.rmCrosshair(); }

private createScannerElements(){ this.crosshairContainer = document.createElement("div");

  // set the crosshairContainer style using CSS
  this.crosshairContainer.style.position = "fixed";
  this.crosshairContainer.id = this.crosshairContainerId;
  this.crosshairContainer.style.top = "50%";
  this.crosshairContainer.style.left = "50%";
  this.crosshairContainer.style.transform = "translate(-50%, -50%)";
  this.crosshairContainer.style.width = "100%";
  this.crosshairContainer.style.height = "85%";
  this.crosshairContainer.style.zIndex = "9999";

  // create a new div element to contain the crosshair
  let crosshair = document.createElement("div");

  // set the crosshair style using CSS
  crosshair.id = this.crosshairId;
  crosshair.style.opacity = "";
  crosshair.style.background = "";
  crosshair.style.position = "fixed";
  crosshair.style.top = "50%";
  crosshair.style.left = "50%";
  crosshair.style.transform = "translate(-50%, -50%)";
  crosshair.style.width = "9em";
  crosshair.style.height = "9em";
  crosshair.style.border = "2px dashed #74a44c";
  crosshair.style.borderRadius= "4px";
  crosshair.style.zIndex = "9999";

  // append the crosshair element to the container element
  this.crosshairContainer.appendChild(crosshair);

  // create a new button element for cancel
  var cancelButton = document.createElement("button");

  // set the cancel button style using CSS
  cancelButton.textContent = "Cancel";
  cancelButton.style.position = "absolute";
  cancelButton.style.bottom = "0";
  cancelButton.style.left = "50%";
  cancelButton.style.transform = "translateX(-50%)";
  cancelButton.style.padding = "10px";
  cancelButton.style.borderRadius = "5px";
  cancelButton.style.backgroundColor = "#aa240e";
  cancelButton.style.color = "#f4f5f8";
  cancelButton.style.width = "80%";
  cancelButton.style.height = "50px";

  // add an event listener to the cancel button to remove the container element when clicked
  cancelButton.addEventListener("click", ()=>{
    this.showBackground();
  });

  // append the cancel button to the container element
  this.crosshairContainer.appendChild(cancelButton);

}

private addCrosshair(){ // append the crosshair element to the root element document.documentElement.appendChild(this.crosshairContainer); }

private rmCrosshair(){ document.getElementById(this.crosshairContainerId).remove(); } } `

ahmad-alk commented 1 year ago

Ionic 6: I have to do one more thing which is not mentioned in readme file. in body.scanner-active the readme here mentioned body.scanner-active { --background: transparent; --ion-background-color: transparent; }

But I had also to add background: transparent; so the result: body.scanner-active { --background: transparent; background: transparent; --ion-background-color: transparent; }

felipefcasas commented 1 year ago

Toggle body visibility worked for me Example

async startScan() { const body = document.getElementById('myBodyId'); try { body.style.visibility = 'hidden'; const result = await BarcodeScanner.startScan(); body.style.visibility = 'visible'; } catch (ex) { body.style.visibility = 'visible'; } }

donmb1 commented 1 year ago

I have this issue since upgrading to Ionic 7 I am using vue 3 with setup configuration

This is my camera active class:

body.scanner-active
  --background: transparent
  --ion-background-color: transparent

and here is how I use the camera:

const startScan = async () => {
      document.querySelector('body')?.classList.add('scanner-active')
      // Check camera permission
      // This is just a simple example, check out the better checks below
      await BarcodeScanner.checkPermission({ force: true });

      // make background of WebView transparent
      // note: if you are using ionic this might not be enough, check below
      BarcodeScanner.hideBackground();

      barcodeStore.$patch({
        scanInProgress: true
      })

      const result = await BarcodeScanner.startScan(); // start scanning and wait for a result
      console.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
      console.log(result)

      // if the result has content
      if (result.hasContent) {
        emit('setResult', result.content)
      }
      barcodeStore.$patch({
        scanInProgress: false
      })
    };

Problems:

Logs of xcode:

To Native ->  BarcodeScanner checkPermission 54809842
⚡️  TO JS {"granted":true}
⚡️  To Native ->  BarcodeScanner hideBackground 54809843
⚡️  TO JS undefined
⚡️  To Native ->  BarcodeScanner startScan 54809844
Thread Performance Checker: -[AVCaptureSession startRunning] should be called from background thread. Calling it on the main thread can lead to UI unresponsiveness
PID: 63437, TID: 4347239
Backtrace
=================================================================
3   CapacitorCommunityBarcodeScanner    0x0000000104eef154 $s32CapacitorCommunityBarcodeScanner0cD0C4scan33_E9781981B91FA2D5C67BE1318BC971C3LLyyFyyScMYccfU1_ + 512
4   CapacitorCommunityBarcodeScanner    0x0000000104eed9f4 $sIeg_IeyB_TR + 48
5   libdispatch.dylib                   0x000000010527053c _dispatch_call_block_and_release + 32
6   libdispatch.dylib                   0x0000000105271ff0 _dispatch_client_callout + 20
7   libdispatch.dylib                   0x0000000105282800 _dispatch_main_queue_drain + 1196
8   libdispatch.dylib                   0x0000000105282344 _dispatch_main_queue_callback_4CF + 44
9   CoreFoundation                      0x00000001b1b016c8 42CCFC7B-FF32-3D25-8F01-CCB2AD843A8B + 632520
10  CoreFoundation                      0x00000001b1ae302c 42CCFC7B-FF32-3D25-8F01-CCB2AD843A8B + 507948
11  CoreFoundation                      0x00000001b1ae7eb0 CFRunLoopRunSpecific + 612
12  GraphicsServices                    0x00000001ebcdd368 GSEventRunModal + 164
13  UIKitCore                           0x00000001b3fdd668 CF21AD9C-EFBF-3961-A7C0-54BD30CEFEA9 + 3806824
14  UIKitCore                           0x00000001b3fdd2cc UIApplicationMain + 340
15  App                                 0x0000000104a3bfbc main + 64
16  dyld                                0x00000001d03e0960 4B042F28-0D14-30EC-A1DE-3DBB10866AD7 + 88416

The permission granting works Also the camera seems to "start" somehow But it does not get to the result part at all (maybe because I am not able to take a picture)

All I see is a white screen.

Any ideas?

thegnuu commented 1 year ago

@donmb1 Your setup looks good in general, and the logs look okay as well.

You might just need to check your DOM tree, I am not sure on which level ionic injects the CSS variables, but it might be higher up in the DOM than the body, which means that your variables on the body won't work as you might expect and are overwritten by ionic. Unfortunately, they change this behavior from time to time...

The camera will never show up in the DOM if you are on iOS or Android, this is by design. The camera view is rendered behind the webview in native environments.

donmb1 commented 1 year ago

@thegnuu I see well its a bit confusing to me that it all of a sudden does not work anymore. Now I tried to migrate back to Ionic 6 but I have the same "white screen" problem there and it worked totally fine before. I am not sure what broke it tbh.

thegnuu commented 1 year ago

@donmb1 there were no changes in the behavior of this plugin in the last months, so I guess the problem is somehow related to your project. I would try to clean up everything (node_modules, cached files aso.) and then try again. I had some weird issues trying to migrate between ionic versions in the past as well, but they were usually not related to the plugins I used ;)

donmb1 commented 1 year ago

@thegnuu yeah I know this exercise very well :-D unfortunately it did not help yet I probably need to debug deeper

thegnuu commented 1 year ago

@donmb1 weird, unfortunately, I am not really able to help you and verify that it is not an issue with the plugin without a repo to reproduce your problem. If you can share your stuff I am happy to have a look, if you cannot share it it will be hard for me...

thegnuu commented 1 year ago

I am cleaning up "old" issues and will close this for now. If there are still problems feel free to open a new issue.

GeekSankha commented 1 year ago

Just a little change in CSS and the problem will get resolved. I am using ionic 7

//Import Renderer2 
import { Component, OnInit, Renderer2 } from '@angular/core';

//Call the function like this
async scan() {
        this.renderer.addClass(document.body, 'scanner-active');

        // Check camera permission
        // This is just a simple example, check out the better checks below
        await BarcodeScanner.checkPermission({ force: true });

        // make background of WebView transparent
        // note: if you are using ionic this might not be enough, check below
        BarcodeScanner.hideBackground();

        const result = await BarcodeScanner.startScan({ targetedFormats: [SupportedFormat.QR_CODE] }); // start scanning and wait for a result

        // if the result has content
        if (result.hasContent) {
            console.log(result.content); // log the raw scanned content

            this.renderer.removeClass(document.body, 'scanner-active');
        }
}
// global.scss
body.scanner-active {
    --background: transparent;
    --ion-background-color: transparent;
    opacity: 0; // Add this line is global.scss file
}

Hope this will solve the problem.

bkvelan commented 1 year ago

in same component add

< div id="container" >
< /div > and in app.component.html in ion-app -> < ion-app class="scanner-hide" >

at the end add < div class="scanner-ui"> < /div >

and in global.scss

.scanner-ui { display: none; } .scanner-hide { visibility: visible; }

body.qrscanner { background-color: transparent; } body.qrscanner .scanner-ui { display: block; } body.qrscanner .scanner-hide { visibility: hidden; }

Francisco-JC commented 1 year ago

Holaa quiero compartir mi solución para el botón de regreso

//estilos en global.css

root.scanner-active {

--background: transparent;
--ion-background-color: transparent;
opacity: 0; // Add this line is global.scss file

}

CancelScanner{

display: none;

}

CancelScanner.return{

display: flex;
z-index: 300;
position: absolute;
background-color: #272761;
max-height: 150px;
align-items: center;
color: #ffffff;
bottom: 0;
padding: 10px 20px;
width: 100%;

} .return img{ width: 40px; } .return p{ margin-left: 30px; } ////////////////////////////////////////////// index.html

Cancelar

//////////////////////////////////////////// componente de implementacion

ademas importe import { Component,Renderer2 } from '@angular/core';

en el constructor private renderer:Renderer2

async scanQR() {

this.renderer.addClass(document.body, 'scanner-active');

    // Check camera permission
    // This is just a simple example, check out the better checks below
    await BarcodeScanner.checkPermission({ force: true });

    // make background of WebView transparent
    // note: if you are using ionic this might not be enough, check below
    BarcodeScanner.hideBackground();
this.renderer.addClass(document.getElementById('CancelScanner'), 'return');
    const result = await BarcodeScanner.startScan({ targetedFormats: [SupportedFormat.QR_CODE] }); // start scanning and wait for a result

    // if the result has content
    if (result.hasContent) {
        console.log(result.content); // log the raw scanned content

        this.renderer.removeClass(document.body, 'scanner-active');
  this.tbCodigo=result.content
    }console.log(result.content); // log the raw scanned content

}

posiblemente para el fondo lo mas sencillo sea crear un png con el centro transparente

CABE ACLARAR QUE CASI NO CONOZCO IONIC Y NO SE SI ESTA SOLUCION PUEDA AFECTAR EL RENDIMIENTO O ALGO MAS

TonhaoSantos commented 5 months ago

Just a little change in CSS and the problem will get resolved. I am using ionic 7

//Import Renderer2 
import { Component, OnInit, Renderer2 } from '@angular/core';

//Call the function like this
async scan() {
      this.renderer.addClass(document.body, 'scanner-active');

      // Check camera permission
      // This is just a simple example, check out the better checks below
      await BarcodeScanner.checkPermission({ force: true });

      // make background of WebView transparent
      // note: if you are using ionic this might not be enough, check below
      BarcodeScanner.hideBackground();

      const result = await BarcodeScanner.startScan({ targetedFormats: [SupportedFormat.QR_CODE] }); // start scanning and wait for a result

      // if the result has content
      if (result.hasContent) {
          console.log(result.content); // log the raw scanned content

          this.renderer.removeClass(document.body, 'scanner-active');
      }
}
// global.scss
body.scanner-active {
    --background: transparent;
    --ion-background-color: transparent;
    opacity: 0; // Add this line is global.scss file
}

Hope this will solve the problem.

Tks

If you are using vue.js 3, you can set it like this within the component itself, without having to do it globally like @GeekSankha tip

:global(body.barcode-scanning-active) {
    --background: transparent;
    --ion-background-color: transparent;
}