qrcode-vue3
Version:
Add a style and an image to your QR code Vue3
339 lines (280 loc) • 9.63 kB
text/typescript
/* eslint-disable @typescript-eslint/unbound-method */
import dotTypes from "../constants/dotTypes";
import type { DotType } from "../types";
type GetNeighbor = (x: number, y: number) => boolean;
type DrawArgs = {
x: number;
y: number;
size: number;
context: CanvasRenderingContext2D;
getNeighbor: GetNeighbor;
};
type BasicFigureDrawArgs = {
x: number;
y: number;
size: number;
context: CanvasRenderingContext2D;
rotation: number;
};
type RotateFigureArgs = {
x: number;
y: number;
size: number;
context: CanvasRenderingContext2D;
rotation: number;
draw: () => void;
};
export default class QRDot {
_context: CanvasRenderingContext2D;
_type: DotType;
constructor({ context, type }: { context: CanvasRenderingContext2D; type: DotType }) {
this._context = context;
this._type = type;
}
draw(x: number, y: number, size: number, getNeighbor: GetNeighbor): void {
const context = this._context;
const type = this._type;
let drawFunction;
switch (type) {
case dotTypes.dots:
drawFunction = this._drawDot;
break;
case dotTypes.classy:
drawFunction = this._drawClassy;
break;
case dotTypes.classyRounded:
drawFunction = this._drawClassyRounded;
break;
case dotTypes.rounded:
drawFunction = this._drawRounded;
break;
case dotTypes.extraRounded:
drawFunction = this._drawExtraRounded;
break;
case dotTypes.square:
default:
drawFunction = this._drawSquare;
}
drawFunction.call(this, { x, y, size, context, getNeighbor });
}
_rotateFigure({ x, y, size, context, rotation, draw }: RotateFigureArgs): void {
const cx = x + size / 2;
const cy = y + size / 2;
context.translate(cx, cy);
rotation && context.rotate(rotation);
draw();
context.closePath();
rotation && context.rotate(-rotation);
context.translate(-cx, -cy);
}
_basicDot(args: BasicFigureDrawArgs): void {
const { size, context } = args;
this._rotateFigure({
...args,
draw: () => {
context.moveTo(0, 0)
context.arc(0, 0, size / 2, 0, Math.PI * 2);
}
});
}
_basicSquare(args: BasicFigureDrawArgs): void {
const { size, context } = args;
this._rotateFigure({
...args,
draw: () => {
context.moveTo(0, 0)
context.rect(-size / 2, -size / 2, size, size);
}
});
}
// if rotation === 0 - right side is rounded
_basicSideRounded(args: BasicFigureDrawArgs): void {
const { size, context } = args;
this._rotateFigure({
...args,
draw: () => {
context.moveTo(0, 0)
context.arc(0, 0, size / 2, -Math.PI / 2, Math.PI / 2);
context.lineTo(-size / 2, size / 2);
context.lineTo(-size / 2, -size / 2);
context.lineTo(0, -size / 2);
}
});
}
// if rotation === 0 - top right corner is rounded
_basicCornerRounded(args: BasicFigureDrawArgs): void {
const { size, context } = args;
this._rotateFigure({
...args,
draw: () => {
context.moveTo(0, 0)
context.arc(0, 0, size / 2, -Math.PI / 2, 0);
context.lineTo(size / 2, size / 2);
context.lineTo(-size / 2, size / 2);
context.lineTo(-size / 2, -size / 2);
context.lineTo(0, -size / 2);
}
});
}
// if rotation === 0 - top right corner is rounded
_basicCornerExtraRounded(args: BasicFigureDrawArgs): void {
const { size, context } = args;
this._rotateFigure({
...args,
draw: () => {
context.moveTo(0, 0)
context.arc(-size / 2, size / 2, size, -Math.PI / 2, 0);
context.lineTo(-size / 2, size / 2);
context.lineTo(-size / 2, -size / 2);
}
});
}
_basicCornersRounded(args: BasicFigureDrawArgs): void {
const { size, context } = args;
this._rotateFigure({
...args,
draw: () => {
context.moveTo(0, 0)
context.arc(0, 0, size / 2, -Math.PI / 2, 0);
context.lineTo(size / 2, size / 2);
context.lineTo(0, size / 2);
context.arc(0, 0, size / 2, Math.PI / 2, Math.PI);
context.lineTo(-size / 2, -size / 2);
context.lineTo(0, -size / 2);
}
});
}
_basicCornersExtraRounded(args: BasicFigureDrawArgs): void {
const { size, context } = args;
this._rotateFigure({
...args,
draw: () => {
context.moveTo(0, 0)
context.arc(-size / 2, size / 2, size, -Math.PI / 2, 0);
context.arc(size / 2, -size / 2, size, Math.PI / 2, Math.PI);
}
});
}
_drawDot({ x, y, size, context }: DrawArgs): void {
this._basicDot({ x, y, size, context, rotation: 0 });
}
_drawSquare({ x, y, size, context }: DrawArgs): void {
this._basicSquare({ x, y, size, context, rotation: 0 });
}
_drawRounded({ x, y, size, context, getNeighbor }: DrawArgs): void {
const leftNeighbor = +getNeighbor(-1, 0);
const rightNeighbor = +getNeighbor(1, 0);
const topNeighbor = +getNeighbor(0, -1);
const bottomNeighbor = +getNeighbor(0, 1);
const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
if (neighborsCount === 0) {
this._basicDot({ x, y, size, context, rotation: 0 });
return;
}
if (neighborsCount > 2 || (leftNeighbor && rightNeighbor) || (topNeighbor && bottomNeighbor)) {
this._basicSquare({ x, y, size, context, rotation: 0 });
return;
}
if (neighborsCount === 2) {
let rotation = 0;
if (leftNeighbor && topNeighbor) {
rotation = Math.PI / 2;
} else if (topNeighbor && rightNeighbor) {
rotation = Math.PI;
} else if (rightNeighbor && bottomNeighbor) {
rotation = -Math.PI / 2;
}
this._basicCornerRounded({ x, y, size, context, rotation });
return;
}
if (neighborsCount === 1) {
let rotation = 0;
if (topNeighbor) {
rotation = Math.PI / 2;
} else if (rightNeighbor) {
rotation = Math.PI;
} else if (bottomNeighbor) {
rotation = -Math.PI / 2;
}
this._basicSideRounded({ x, y, size, context, rotation });
}
}
_drawExtraRounded({ x, y, size, context, getNeighbor }: DrawArgs): void {
const leftNeighbor = +getNeighbor(-1, 0);
const rightNeighbor = +getNeighbor(1, 0);
const topNeighbor = +getNeighbor(0, -1);
const bottomNeighbor = +getNeighbor(0, 1);
const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
if (neighborsCount === 0) {
this._basicDot({ x, y, size, context, rotation: 0 });
return;
}
if (neighborsCount > 2 || (leftNeighbor && rightNeighbor) || (topNeighbor && bottomNeighbor)) {
this._basicSquare({ x, y, size, context, rotation: 0 });
return;
}
if (neighborsCount === 2) {
let rotation = 0;
if (leftNeighbor && topNeighbor) {
rotation = Math.PI / 2;
} else if (topNeighbor && rightNeighbor) {
rotation = Math.PI;
} else if (rightNeighbor && bottomNeighbor) {
rotation = -Math.PI / 2;
}
this._basicCornerExtraRounded({ x, y, size, context, rotation });
return;
}
if (neighborsCount === 1) {
let rotation = 0;
if (topNeighbor) {
rotation = Math.PI / 2;
} else if (rightNeighbor) {
rotation = Math.PI;
} else if (bottomNeighbor) {
rotation = -Math.PI / 2;
}
this._basicSideRounded({ x, y, size, context, rotation });
}
}
_drawClassy({ x, y, size, context, getNeighbor }: DrawArgs): void {
const leftNeighbor = +getNeighbor(-1, 0);
const rightNeighbor = +getNeighbor(1, 0);
const topNeighbor = +getNeighbor(0, -1);
const bottomNeighbor = +getNeighbor(0, 1);
const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
if (neighborsCount === 0) {
this._basicCornersRounded({ x, y, size, context, rotation: Math.PI / 2 });
return;
}
if (!leftNeighbor && !topNeighbor) {
this._basicCornerRounded({ x, y, size, context, rotation: -Math.PI / 2 });
return;
}
if (!rightNeighbor && !bottomNeighbor) {
this._basicCornerRounded({ x, y, size, context, rotation: Math.PI / 2 });
return;
}
this._basicSquare({ x, y, size, context, rotation: 0 });
}
_drawClassyRounded({ x, y, size, context, getNeighbor }: DrawArgs): void {
const leftNeighbor = +getNeighbor(-1, 0);
const rightNeighbor = +getNeighbor(1, 0);
const topNeighbor = +getNeighbor(0, -1);
const bottomNeighbor = +getNeighbor(0, 1);
const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
if (neighborsCount === 0) {
this._basicCornersRounded({ x, y, size, context, rotation: Math.PI / 2 });
return;
}
if (!leftNeighbor && !topNeighbor) {
this._basicCornerExtraRounded({ x, y, size, context, rotation: -Math.PI / 2 });
return;
}
if (!rightNeighbor && !bottomNeighbor) {
this._basicCornerExtraRounded({ x, y, size, context, rotation: Math.PI / 2 });
return;
}
this._basicSquare({ x, y, size, context, rotation: 0 });
}
}