instascan icon indicating copy to clipboard operation
instascan copied to clipboard

Instascan instance not displaying the video stream

Open khawarizmus opened this issue 6 years ago • 5 comments

in my application i have two instascan instances in two different vue.js components that lives in two different routes (pages) the code is identical but one route shows the video stream whereas the other page doesn't

in the DOM the first page have this

<video id="scanner" autoplay="autoplay" style="transform: scaleX(-1);" class="inactive" src=""></video>

whereas the second page seems that instascan is not able to mount the camera properly and i am getting this

<video id="QRscanner"></video>

I have tried to use different variable names in my components and different CSS ids but in vain

and what is strange tho is that the scanner is actually working and i am scanning the QR codes but the video stream is not showing

and it's not a CSS issue since i have checked and used background-color for the video element and it's visible I also rose the z-index to a big number but again nothing and i am sure it's something with instascan not able to mount properly but i can't figure out why since i am not getting any error

I have also tried to destroy the video element before leaving the rout and assign a null value to the camera and instascan instance variable

beforeRouteLeave(to, from, next) {
    EventBus.$emit('close');
    this.camera = null;
    this.cameras = null;
    this.scanner = null;
    const video = document.getElementById('scanner');
    console.log(video.parentNode);
    video.parentNode.removeChild(video);
    next();
  },

any help would be very much appreciated

khawarizmus avatar Oct 05 '17 14:10 khawarizmus

Can you show your JS code

mairh avatar Oct 05 '17 14:10 mairh

Ok here i go @mairh

    <div v-show="scanning" class="QRScanner-lock" @click="scanning = false" >......</div>
    <div v-show="scanning" class="QRScanner-container">
      <el-select v-model="camera" placeholder="Choose a camera">
        <el-option v-for="camera in cameras" :key="camera.id" :label="camera.name" :value="camera">
        </el-option>
      </el-select>
      <video id="scanner"></video>
    </div>
    <el-button type="danger" style="margin: 20px;" @click="scanning = !scanning" class="button">
      <icon name="qrcode" scale="2"></icon><span class="text">Scan my Barsaris</span>
    </el-button>

the script part

export default {
 data() {
  return {
      scanning: false,
      scanner: null,
      cameras: [],
      camera: null,
     };
},
beforeCreate() {
    EventBus.$emit('loading-stop');
    Instascan.Camera.getCameras().then((cameras) => {
      this.cameras = cameras;
      if (cameras.length > 0) {
        this.camera = cameras[0];
        this.$message({
          message: `found ${cameras.length} cameras`,
          type: 'success',
          showClose: true,
        });
      } else {
        this.$notify({
          message: 'No camera found!',
          type: 'error',
          showClose: true,
        });
      }
    }).catch((e) => {
      this.$message({
        message: 'an Internal error occured',
        type: 'error',
        showClose: true,
      });
    });
  },
mounted() {
    EventBus.$emit('loading-stop');

    const that = this;
    this.scanner = new Instascan.Scanner({
      video: document.getElementById('scanner'),
    });

    this.scanner.addListener('scan', (result) => {
      console.log(result);
    });
  },
  watch: {
    scanning() {
      if (this.scanning === true) {
        if (this.cameras.length > 0) {
          this.scanner.start(this.camera);
        } else {
          Instascan.Camera.getCameras().then((cameras) => {
            this.cameras = cameras;
            if (cameras.length > 0) {
              this.camera = cameras[0];
              this.$message({
                message: `found ${cameras.length} cameras`,
                type: 'success',
                showClose: true,
              });
              this.scanner.start(this.camera);
            } else {
              this.cameras = [];
              this.camera = null;
              this.$notify({
                message: 'No camera found!',
                type: 'error',
                showClose: true,
              });
            }
          }).catch((e) => {
            this.$message({
              message: 'Camera detection error',
              type: 'error',
              showClose: true,
            });
          });
        }
      } else {
        this.scanner.stop(this.camera);
      }
    },
  },
  beforeRouteLeave(to, from, next) {
    EventBus.$emit('close');
    // TODO: if this code doesn't solve anything we need to omit it
    this.camera = null;
    this.cameras = null;
    this.scanner = null;
    const video = document.getElementById('scanner');
    console.log(video.parentNode);
    video.parentNode.removeChild(video);
    next();
  },
}

of course i have put only the part that is relevant to this question i omitted all the other code notice that this works perfectly in the first component but doesn't on the second component where i renamed scanner to scanner2 in the data part and renamed the video tag id

also as i mentioned before even tho the video stream is not showing on the second component it's working perfectly and i am able to scan the QR code and i am not getting any errors

so the problem lies when the instscan instance mount on the video tag i have tried wrapping the following snippet in a try and catch and it was clean without errors even tho the instance was not mounted properly in my opinion

this.scanner2 = new Instascan.Scanner({
      video: document.getElementById('scanner'),
    });

khawarizmus avatar Oct 05 '17 15:10 khawarizmus

I will have a look in a while as soon as I get on my laptop.

mairh avatar Oct 05 '17 16:10 mairh

@gimyboya Can you try to create 2 different Vue initialization instances for your 2 separate version?

Something along the line

var app = new Vue({
  el: '#instance-1',
  data: {
    scanner: null,
    activeCameraId: null,
    cameras: [],
    scans: []
  },
  mounted: function () {
    // Something
  },
  methods: {
    // Something
  }
});

and

var app = new Vue({
  el: '#instance-2',
  data: {
    scanner: null,
    activeCameraId: null,
    cameras: [],
    scans: []
  },
  mounted: function () {
    // Something
  },
  methods: {
    // Something
  }
});

and wrap your html on 2 different routes like this

<div id="instance-1">
  // Your html code
</div>

and

<div id="instance-2">
  // Your html code
</div>

mairh avatar Oct 06 '17 07:10 mairh

try with

rudy-randria avatar May 10 '20 17:05 rudy-randria