Skip to content

Commit

Permalink
New feature: select region to scan
Browse files Browse the repository at this point in the history
  • Loading branch information
monyxie committed Apr 10, 2021
1 parent 61f9e5e commit 2080666
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 8 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"style-loader": "^2.0.0"
},
"scripts": {
"dev": "yarn run webpack watch --mode development",
"dev": "webpack watch --mode development",
"lint": "eslint src",
"release": "./release.sh"
}
}
6 changes: 6 additions & 0 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
"context_menu_scan_qr_code_in_image": {
"message": "Scan QR Code In This Image"
},
"context_menu_pick_region_to_scan": {
"message": "Select a region to scan..."
},
"scan_region_picker_tips_html": {
"message": "To scan, draw a rectangle to capture an image of the QR code,\n<br>Then right click on the image and choose \"Scan QR code in this image\". <br>Press &lt;Esc&gt; to cancel."
},
"decoding": {
"message": "Decoding..."
},
Expand Down
6 changes: 6 additions & 0 deletions src/_locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
"context_menu_scan_qr_code_in_image": {
"message": "扫描该图片中的二维码"
},
"context_menu_pick_region_to_scan": {
"message": "选择区域进行扫描..."
},
"scan_region_picker_tips_html": {
"message": "按住鼠标左键并拖拽来截取二维码图片,\n<br>然后在图片上右击并选择“扫描该图片中的二维码”。 <br>按 &lt;Esc&gt; 键可以取消。"
},
"decoding": {
"message": "正在扫码..."
},
Expand Down
23 changes: 23 additions & 0 deletions src/background/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,27 @@
openPopup({ action: 'ACTION_DECODE', image: info.srcUrl })
}
})

// manually pick region to scan
browser.contextMenus.create({
title: browser.i18n.getMessage('context_menu_pick_region_to_scan'),
contexts: ['page', 'browser_action'],
onclick: function decodeQR (info, tab) {
browser.tabs.executeScript({
file: '../content_scripts/scan_region_picker.js'
})
}
})

