'cordova-plugin-camera' with Quasar
-
Hello,
I am looking to use the Cordova plugin “cordova-plugin-camera”.
If I create a project directly under cordova, I have no problem.
If I use the example of the documentation of quasar framework for this plugin, the photo does not appear after using the camera and I have a message in the console of the ‘remote devices’ of Chrome "Failed to load resource: net :: ERR_INVALID_URL "with the url of my picture.
Has anyone ever had this problem?
Thank you for your help. -
you can access the camera with the html5 API, did you know that? I could share with you some (still dirty) code I’m currently working on:
<template> <section> <q-modal v-model="opened"> <section class="relative-position overflow-hidden fit"> <figure class="no-margin" v-if="!imageUrl"> <video ref="video" :src="source" class="fixed-center" :height="camHeight" /> </figure> <div> <img v-if="bin.images.length > 0" class="rounded-borders border-primary avatar-lg absolute-bottom-right last-pic-avatar" :src="lastImg" /> <loading v-if="loadingCamera"> Loading... </loading> <img v-if="imageUrl" class="window-width" :src="imageUrl" alt="Image taken"> <!--<div class="fullscreen" v-if="notSupported !== false">--> <!--<h2>This browser does not support the camera.</h2>--> <!--<p>--> <!--Please download <a href="https://www.mozilla.org">Firefox</a> <a href="https://www.google.com/chrome">Chrome</a>--> <!--</p>--> <!--</div>--> <cam-btn-group class="no-shadow bg-black flat"> <div v-if="imageUrl"> <q-btn @click="onConfirmImage" class="absolute-center" flat color="white" icon="far fa-check-circle" size="xl" /> <q-page-sticky position="bottom-right" :offset="[10, -54]"> <q-btn @click="onCancelImage" class="q-ml-xl" flat color="white" icon="far fa-times-circle" size="xl" wait-for-ripple /> </q-page-sticky> </div> <div v-else> <q-btn @click="onCapture" class="absolute-center" flat color="white" icon="far fa-dot-circle" size="xl"/> <q-page-sticky position="bottom-right" :offset="[10, -60]"> <q-btn @click="exitCamera" label="Next" wait-for-ripple flat color="white" icon-right="fas fa-long-arrow-alt-right" size="xl" /> </q-page-sticky> </div> </cam-btn-group> </div> </section> </q-modal> </section> </template> <script> import { get } from 'vuex-pathify' import messages from '../../common/messages.json' import Loading from '../VLoading' import CamBtnGroup from '../VFooterBtns.vue' export default { name: 'Camera', components: { Loading, CamBtnGroup }, data () { return { opened: true, mediaStream: null, imageUrl: null, isDefaultBackCamera: true, camera: null, cameras: [], source: null, loadingCamera: true } }, computed: { ...get('DeployModule/*'), hasBackCamera () { const backCameraDevices = this.cameras.filter(device => { return device.label.indexOf('back') !== -1 }) return backCameraDevices.length > 1 ? backCameraDevices[0] : false }, camHeight () { return window.innerHeight }, lastImg () { return this.bin.images.slice(-1) } }, mounted () { if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {} } if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = this.legacyGetUserMediaSupport() } let constraints = this.isDefaultBackCamera ? 'environment' : 'user' constraints = { video: { facingMode: { ideal: constraints } } } this.testMediaAccess(constraints) }, methods: { legacyGetUserMediaSupport () { return constraints => { // First get ahold of the legacy getUserMedia, if present let getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia // Some browsers just don't implement it - return a rejected promise with an error // to keep a consistent interface if (!getUserMedia) { return Promise.reject( new Error('getUserMedia is not implemented in this browser') ) } // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise return new Promise(function (resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject) }) } }, testMediaAccess (constraints) { // Check if there's the camera set by isDefaultBackCamera navigator.mediaDevices.getUserMedia(constraints).then(stream => { this.getCameras() }).catch(() => { this.$q.notify(messages.camera.error.backCamera) this.testMediaAccess({ video: true }) }) }, getCameras () { // GET The cameras && and get the Back Camera navigator.mediaDevices.enumerateDevices().then(deviceInfos => { this.loadingCamera = false deviceInfos.forEach(device => { const constraints = this.hasBackCamera ? 'back' : 'amera' // Camera string could be Capital if (device.label.indexOf(constraints) !== -1) { this.camera = device } if (device.kind.indexOf('videoinput') !== -1) { this.cameras.push(device) } }) this.setCamera(this.camera.deviceId) }) }, setCamera (deviceId) { navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: deviceId } } }).then(mediaStream => { this.mediaStream = mediaStream /** Load the stream to the video tag**/ if ('srcObject' in this.$refs.video) { // new browsers api this.$refs.video.srcObject = mediaStream this.$refs.video.onloadedmetadata = () => { this.$refs.video.play() } } else { // old broswers this.mediaStream = window.HTMLMediaElement.srcObject(mediaStream) } }).catch(() => this.$q.notify(messages.camera.error.camera)) }, onCapture () { const mediaStreamTrack = this.mediaStream.getVideoTracks()[0] const imageCapture = new window.ImageCapture(mediaStreamTrack) return imageCapture.takePhoto().then(blob => { this.imageUrl = URL.createObjectURL(blob) }) }, onConfirmImage () { this.bin.images.push(this.imageUrl) this.$store.set('DeployModule/bin@images', this.bin.images) this.imageUrl = null this.setCamera(this.camera.deviceId) }, onCancelImage () { this.imageUrl = null this.setCamera(this.camera.deviceId) }, exitCamera () { let stream = this.$refs.video.srcObject let tracks = stream.getTracks() tracks.forEach(track => { // stops the video track track.stop() }) this.$refs.video.srcObject = null this.mediaStream = null this.$emit('stopped') this.opened = false } } } </script> <style lang="stylus" scoped> .icon-group .wrapper-icon font-size 36px .inner-icon position relative font-size 20px top 4px left -54px z-index 99 .last-pic-avatar margin 0 60px 70px 0 z-index 9999999999 .arrow-exit position absolute left 0 top 5px </style>
-
Thank you, but I would really like to use this plugin.
- Here is the cordova code that I use to do my tests which is not a problem:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; script-src 'self' 'unsafe-inline'; img-src 'self' data: content: ;"/> <meta name="format-detection" content="telephone=no" /> <meta name="msapplication-tap-highlight" content="no" /> <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width" /> <link rel="stylesheet" type="text/css" href="css/index.css" /> <title>Hello World</title> </head> <body> <div class="page"> <p><img src="img/logo.png" alt="image" id="photo" /></p> <p><button id="btn">Take picture</button></p> <p id="msg"></p> </div> <script type="text/javascript" src="cordova.js"></script> <script> let app = { init: function() { document .getElementById('btn') .addEventListener('click', app.takephoto); }, takephoto: function() { let opts = { quality: 80, destinationType: Camera.DestinationType.FILE_URI, sourceType: Camera.PictureSourceType.CAMERA, mediaType: Camera.MediaType.PICTURE, encodingType: Camera.EncodingType.JPEG, cameraDirection: Camera.Direction.BACK, targetWidth: 300, targetHeight: 400 }; navigator.camera.getPicture(app.ftw, app.wtf, opts); }, ftw: function(imgURI) { console.log(imgURI) document.getElementById('msg').textContent = imgURI; document.getElementById('photo').src = imgURI; }, wtf: function(msg) { document.getElementById('msg').textContent = msg; } }; document.addEventListener('deviceready', app.init); </script> </body> </html>
- And now the code under Quasar where the image does not appear.
<template> <div class="page"> <img :src="photo" alt="photo" /> <button @click="takephoto">Take picture</button> <p>{{msg}}</p> </div> </template> <script> export default { name: 'hello', data () { return { msg: '', photo: 'statics/quasar-logo.png' } }, methods: { takephoto: function () { let opts = { quality: 80, destinationType: Camera.DestinationType.FILE_URI, sourceType: Camera.PictureSourceType.CAMERA, mediaType: Camera.MediaType.PICTURE, encodingType: Camera.EncodingType.JPEG, cameraDirection: Camera.Direction.BACK, targetWidth: 300, targetHeight: 400 } navigator.camera.getPicture(this.ftw, this.wtf, opts) }, ftw: function (imgURI) { this.msg = imgURI this.photo = imgURI }, wtf: function (msg) { this.msg = msg } } } </script> <style> </style>
-
I use the same plugin for a project and it works perfectly.
Your code does look very similar to mine. The only difference I’ve spotted is that I’ve declared navigator before camera in the options like so:
encodingType: navigator.camera.EncodingType.JPEG,
mediaType: navigator.camera.MediaType.PICTURE,Hope this helps
-
@Lou-Rectoret
Good Morning. The example that you use is very useful to me, only that at this moment it is generating problems to me “… get (‘DeployModule / *’)”, you could help, showing the code of that module. Thank you so much. -
Hey @desman !
“… get (‘DeployModule / *’)
is the way vuex-pathify works for mapping the State less verbose.So I’m only storing the image path in the Central State, see this:
That’s all, I’m using vuex-pathify on top of Vuex.
Try setting the path on the data property of the component, you should be fine.GL
-
@Kevin O meu ta igual ao seu a diferença é que nas options eu pego a imagem com: destinationType: navigator.camera.DestinationType.DATA_URL
-
@Kevin ```
Código completo!<template> <q-page class="flex flex-center"> <q-btn color="primary" label="Get Picture" @click="captureImage" /> <q-img :src="imageSrc" placeholder-src="statics/quasar-logo.png" :alt="'Imagem: ' + imageSrc" id="photo" /> </q-page> </template> <script> export default { data () { return { imageSrc: '' } }, methods: { captureImage () { navigator.camera.getPicture( data => { // on success this.imageSrc = `data:image/jpeg;base64, ${data}` // document.getElementById('photo').src = data alert(this.imageSrc) }, () => { // on fail this.$q.notify('Não foi possível acessar a câmera do dispositivo.') }, { // camera options quality: 50, destinationType: navigator.camera.DestinationType.DATA_URL, encodingType: navigator.camera.EncodingType.JPEG, MEDIATYPE: navigator.camera.MediaType.PICTURE, sourceType: navigator.camera.PictureSourceType.CAMERA, mediaType: navigator.camera.MediaType.PICTURE, cameraDirection: navigator.camera.Direction.BACK, targetWidth: 300, targetHeight: 400 } ) } } } </script>