Created
November 2, 2019 12:40
-
-
Save danielsmykowski1/89109403096188692748bee7f17d84d6 to your computer and use it in GitHub Desktop.
AngularDart Qrcode scan component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| :host { | |
| position: absolute; | |
| top: 0; | |
| height: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| overflow: hidden; | |
| } | |
| .scan-video { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| opacity: 0; | |
| object-fit: cover; | |
| } | |
| .scan-image { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: contain; | |
| opacity: 0; | |
| } | |
| .scan-image.show, .scan-video.show { | |
| opacity: 1; | |
| } | |
| .camera-button { | |
| position: absolute; | |
| top: 20px; | |
| right: 20px; | |
| background-color: #3f51b5; | |
| color: #fff; | |
| } | |
| input[type="file"] { | |
| display: none; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'dart:async'; | |
| import 'dart:html'; | |
| import 'package:angular/angular.dart'; | |
| import 'package:angular_components/angular_components.dart'; | |
| import 'package:angular_router/angular_router.dart'; | |
| import 'package:cashjar_dart/src/screens/qrcode_scan/QRReader.dart'; | |
| import 'package:cashjar_dart/src/services/title_service.dart'; | |
| @Component( | |
| selector: 'qrcode-scan', | |
| templateUrl: 'qrcode_scan_component.html', | |
| styleUrls: ['qrcode_scan_component.css'], | |
| directives: [coreDirectives, MaterialFabComponent, MaterialIconComponent], | |
| providers: [materialProviders], | |
| ) | |
| class QrcodeScanComponent implements OnActivate { | |
| final TitleService _titleService; | |
| String imageUrl = ''; | |
| QRReader _reader; | |
| QrcodeScanComponent(this._titleService); | |
| @override | |
| void onActivate(RouterState previous, RouterState current) { | |
| _titleService.title = ''; | |
| _titleService.showTitle = false; | |
| _reader = QRReader(callback: handleQrCodeScanned); | |
| _reader.init(); | |
| Timer(Duration(seconds: 1), () { | |
| if (MediaStream.supported) { | |
| scan(false); | |
| } | |
| }); | |
| } | |
| void handleQrCodeScanned(String qrcode) { | |
| print('scanned qrcode : $qrcode'); | |
| window.alert('scanned qrcode: $qrcode'); | |
| } | |
| void handleFileChange(FileList files) async { | |
| if (files.isNotEmpty && files.first.type.startsWith('image/')) { | |
| var imageReader = FileReader()..readAsDataUrl(files.first); | |
| await imageReader.onLoadEnd.first; | |
| imageUrl = imageReader.result; | |
| Timer.run(() => scan(true)); | |
| } | |
| else { | |
| imageUrl = ''; | |
| _reader.stopScanning(); | |
| Timer.run(() { | |
| if (MediaStream.supported) { | |
| scan(false); | |
| } | |
| }); | |
| } | |
| } | |
| void scan(bool forSelectedPhotos) { | |
| _reader.scan(forSelectedPhotos); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <video | |
| autoplay | |
| class="scan-video" | |
| [ngClass]="imageUrl == '' ? 'show' : ''" | |
| ></video> | |
| <img | |
| class="scan-image" | |
| [src]="imageUrl" | |
| [ngClass]="imageUrl != '' ? 'show' : ''" | |
| > | |
| <material-fab | |
| (trigger)="mediaCapture.click()" | |
| class="camera-button" | |
| > | |
| <material-icon icon="add_a_photo"></material-icon> | |
| </material-fab> | |
| <input | |
| #mediaCapture | |
| type="file" | |
| (change)="handleFileChange(mediaCapture.files)" | |
| > |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'dart:async'; | |
| import 'dart:html'; | |
| class QRReader { | |
| bool active = false; | |
| VideoElement videoElement; | |
| ImageElement imageElement; | |
| CanvasImageSource imageSource; | |
| CanvasElement canvas; | |
| CanvasRenderingContext2D ctx; | |
| Worker decorder; | |
| Timer timer; | |
| bool isIOS = false; | |
| Function callback; | |
| MediaStream videoStream; | |
| static bool noCameraPermission = false; | |
| QRReader({this.callback}) { | |
| isIOS = ['iPad', 'iPhone', 'iPod'].contains(window.navigator.platform); | |
| videoElement = window.document.querySelector('video.scan-video'); | |
| imageElement = window.document.querySelector('img.scan-image'); | |
| String baseurl = ''; | |
| decorder = Worker(baseurl + 'decoder.js'); | |
| decorder.addEventListener('message', onDecoderMessage); | |
| } | |
| void setCanvas() { | |
| canvas = window.document.createElement('canvas'); | |
| ctx = canvas.getContext('2d'); | |
| } | |
| void setPhotoSourceToScan(bool forSelectedPhotos) { | |
| if (!forSelectedPhotos && MediaStream.supported) { | |
| imageSource = videoElement; | |
| } | |
| else { | |
| imageSource = imageElement; | |
| } | |
| } | |
| void setCanvasProperties() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| void startCapture(dynamic constraints) { | |
| window.navigator | |
| .getUserMedia(audio: false, video: constraints) | |
| .then((stream) { | |
| print('get media success'); | |
| videoStream = stream; | |
| videoElement.srcObject = stream; | |
| videoElement.setAttribute('playsinline', 'true'); | |
| videoElement.setAttribute('controls', 'true'); | |
| Timer.run(() => videoElement.removeAttribute('controls')); | |
| }) | |
| .catchError((err) { | |
| print('Error occurred $err'); | |
| showErrorMsg(); | |
| }); | |
| } | |
| void showErrorMsg() { | |
| noCameraPermission = true; | |
| } | |
| void init() { | |
| bool streaming = false; | |
| setPhotoSourceToScan(false); | |
| setCanvas(); | |
| if (MediaStream.supported) { | |
| videoElement.addEventListener('play', (event) { | |
| if (!streaming) { | |
| setCanvasProperties(); | |
| streaming = true; | |
| } | |
| }, false); | |
| } | |
| else { | |
| setCanvasProperties(); | |
| } | |
| if (MediaStream.supported) { | |
| startCapture({'facingMode': 'environment'}); | |
| } | |
| } | |
| void scan(bool forSelectedPhotos) { | |
| active = true; | |
| setPhotoSourceToScan(forSelectedPhotos); | |
| newDecoderFrame(); | |
| } | |
| void onDecoderMessage(event) { | |
| if (event.data.length > 0) { | |
| var qrid = event.data[0][2]; | |
| if (active) { | |
| active = false; | |
| callback(qrid); | |
| } | |
| } | |
| else { | |
| Timer(Duration(milliseconds: 20), newDecoderFrame); | |
| } | |
| } | |
| void newDecoderFrame() { | |
| if (!active) return; | |
| try { | |
| ctx.drawImageScaled(imageSource, 0, 0, canvas.width, canvas.height); | |
| var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| if (imgData.data != null && imgData.data.isNotEmpty) { | |
| decorder.postMessage(imgData); | |
| } | |
| } catch (e) { | |
| print('error: $e'); | |
| } | |
| } | |
| void stopScanning() { | |
| if (videoStream == null) return; | |
| for (int i = 0; i < videoStream.getTracks().length; i++) { | |
| videoStream.getTracks()[i].stop(); | |
| } | |
| videoStream = null; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment