Skip to content

Instantly share code, notes, and snippets.

@danielsmykowski1
Created November 2, 2019 12:40
Show Gist options
  • Select an option

  • Save danielsmykowski1/89109403096188692748bee7f17d84d6 to your computer and use it in GitHub Desktop.

Select an option

Save danielsmykowski1/89109403096188692748bee7f17d84d6 to your computer and use it in GitHub Desktop.
AngularDart Qrcode scan component
: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;
}
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);
}
}
<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)"
>
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