I have a b-modal
with a camara app.
First I had my <video>
and my canvas
seperate – but now I want to have them overlapping each other. So first I want to show my video
with my button make photo
– if I press this my canvas
with the taken photo and the other two buttons delete
upload
should be shown.
But I always get following error (I made a comment in my code where the error is):
[Vue warn]: Error in v-on handler: “TypeError: Cannot read properties of null (reading ‘getContext’)”
How can I handle this? Thanks!
<template> <div :id="IDParent+'camera'"> <div class="text-center" info> <div center :close="false"> <p class="heading">Make photo</p> </div> <div> <video v-if="hide" class="mb-4" ref="video" id="video" width="100%" height="100%" autoplay /> <canvas v-if="!hide" class="mb-4" id="responsive-canvas"></canvas> <div class="row mb-5"> <div class="col-md-12"> <b-button v-if="hide" variant="block p-3 colorSuccess" @click="takePicture()"><b-icon icon="camera-fill"></b-icon> Make Photo</b-button> </div> <div class="col-md-4"> <b-button v-if="!hide" variant="block p-3 colorDanger mb-2" @click="deletePicture()"><b-icon icon="exclamation-circle-fill"></b-icon> Delete </b-button> </div> <div class="col-md-8"> <b-button v-if="!hide" variant="block p-3 colorSuccess" @click="uploadPicture()"><b-icon icon="cloud-upload"></b-icon> upload photo</b-button> </div> </div> </div> </div> </div> </template>
<script> export default { data() { return { video: {}, canvas: {}, captures: [], push: false, hide: true, } }, props: [ "IDParent", ], mounted() { this.video = this.$refs.video; if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({ video: true }).then(stream => { this.video.srcObject = stream; this.video.play(); this.video.onplay = function () { }; this.video.play(); }); } }, methods: { closeModal() { }, uploadPicture() { }, takePicture() { if(this.hide == true) { const picture = document.querySelector("canvas"); //ERROR IN THIS LINE! const ctx = picture.getContext("2d"); ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = "high"; ctx.drawImage(document.querySelector("video"), 0, 0, picture.width, picture.height); } this.hide = false; }, showCanvas() { var canvas = document.getElementById('responsive-canvas'); var heightRatio = 0.75; canvas.height = canvas.width * heightRatio; }, deletePicture() { const inputPicture = document.querySelector("canvas"); const context = inputPicture.getContext("2d"); context.clearRect(0, 0, inputPicture.width, inputPicture.height); this.hide = true; }, capture() { this.canvas = this.$refs.canvas; this.canvas.getContext("2d").drawImage(this.video, 0, 0, 640, 480); this.captures.push(); }, } } </script>
Advertisement
Answer
If you carefully observe the below function,
takePicture() { if(this.hide == true) { const picture = document.querySelector("canvas"); //ERROR IN THIS LINE! const ctx = picture.getContext("2d"); ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = "high"; ctx.drawImage(document.querySelector("video"), 0, 0, picture.width, picture.height); } this.hide = false; },
the above gets triggered on clicking the below button
<b-button v-if="hide" variant="block p-3 colorSuccess" @click="takePicture()"><b-icon icon="camera-fill"></b-icon> Make Photo</b-button>
Note the condition v-if=”hide”, at this instant you canvas is hidden from the Dom because of the below condition that you have used
<canvas v-if="!hide" class="mb-4" id="responsive-canvas"></canvas>
Thats the reason why when you try to query it, its showing error.
Modify your condition so that its available on DOM and then try to access it