-
-
Notifications
You must be signed in to change notification settings - Fork 517
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
dkozak
committed
Sep 3, 2019
1 parent
bbd4ac2
commit 04fe449
Showing
9 changed files
with
222 additions
and
196 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import calculateImageSize from "../tools/calculateImageSize"; | ||
import errorCorrectionPercents from "../constants/errorCorrectionPercents"; | ||
import QRDot from "./QRDot"; | ||
|
||
export default class QRCanvas { | ||
constructor(options) { | ||
this.canvas = document.createElement("canvas"); | ||
this.canvas.width = options.width; | ||
this.canvas.height = options.height; | ||
this.options = options; | ||
} | ||
|
||
get context () { | ||
return this.canvas.getContext("2d"); | ||
} | ||
|
||
get width () { | ||
return this.canvas.width; | ||
} | ||
|
||
get height () { | ||
return this.canvas.height; | ||
} | ||
|
||
getCanvas () { | ||
return this.canvas; | ||
} | ||
|
||
clear () { | ||
const canvasContext = this.context; | ||
|
||
canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); | ||
} | ||
|
||
drawQR(qr) { | ||
this.clear(); | ||
this.drawBackground(); | ||
this.qr = qr; | ||
|
||
if (this.options.image) { | ||
this.drawImageAndDots(); | ||
} else { | ||
this.drawDots(); | ||
} | ||
} | ||
|
||
drawBackground() { | ||
const canvasContext = this.context; | ||
const options = this.options; | ||
|
||
canvasContext.fillStyle = options.backgroundOptions.colour; | ||
canvasContext.fillRect(0, 0, this.canvas.width, this.canvas.height); | ||
} | ||
|
||
|
||
drawDots(filter) { | ||
const canvasContext = this.context; | ||
const options = this.options; | ||
const count = this.qr.getModuleCount(); | ||
const minSize = Math.min(options.width, options.height); | ||
const dotSize = Math.floor(minSize / count); | ||
const xBeginning = Math.floor((options.width - count * dotSize) / 2); | ||
const yBeginning = Math.floor((options.height - count * dotSize) / 2); | ||
|
||
if (count > options.width || count > options.height) { | ||
throw "The canvas is too small."; | ||
} | ||
|
||
const dot = new QRDot({ context: canvasContext, type: options.dotsOptions.type }); | ||
|
||
for(let i = 0; i < count; i++) { | ||
for(let j = 0; j < count; j++) { | ||
if (filter && !filter(i, j)) { | ||
continue; | ||
} | ||
|
||
if (this.qr.isDark(i, j)) { | ||
canvasContext.fillStyle = options.dotsOptions.colour; | ||
dot.draw(xBeginning + i * dotSize, yBeginning + j * dotSize, dotSize, (xOffset, yOffset) => { | ||
if (i + xOffset >= 0 && j + yOffset >= 0 && i + xOffset < count && j + yOffset < count) { | ||
if (filter && !filter(i + xOffset, j + yOffset)) return false; | ||
return this.qr.isDark(i + xOffset, j + yOffset); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
|
||
drawImageAndDots() { | ||
const canvasContext = this.context; | ||
const options = this.options; | ||
const count = this.qr.getModuleCount(); | ||
const minSize = Math.min(options.width, options.height); | ||
const dotSize = Math.floor(minSize / count); | ||
const xBeginning = Math.floor((options.width - count * dotSize) / 2); | ||
const yBeginning = Math.floor((options.height - count * dotSize) / 2); | ||
|
||
const image = new Image(); | ||
const coverLevel = options.imageOptions.imageSize * errorCorrectionPercents[options.qrOptions.errorCorrectionLevel]; | ||
|
||
image.src = options.image; | ||
image.onload = () => { | ||
const maxHiddenDots = Math.floor(coverLevel * count * count); | ||
const { | ||
resizedImageWidth, | ||
resizedImageHeight, | ||
hiddenDotsWidth, | ||
hiddenDotsHeight | ||
} = calculateImageSize({ | ||
originalWidth: image.width, | ||
originalHeight: image.height, | ||
maxHiddenDots, | ||
dotSize | ||
}); | ||
|
||
this.drawDots((i, j) => { | ||
if (!options.imageOptions.hideBackgroundDots) { | ||
return true; | ||
} | ||
return ( | ||
i < (count - hiddenDotsWidth) / 2 | ||
|| i >= (count + hiddenDotsWidth) / 2 | ||
|| j < (count - hiddenDotsHeight) / 2 | ||
|| j >= (count + hiddenDotsHeight) / 2 | ||
) | ||
}); | ||
|
||
canvasContext.drawImage( | ||
image, | ||
xBeginning + (count * dotSize - resizedImageWidth) / 2, | ||
yBeginning + (count * dotSize - resizedImageHeight) / 2, | ||
resizedImageWidth, | ||
resizedImageHeight); | ||
}; | ||
} | ||
}; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import qrcode from "qrcode-generator"; | ||
|
||
export default class QRCode extends qrcode { | ||
constructor(options) { | ||
super(options.qrOptions.typeNumber, options.qrOptions.errorCorrectionLevel); | ||
} | ||
}; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import getMode from "../tools/getMode"; | ||
import mergeDeep from "../tools/merge"; | ||
import errorCorrectLevels from "../constants/errorCorrectLevels"; | ||
import types from "../constants/types"; | ||
import QRCode from "./QRCode"; | ||
import QRCanvas from "./QRCanvas"; | ||
|
||
const defaultOptions = { | ||
width: 300, | ||
height: 300, | ||
data: undefined, | ||
image: undefined, | ||
qrOptions: { | ||
typeNumber: types[0], | ||
mode: undefined, | ||
errorCorrectionLevel: errorCorrectLevels.Q, | ||
}, | ||
imageOptions: { | ||
hideBackgroundDots: true, | ||
imageSize: 0.4 | ||
}, | ||
dotsOptions: { | ||
type: "square", | ||
colour: "#000", | ||
}, | ||
backgroundOptions: { | ||
colour: "#fff", | ||
} | ||
}; | ||
|
||
export default class QRCodeStyling { | ||
constructor(options) { | ||
this.options = mergeDeep(defaultOptions, options); | ||
this.update(); | ||
} | ||
|
||
update(options) { | ||
this.clearContainer(this.container); | ||
this.options = mergeDeep(this.options, options); | ||
|
||
if (!this.options.data) { | ||
return; | ||
} | ||
|
||
this.qr = new QRCode(this.options); | ||
this.qr.addData(this.options.data, this.options.qrOptions.mode || getMode(this.options.data)); | ||
this.qr.make(); | ||
this.canvas = new QRCanvas(this.options); | ||
this.canvas.drawQR(this.qr); | ||
this.append(this.container); | ||
} | ||
|
||
clearContainer(container) { | ||
if (container) { | ||
container.innerHTML = ""; | ||
} | ||
} | ||
|
||
append(container) { | ||
if (!container) { | ||
return; | ||
} | ||
|
||
if (typeof container.appendChild !== "function") { | ||
throw "Container should be a single DOM node"; | ||
} | ||
|
||
this.container = container; | ||
|
||
container.appendChild(this.canvas.getCanvas()); | ||
} | ||
}; |
This file contains 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
Oops, something went wrong.