1616 class =" qrcode-reader__tracking-layer"
1717 ></canvas>
1818
19- <div class="qrcode-reader__camera-layer" ref="videoWrapper">
20- <canvas ref =" pauseFrame" ></canvas>
21- <video ref="video"></video>
22- </div>
19+ <canvas
20+ ref="pauseFrame"
21+ v-show="!shouldScan"
22+ class="qrcode-reader__pause-frame"
23+ ></canvas>
24+
25+ <video
26+ ref="video"
27+ v-show="shouldScan"
28+ class="qrcode-reader__camera"
29+ ></video>
2330 </div>
2431 </div>
2532</template >
@@ -65,10 +72,15 @@ export default {
6572
6673 computed: {
6774
68- shouldScan () {
75+ shouldStream () {
6976 return this .paused === false &&
70- this .cameraInstance !== null &&
71- this .destroyed === false
77+ this .destroyed === false &&
78+ this .constraints .video !== false
79+ },
80+
81+ shouldScan () {
82+ return this .shouldStream === true &&
83+ this .cameraInstance !== null
7284 },
7385
7486 /**
@@ -148,22 +160,19 @@ export default {
148160 },
149161
150162 watch: {
151- /**
152- * Starts continuous scanning process as soon as conditions for that are
153- * fullfilled. The process stops itself automatically when the conditions
154- * are not fullfilled anymore.
155- */
156- shouldScan (shouldScan ) {
157- if (shouldScan) {
158- this .startScanning ()
163+
164+ shouldStream (shouldStream ) {
165+ if (! shouldStream) {
166+ const frame = this .cameraInstance .captureFrame ()
167+ this .paintPauseFrame (frame)
159168 }
160169 },
161170
162- paused ( paused ) {
163- if (paused ) {
164- this .stopPlayback ()
165- } else {
166- this .startPlayback ()
171+ shouldScan ( shouldScan ) {
172+ if (shouldScan ) {
173+ this .clearPauseFrame ()
174+ this . clearTrackingLayer ()
175+ this .startScanning ()
167176 }
168177 },
169178
@@ -194,7 +203,6 @@ export default {
194203 this .cameraInstance = null
195204 } else {
196205 this .cameraInstance = await Camera (this .constraints , this .$refs .video )
197- this .startPlayback ()
198206 }
199207 },
200208
@@ -209,15 +217,18 @@ export default {
209217
210218 beforeResetCamera () {
211219 if (this .cameraInstance !== null ) {
212- this .stopPlayback ()
213220 this .cameraInstance .stop ()
214221 this .cameraInstance = null
215222 }
216223 },
217224
218225 onLocate (location ) {
219226 if (this .trackRepaintFunction !== null ) {
220- this .repaintTrack (location)
227+ if (location === null ) {
228+ this .clearTrackingLayer ()
229+ } else {
230+ this .repaintTrackingLayer (location)
231+ }
221232 }
222233 },
223234
@@ -287,85 +298,57 @@ export default {
287298 return normalized
288299 },
289300
290- repaintTrack (location ) {
301+ repaintTrackingLayer (location ) {
302+ const video = this .$refs .video
291303 const canvas = this .$refs .trackingLayer
292304 const ctx = canvas .getContext (' 2d' )
293- const cameraInstance = this .cameraInstance
294305
295- window .requestAnimationFrame (() => {
296- if (location === null ) {
297- ctx .clearRect (0 , 0 , canvas .width , canvas .height )
298- } else {
299- const displayWidth = cameraInstance .displayWidth
300- const displayHeight = cameraInstance .displayHeight
306+ const displayWidth = video .offsetWidth
307+ const displayHeight = video .offsetWidth
308+ const resolutionWidth = video .videoWidth
309+ const resolutionHeight = video .videoHeight
301310
302- canvas .width = displayWidth
303- canvas .height = displayHeight
311+ window .requestAnimationFrame (() => {
312+ canvas .width = displayWidth
313+ canvas .height = displayHeight
304314
305- const widthRatio = displayWidth / cameraInstance . resolutionWidth
306- const heightRatio = displayHeight / cameraInstance . resolutionHeight
315+ const widthRatio = displayWidth / resolutionWidth
316+ const heightRatio = displayHeight / resolutionHeight
307317
308- location = this .normalizeLocation (widthRatio, heightRatio, location)
318+ location = this .normalizeLocation (widthRatio, heightRatio, location)
309319
310- this .trackRepaintFunction (location, ctx)
311- }
320+ this .trackRepaintFunction (location, ctx)
312321 })
313322 },
314323
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
324+ clearTrackingLayer () {
325+ const canvas = this .$refs .trackingLayer
326+ const ctx = canvas .getContext (' 2d' )
337327
338- ctx .drawImage (this .$refs .video , 0 , 0 , displayWidth, displayHeight)
328+ window .requestAnimationFrame (() => {
329+ ctx .clearRect (0 , 0 , canvas .width , canvas .height )
330+ })
339331 },
340332
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
333+ paintPauseFrame (imageData ) {
334+ const canvas = this .$refs .pauseFrame
335+ const ctx = canvas .getContext (' 2d' )
350336
351- if (cameraInstance !== null ) {
352- const displayWidth = cameraInstance . displayWidth
353- const displayHeight = cameraInstance . displayHeight
337+ window . requestAnimationFrame (() => {
338+ canvas . width = imageData . width
339+ canvas . height = imageData . height
354340
355- videoWrapper .style .width = displayWidth + ' px'
356- videoWrapper .style .height = displayHeight + ' px'
357- }
341+ ctx .putImageData (imageData, 0 , 0 )
342+ })
358343 },
359344
360- /**
361- * The video elements wrapper div should not have a fixed size all the so
362- * it can be responsive.
363- */
364- unlockCameraLayerSize () {
365- const videoWrapper = this .$refs .videoWrapper
345+ clearPauseFrame () {
346+ const canvas = this .$refs .pauseFrame
347+ const ctx = canvas .getContext (' 2d' )
366348
367- videoWrapper .style .width = ' '
368- videoWrapper .style .height = ' '
349+ window .requestAnimationFrame (() => {
350+ ctx .clearRect (0 , 0 , canvas .width , canvas .height )
351+ })
369352 },
370353
371354 },
@@ -382,27 +365,11 @@ export default {
382365
383366.qrcode-reader__inner-wrapper {
384367 position : relative ;
385- }
386-
387- .qrcode-reader__camera-layer {
388- position : relative ;
389- z-index : 10 ;
390- }
391-
392- .qrcode-reader__camera-layer > video {
393- display : block ;
394- object-fit : contain ;
395- }
396-
397- .qrcode-reader__inner-wrapper ,
398- .qrcode-reader__camera-layer ,
399- .qrcode-reader__camera-layer > video {
400368 max-width : 100% ;
401369 max-height : 100% ;
402370}
403371
404372.qrcode-reader__overlay ,
405- .qrcode-reader__camera-layer > canvas ,
406373.qrcode-reader__tracking-layer {
407374 position : absolute ;
408375 width : 100% ;
@@ -411,6 +378,14 @@ export default {
411378 left : 0 ;
412379}
413380
381+ .qrcode-reader__camera ,
382+ .qrcode-reader__pause-frame {
383+ display : block ;
384+ object-fit : contain ;
385+ max-width : 100% ;
386+ max-height : 100% ;
387+ }
388+
414389.qrcode-reader__overlay {
415390 z-index : 30 ;
416391}
0 commit comments