Skip to content

Instantly share code, notes, and snippets.

@yuanoook
Last active April 6, 2017 10:28
Show Gist options
  • Save yuanoook/69ef0d81d107c3d06d8d7c891b5fdef4 to your computer and use it in GitHub Desktop.
Save yuanoook/69ef0d81d107c3d06d8d7c891b5fdef4 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style>
.app__layout {
position: absolute;
width: 100%;
height: 100%;
-webkit-overflow-scrolling: touch;
overflow: hidden;
}
.app__layout-content {
height: inherit;
margin-top: 56px;
}
.custom-btn {
position: fixed;
right: 26px;
bottom: 26px;
background: #448aff;
border-radius: 50%;
border: none;
width: 56px;
height: 56px;
outline: none;
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
}
.custom-btn:active {
box-shadow: none;
}
.custom-msg {
text-align: center;
width: 90%;
height: 50%;
overflow: auto;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
font-size: 16px;
}
.custom-fab-icon {
color: #fff;
font-size: 30px;
margin-top: 2px;
user-select: none;
}
video {
transform: translateX(-50%) translateY(-50%);
top: 50%;
left: 50%;
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
position: absolute;
}
#list li {
list-style-type: none;
text-decoration: underline;
color: #00F;
}
.custom-copy-btn {
opacity: 0;
}
.hide {
display: none;
}
@-webkit-keyframes scanner {
0% {
bottom: 95%;
}
50% {
bottom: 5%;
}
100% {
bottom: 95%;
}
}
@-moz-keyframes scanner {
0% {
bottom: 90%;
}
50% {
bottom: 15%;
}
100% {
bottom: 90%;
}
}
@-o-keyframes scanner {
0% {
bottom: 90%;
}
50% {
bottom: 15%;
}
100% {
bottom: 90%;
}
}
@keyframes scanner {
0% {
bottom: 90%;
}
50% {
bottom: 15%;
}
100% {
bottom: 90%;
}
}
.custom-scanner {
width: 100%;
height: 2px;
background: #4CAF50;
position: absolute;
-webkit-transition: all 200ms linear;
-moz-transition: all 200ms linear;
transition: all 200ms linear;
-webkit-animation: scanner 3s infinite linear;
-moz-animation: scanner 3s infinite linear;
-o-animation: scanner 3s infinite linear;
animation: scanner 3s infinite linear;
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.4);
display: none;
}
#camera {
opacity: 0;
}
#frame {
width: 100%;
max-width: 100%;
}
.no-support {
font-size: 20px;
text-align: center;
}
.menu {
width: 280px;
height: 100%;
background: #fff;
position: fixed;
top: 0;
bottom: 0;
box-shadow: 0px 0px 11px 0px rgba(0, 0, 0, 0.4);
z-index: 1;
-webkit-transition: all 200ms cubic-bezier(0, 0, 0.30, 1);
transition: all 200ms cubic-bezier(0, 0, 0.30, 1);
-webkit-transform: translateX(-101%);
transform: translateX(-101%);
will-change: transform;
z-index: 11;
}
.app__snackbar {
position: fixed;
bottom: 20px;
left: 20px;
pointer-events: none;
}
.app__snackbar-msg {
width: 250px;
min-height: 50px;
background: rgba(0, 0, 0, 0.99);
color: #fff;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
font-size: 14px;
font-weight: 500;
padding-left: 15px;
padding-right: 10px;
word-break: break-all;
-webkit-transition: opacity 3s cubic-bezier(0, 0, 0.30, 1) 0;
transition: opacity 0.30s cubic-bezier(0, 0, 0.30, 1) 0;
text-transform: initial;
margin-bottom: 10px;
border-radius: 2px;
}
.app__snackbar--hide {
opacity: 0;
}
.app__dialog {
z-index: 12;
background-color: #fff;
width: 290px;
height: 180px;
border-radius: 2px;
display: flex;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
box-shadow: 0 9px 46px 8px rgba(0,0,0,.14), 0 11px 15px -7px rgba(0,0,0,.12), 0 24px 38px 3px rgba(0,0,0,.2);
}
.app__dialog h5 {
margin-top: 20px;
margin-left: 18px;
font-weight: 500;
}
.app__dialog input {
width: 250px;
margin: 20px;
height: 30px;
border: none;
border-bottom: 1px solid rgba(0,0,0,.12);
outline: none;
font-size: 15px;
margin-top: 25px;
color: rgba(0,0,0,.54);
font-weight: 500;
}
.app__dialog-actions {
display: block;
position: absolute;
bottom: 13px;
right: 20px;
}
.app__dialog-open,
.app__dialog-close {
border: 0;
height: 35px;
width: 70px;
font-size: 15px;
background: transparent;
font-weight: 500;
outline: none;
cursor: pointer;
}
.app__dialog-open {
display: none;
}
.app__dialog-open:active,
.app__dialog-close:active {
opacity: 0.9;
}
.app__dialog--hide {
display: none;
}</style></head>
<body>
<div class="app__layout">
<main class="app__layout-content">
<video autoplay></video>
<!-- Scanner animation -->
<div class="custom-scanner"></div>
<!-- Scan button -->
<button class="custom-btn">Scan</button>
<!-- Dialog -->
<div class="app__dialog app__dialog--hide">
<div class="app__dialog-content">
<h5>QR Code</h5>
<input type="text" id="result">
</div>
<div class="app__dialog-actions">
<button type="button" class="app__dialog-open">Open</button>
<button type="button" class="app__dialog-close">Close</button>
</div>
</div>
<!-- Snackbar -->
<div class="app__snackbar"></div>
</main>
</div>
<script type="text/javascript">
var snackbar = {};
var snackBarElement = document.querySelector('.app__snackbar');
var snackbarMsg = null;
//To show notification
snackbar.show = (msg, options=4000) => {
if (!msg) return;
if (snackbarMsg) {
snackbarMsg.remove();
}
snackbarMsg = document.createElement('div');
snackbarMsg.className = 'app__snackbar-msg';
snackbarMsg.textContent = msg;
snackBarElement.appendChild(snackbarMsg);
//Show toast for 3secs and hide it
setTimeout(() => {
snackbarMsg.remove();
}, options);
};
//------------------------------------------------------------------------------------------------------------------------------
var QRReader = {};
QRReader.active = false;
QRReader.webcam = null;
QRReader.canvas = null;
QRReader.ctx = null;
QRReader.decoder = null;
QRReader.setCanvas = () => {
QRReader.canvas = document.createElement("canvas");
QRReader.ctx = QRReader.canvas.getContext("2d");
}
QRReader.init = () => {
var decoder_worker_url = "//fly.yuanoook.com/file?hash=e69d4d973eea2229c7ba134d005f103d";
var streaming = false;
// Init Webcam + Canvas
if (!window.iOS) {
QRReader.webcam = document.querySelector("video");
}
else {
QRReader.webcam = document.querySelector("img");
}
QRReader.setCanvas();
QRReader.decoder = new Worker(decoder_worker_url);
if (!window.iOS) {
// Resize webcam according to input
QRReader.webcam.addEventListener("play", function (ev) {
if (!streaming) {
setCanvasProperties();
streaming = true;
}
}, false);
}
else {
setCanvasProperties();
}
function setCanvasProperties() {
QRReader.canvas.width = window.innerWidth;
QRReader.canvas.height = window.innerHeight;
}
function startCapture(constraints) {
navigator.mediaDevices.getUserMedia(constraints)
.then(function (stream) {
QRReader.webcam.srcObject = stream;
})
.catch(function(err) {
console.log("Error occurred ", err);
showErrorMsg();
});
}
if (!window.iOS) {
navigator.mediaDevices.enumerateDevices()
.then(function (devices) {
var device = devices.filter(function(device) {
var deviceLabel = device.label.split(',')[1];
if (device.kind == "videoinput") {
return device;
}
});
if (device.length > 1) {
var constraints = {
video: {
mandatory: {
sourceId: device[1].deviceId ? device[1].deviceId : null
}
},
audio: false
};
startCapture(constraints);
}
else if (device.length) {
var constraints = {
video: {
mandatory: {
sourceId: device[0].deviceId ? device[0].deviceId : null
}
},
audio: false
};
startCapture(constraints);
}
else {
startCapture({video:true});
}
})
.catch(function (error) {
showErrorMsg();
console.error("Error occurred : ", error);
});
}
function showErrorMsg() {
document.querySelector('.custom-btn').style.display = "none"; //Hide scan button, if error
snackbar.show('Unable to open the camera, provide permission to access the camera', 5000);
}
}
/**
* \brief QRReader Scan Action
* Call this to start scanning for QR codes.
*
* \param A function(scan_result)
*/
QRReader.scan = function (callback) {
QRReader.active = true
QRReader.setCanvas();
function onDecoderMessage(event) {
if (event.data.length > 0) {
var qrid = event.data[0][2];
QRReader.active = false
callback(qrid);
}
setTimeout(newDecoderFrame, 0);
}
QRReader.decoder.onmessage = onDecoderMessage;
// Start QR-decoder
function newDecoderFrame() {
if (!QRReader.active) return;
try {
QRReader.ctx.drawImage(QRReader.webcam, 0, 0,
QRReader.canvas.width, QRReader.canvas.height);
var imgData = QRReader.ctx.getImageData(0, 0, QRReader.canvas.width,
QRReader.canvas.height);
if (imgData.data) {
QRReader.decoder.postMessage(imgData);
}
} catch(e) {
// Try-Catch to circumvent Firefox Bug #879717
if (e.name == "NS_ERROR_NOT_AVAILABLE") setTimeout(newDecoderFrame, 0);
}
}
newDecoderFrame();
}
//------------------------------------------------------------------------------------------------------------------------------
function isURL(string){
return /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/.test(string);
}
//------------------------------------------------------------------------------------------------------------------------------
window.addEventListener("DOMContentLoaded", () => {
//To check the device and add iOS support
window.iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0;
var copiedText = null;
var scanBtnElement = document.querySelector('.custom-btn');
var dialogElement = document.querySelector('.app__dialog');
var dialogOpenBtnElement = document.querySelector('.app__dialog-open');
var dialogCloseBtnElement = document.querySelector('.app__dialog-close');
var scanningEle = document.querySelector('.custom-scanner');
var textBoxEle = document.querySelector('#result');
var frame = document.createElement('img');
frame.src = '';
frame.id = 'frame';
//Dialog close btn event
dialogCloseBtnElement.addEventListener('click', hideDialog, false);
dialogOpenBtnElement.addEventListener('click', openInBrowser, false);
//To open result in browser
function openInBrowser() {
console.log('Result: ', copiedText);
window.open(copiedText, '_blank', 'toolbar=0,location=0,menubar=0');
copiedText = null;
}
//Add scan funz to fab button, if its other iOS
if (!window.iOS) {
//Fab btn to scan
scanBtnElement.addEventListener('click', () => {
snackbar.show('Scanning please wait...', 2000);
scanningEle.style.display = 'block';
scan();
});
}
//Scan
function scan() {
QRReader.scan((result) => {
copiedText = result;
textBoxEle.value = result;
textBoxEle.select();
scanningEle.style.display = 'none';
if (isURL(result)) {
dialogOpenBtnElement.style.display = 'inline-block';
}
dialogElement.classList.remove('app__dialog--hide');
});
}
//Hide dialog
function hideDialog() {
copiedText = null;
textBoxEle.value = "";
frame.src = "";
dialogElement.classList.add('app__dialog--hide');
}
//If its iOS, then remove the video element and camera element.
if (window.iOS) {
document.querySelector('video').remove(); //removing the video element
//Creating the camera element
var camera = document.createElement('input');
camera.setAttribute('type', 'file');
camera.setAttribute('capture', 'camera');
camera.id = 'camera';
var iconElement = document.createElement('i');
iconElement.className = 'material-icons custom-fab-icon';
iconElement.textContent = 'camera_enhance';
var noSupportText = document.createElement('h2');
noSupportText.className = "no-support";
noSupportText.textContent = "Press the camera icon below."
//Add the camera and img element to DOM
var pageContentElement = document.querySelector('.app__layout-content');
var fabElement = document.querySelector('.custom-btn');
var fabIconElement = document.querySelector('.custom-fab-icon');
pageContentElement.appendChild(camera);
pageContentElement.appendChild(frame);
pageContentElement.appendChild(noSupportText);
fabElement.removeChild(fabIconElement);
fabElement.appendChild(iconElement);
//On camera change
camera.addEventListener('change', (event) => {
if (event.target && event.target.files.length > 0) {
frame.src = URL.createObjectURL(event.target.files[0]);
snackbar.show('Scanning please wait...', 2000);
scanningEle.style.display = 'block';
scan();
}
});
//Click of camera fab icon
fabElement.addEventListener('click', () => {
scanningEle.style.display = 'none';
document.querySelector("#camera").click();
});
}
});
//Initializing qr scanner
window.addEventListener('load', (event) => {
QRReader.init(); //To initialize QR Scanner
});
</script></body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment