Skip to content

Commit

Permalink
Update code structure, fix issue #5
Browse files Browse the repository at this point in the history
  • Loading branch information
dkozak committed Sep 3, 2019
1 parent bbd4ac2 commit 04fe449
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 196 deletions.
2 changes: 1 addition & 1 deletion app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ const qrCode = new QrCodeStyling({
}
});

qrCode.append("#canvas");
qrCode.append(document.getElementById("canvas"));
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.2",
"description": "Add a style and an image to your QR code",
"main": "lib/qr-code-styling.js",
"module": "src/index.js",
"module": "src/core/index.js",
"files": [
"lib"
],
Expand Down
137 changes: 137 additions & 0 deletions src/core/QRCanvas.js
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);
};
}
};
7 changes: 7 additions & 0 deletions src/core/QRCode.js
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);
}
};
72 changes: 72 additions & 0 deletions src/core/QRCodeStyling.js
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());
}
};
2 changes: 1 addition & 1 deletion src/dot.js → src/core/QRDot.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default class Dot {
export default class QRDot {
constructor({ context, type }) {
this.context = context;
this.type = type;
Expand Down
Loading

0 comments on commit 04fe449

Please sign in to comment.