1616 class =" qrcode-reader__tracking-layer"
1717 ></canvas>
1818
19- <video
20- ref="video"
21- class="qrcode-reader__camera-layer"
22- ></video >
19+ <div class="qrcode-reader__camera-layer" ref="videoWrapper">
20+ < canvas ref =" pauseFrame " ></canvas>
21+ <video ref="video"></video>
22+ </div >
2323 </div>
2424 </div>
2525</template >
2828import * as Scanner from ' ../misc/scanner.js'
2929import Camera from ' ../misc/camera.js'
3030import { imageDataFromFile , imageDataFromUrl } from ' ../misc/image-data.js'
31- import { hasFired } from ' ../misc/promisify.js'
3231import isBoolean from ' lodash/isBoolean'
3332
3433export default {
34+
3535 props: {
3636 paused: {
3737 type: Boolean ,
@@ -60,7 +60,6 @@ export default {
6060 return {
6161 cameraInstance: null ,
6262 destroyed: false ,
63- readyAfterPause: true ,
6463 }
6564 },
6665
@@ -69,8 +68,7 @@ export default {
6968 shouldScan () {
7069 return this .paused === false &&
7170 this .cameraInstance !== null &&
72- this .destroyed === false &&
73- this .readyAfterPause
71+ this .destroyed === false
7472 },
7573
7674 /**
@@ -161,21 +159,11 @@ export default {
161159 }
162160 },
163161
164- async paused (paused ) {
165- const video = this .$refs .video
166-
162+ paused (paused ) {
167163 if (paused) {
168- video .pause ()
169-
170- this .readyAfterPause = false
164+ this .stopPlayback ()
171165 } else {
172- this .repaintTrack (null )
173-
174- video .play ()
175-
176- await hasFired (video, ' timeupdate' )
177-
178- this .readyAfterPause = true
166+ this .startPlayback ()
179167 }
180168 },
181169
@@ -193,24 +181,20 @@ export default {
193181 },
194182
195183 beforeDestroy () {
196- if (this .cameraInstance !== null ) {
197- this .cameraInstance .stop ()
198- }
199-
184+ this .beforeResetCamera ()
200185 this .destroyed = true
201186 },
202187
203188 methods: {
204189
205190 async init () {
206- if (this .cameraInstance !== null ) {
207- this .cameraInstance .stop ()
208- }
191+ this .beforeResetCamera ()
209192
210- if (this .videoConstraints === false ) {
193+ if (this .constraints . video === false ) {
211194 this .cameraInstance = null
212195 } else {
213196 this .cameraInstance = await Camera (this .constraints , this .$refs .video )
197+ this .startPlayback ()
214198 }
215199 },
216200
@@ -223,6 +207,14 @@ export default {
223207 })
224208 },
225209
210+ beforeResetCamera () {
211+ if (this .cameraInstance !== null ) {
212+ this .stopPlayback ()
213+ this .cameraInstance .stop ()
214+ this .cameraInstance = null
215+ }
216+ },
217+
226218 onLocate (location ) {
227219 if (this .trackRepaintFunction !== null ) {
228220 this .repaintTrack (location)
@@ -320,6 +312,64 @@ export default {
320312 })
321313 },
322314
315+ startPlayback () {
316+ this .unlockCameraLayerSize ()
317+ this .repaintTrack (null )
318+
319+ const pauseFrame = this .$refs .pauseFrame
320+ const ctx = pauseFrame .getContext (' 2d' )
321+
322+ ctx .clearRect (0 , 0 , pauseFrame .width , pauseFrame .height )
323+ },
324+
325+ stopPlayback () {
326+ this .lockCameraLayerSize ()
327+
328+ const pauseFrame = this .$refs .pauseFrame
329+ const ctx = pauseFrame .getContext (' 2d' )
330+ const cameraInstance = this .cameraInstance
331+
332+ const displayWidth = cameraInstance .displayWidth
333+ const displayHeight = cameraInstance .displayHeight
334+
335+ pauseFrame .width = displayWidth
336+ pauseFrame .height = displayHeight
337+
338+ ctx .drawImage (this .$refs .video , 0 , 0 , displayWidth, displayHeight)
339+ },
340+
341+ /*
342+ * When a new stream is requested, the video element looses its width and
343+ * height, causing the component to collapse until the new stream is loaded.
344+ * Copying the size from the video element to its wrapper div compensates
345+ * for this effect.
346+ */
347+ lockCameraLayerSize () {
348+ const videoWrapper = this .$refs .videoWrapper
349+ const cameraInstance = this .cameraInstance
350+
351+ if (cameraInstance !== null ) {
352+ console .log (' TEST' )
353+
354+ const displayWidth = cameraInstance .displayWidth
355+ const displayHeight = cameraInstance .displayHeight
356+
357+ videoWrapper .style .width = displayWidth + ' px'
358+ videoWrapper .style .height = displayHeight + ' px'
359+ }
360+ },
361+
362+ /**
363+ * The video elements wrapper div should not have a fixed size all the so
364+ * it can be responsive.
365+ */
366+ unlockCameraLayerSize () {
367+ const videoWrapper = this .$refs .videoWrapper
368+
369+ videoWrapper .style .width = ' '
370+ videoWrapper .style .height = ' '
371+ },
372+
323373 },
324374}
325375 </script >
@@ -337,14 +387,24 @@ export default {
337387}
338388
339389.qrcode-reader__camera-layer {
390+ position : relative ;
391+ z-index : 10 ;
392+ }
393+
394+ .qrcode-reader__camera-layer > video {
340395 display : block ;
341396 object-fit : contain ;
397+ }
398+
399+ .qrcode-reader__inner-wrapper ,
400+ .qrcode-reader__camera-layer ,
401+ .qrcode-reader__camera-layer > video {
342402 max-width : 100% ;
343403 max-height : 100% ;
344- z-index : 10 ;
345404}
346405
347406.qrcode-reader__overlay ,
407+ .qrcode-reader__camera-layer > canvas ,
348408.qrcode-reader__tracking-layer {
349409 position : absolute ;
350410 width : 100% ;
0 commit comments