// image capturing
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
switch (request.action) {
case 'ACTION_CAPTURE':
return browser.tabs.captureVisibleTab({
rect: request.rect
}).then((dataUri) => {
return { dataUri: dataUri }
})
}
})
})(window.browser)
171 changes: 171 additions & 0 deletions src/content_scripts/scan_region_picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
class Picker {
constructor (browser) {
this.browser = browser
this.x1 = this.x2 = this.y1 = this.y2 = null
}

init () {
const domMask = document.createElement('div')
const domTips = document.createElement('p')
domMask.innerHTML = '<div class="qr-lite-mask-top"></div>' +
'<div class="qr-lite-mask-middle" style="margin:0;width:100%;display:flex;justify-content:space-between;user-select:none;">' +
'<div class="qr-lite-mask-left" style="margin:0;height:100%;float:left;user-select:none;"></div>' +
'<div class="qr-lite-rect" style="margin:0;height:100%;float:left;user-select:none;"></div>' +
'<div class="qr-lite-mask-right" style="margin:0;height:100%;user-select:none;"></div>' +
'</div>' +
'<div class="qr-lite-mask-bottom" style="margin:0;width:100%;user-select:none;"></div>'

this.domMask = domMask
this.domRect = domMask.getElementsByClassName('qr-lite-rect')[0]
this.domMaskTop = domMask.getElementsByClassName('qr-lite-mask-top')[0]
this.domMaskMiddle = domMask.getElementsByClassName('qr-lite-mask-middle')[0]
this.domMaskLeft = domMask.getElementsByClassName('qr-lite-mask-left')[0]
this.domMaskRight = domMask.getElementsByClassName('qr-lite-mask-right')[0]
this.domMaskMiddle = domMask.getElementsByClassName('qr-lite-mask-middle')[0]
this.domMaskBottom = domMask.getElementsByClassName('qr-lite-mask-bottom')[0]
this.keyupHandler = (event) => {
if (event.key === 'Escape') {
event.preventDefault()
event.stopPropagation()
this.hide()
}
}

this.domMask.style.position = 'fixed'
this.domMask.style.top = '0px'
this.domMask.style.left = '0px'
this.domMask.style.zIndex = '9999'
this.domMask.style.width = '100%'
this.domMask.style.height = '100%'

domTips.style.position = 'fixed'
domTips.style.left = '0px'
domTips.style.top = '0px'
domTips.style.color = 'white'
domTips.style.backgroundColor = 'rgba(0,0,0,0.75)'
domTips.style.zIndex = '9998'
domTips.style.border = '1px solid white'
domTips.style.userSelect = 'none'
domTips.style.fontSize = '14px'
domTips.style.fontFamily = 'Sans Serif'
domTips.innerHTML = this.browser.i18n.getMessage('scan_region_picker_tips_html')
this.domMaskTop.style.backgroundColor = 'rgba(0,0,0,0.5)'
this.domMaskTop.style.height = '100%'

this.domMask.appendChild(domTips)

this.domMask.addEventListener('mousedown', event => {
this.isMouseDown = true
this.startX = event.clientX
this.startY = event.clientY
})
this.domMask.addEventListener('mouseup', event => {
this.isMouseDown = false
if (this.isDragged) {
this.isDragged = false
this.updateSelection()

const rect = {
x: document.documentElement.scrollLeft + this.x1,
y: document.documentElement.scrollTop + this.y1,
width: this.x2 - this.x1,
height: this.y2 - this.y1
}
// console.log('capturing image: ', rect)
this.browser.runtime.sendMessage({
action: 'ACTION_CAPTURE',
rect: rect
}).then(response => {
const image = document.createElement('img')
image.src = response.dataUri
image.style.display = 'block'
image.style.margin = '0'
image.style.padding = '0'
image.style.position = 'static'
image.style.top = '0'
image.style.left = '0'
image.style.width = rect.width + 'px'
image.style.height = rect.height + 'px'
this.domRect.appendChild(image)
}).catch(e => {
console.log('image capture failed: ', e)
})
}
})

this.domMask.addEventListener('mousemove', event => {
domTips.style.top = event.clientY + 'px'
domTips.style.left = (event.clientX + 20) + 'px'
if (this.isMouseDown) {
this.domRect.innerHTML = ''
this.isDragged = true
this.currentX = event.clientX
this.currentY = event.clientY
this.updateSelection()
}
})

document.addEventListener('keyup', this.keyupHandler)
}

show () {
if (!this.isShown) {
this.isShown = true
document.body.style.overflow = 'hidden'
document.body.appendChild(this.domMask)
}
}

updateSelection () {
if (this.startX < this.currentX) {
this.x1 = this.startX
this.x2 = this.currentX
} else {
this.x2 = this.startX
this.x1 = this.currentX
}

if (this.startY < this.currentY) {
this.y1 = this.startY
this.y2 = this.currentY
} else {
this.y2 = this.startY
this.y1 = this.currentY
}

// this.domMask.style.width = window.innerWidth + 'px'
// this.domMask.style.height = window.innerHeight + 'px'

this.domMaskTop.style.backgroundColor =
this.domMaskLeft.style.backgroundColor =
this.domMaskRight.style.backgroundColor =
this.domMaskBottom.style.backgroundColor = 'rgba(0,0,0,0.5)'
this.domRect.style.backgroundColor = 'transparent'
this.domMaskTop.style.height = this.y1 + 'px'
this.domMaskMiddle.style.height = (this.y2 - this.y1) + 'px'
this.domMaskBottom.style.height = (window.innerHeight - this.y2) + 'px'
this.domMaskLeft.style.width = this.x1 + 'px'
this.domRect.style.width = (this.x2 - this.x1) + 'px'
this.domMaskRight.style.width = (window.innerWidth - this.x2) + 'px'
}

hide () {
if (this.isShown) {
this.isShown = false
this.domRect.style.backgroundColor = 'rgba(0,0,0,0.5)'
this.currentX = this.currentY = this.x1 = this.x2 = this.y1 = this.y2 = null
this.domRect.innerHTML = ''
this.domMask.parentElement.removeChild(this.domMask)
document.body.style.overflow = ''
document.removeEventListener('keydown', this.keyupHandler)
}
}
}

if (!window._qrLite_RegionPicker) {
// eslint-disable-next-line no-undef
window._qrLite_RegionPicker = new Picker(browser)
window._qrLite_RegionPicker.init()
}

window._qrLite_RegionPicker.show()
13 changes: 7 additions & 6 deletions src/popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ class Popup {
this.domSource.placeholder = this.browser.i18n.getMessage('decoding')

const that = this

that.domResult.innerText = ''
const img = document.createElement('img')
img.classList.add('decoded-image')
img.src = url
that.domResult.appendChild(img)

return codeReader.decodeFromImageUrl(url).then(function (result) {
const text = result.getText()
const points = result.getResultPoints()
Expand All @@ -134,12 +141,6 @@ class Popup {
}
}

that.domResult.innerText = ''
const img = document.createElement('img')
img.classList.add('decoded-image')
img.src = url
that.domResult.appendChild(img)

for (let i = 0; i < points.length; i++) {
that.createPointMarkerElement(points[i], that.domResult, img)
}
Expand Down
3 changes: 2 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module.exports = {
devtool: false,
entry: {
popup: { import: './src/popup/popup.js', filename: 'popup/popup.js' },
background: { import: './src/background/background.js', filename: 'background/background.js' }
background: { import: './src/background/background.js', filename: 'background/background.js' },
scan_region_picker: { import: './src/content_scripts/scan_region_picker.js', filename: 'content_scripts/scan_region_picker.js' }
},
output: {
filename: '[name].js',
Expand Down

0 comments on commit 2080666

Please sign in to comment.