kopia lustrzana https://github.com/zhengkyl/qrframe
marginless
rodzic
d2efe998f1
commit
46fc279f58
|
@ -19,7 +19,7 @@
|
||||||
"@solidjs/start": "^1.0.0",
|
"@solidjs/start": "^1.0.0",
|
||||||
"@unocss/reset": "^0.59.4",
|
"@unocss/reset": "^0.59.4",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"fuqr": "^0.0.5",
|
"fuqr": "^0.0.6",
|
||||||
"lucide-solid": "^0.378.0",
|
"lucide-solid": "^0.378.0",
|
||||||
"qr-scanner-wechat": "^0.1.3",
|
"qr-scanner-wechat": "^0.1.3",
|
||||||
"solid-js": "^1.8.17",
|
"solid-js": "^1.8.17",
|
||||||
|
|
|
@ -49,8 +49,8 @@ importers:
|
||||||
specifier: ^6.0.1
|
specifier: ^6.0.1
|
||||||
version: 6.0.1(@lezer/common@1.2.1)
|
version: 6.0.1(@lezer/common@1.2.1)
|
||||||
fuqr:
|
fuqr:
|
||||||
specifier: ^0.0.5
|
specifier: ^0.0.6
|
||||||
version: 0.0.5
|
version: 0.0.6
|
||||||
lucide-solid:
|
lucide-solid:
|
||||||
specifier: ^0.378.0
|
specifier: ^0.378.0
|
||||||
version: 0.378.0(solid-js@1.8.17)
|
version: 0.378.0(solid-js@1.8.17)
|
||||||
|
@ -1781,8 +1781,8 @@ packages:
|
||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
fuqr@0.0.5:
|
fuqr@0.0.6:
|
||||||
resolution: {integrity: sha512-KxIKlDZ4HQFTouhH8VuDuA6EdPZbLy2GklevRP2KRJ7FiBlh/fET+26IizsWIpkwfpNWb0QkDs9rdL8Nn6PbkA==}
|
resolution: {integrity: sha512-pYq22vaBSzVEJJ9lkh11kFMdDtv++saB09I/dGSB5gL3GUTVrV0M1l5FDiSIjLisylrUOKsBXuSfH70a3desjg==}
|
||||||
|
|
||||||
gauge@3.0.2:
|
gauge@3.0.2:
|
||||||
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
||||||
|
@ -4790,7 +4790,7 @@ snapshots:
|
||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
fuqr@0.0.5: {}
|
fuqr@0.0.6: {}
|
||||||
|
|
||||||
gauge@3.0.2:
|
gauge@3.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -159,7 +159,7 @@ export function Editor(props: Props) {
|
||||||
placeholder="https://qrcode.kylezhe.ng"
|
placeholder="https://qrcode.kylezhe.ng"
|
||||||
setValue={(s) => setInputQr("text", s)}
|
setValue={(s) => setInputQr("text", s)}
|
||||||
/>
|
/>
|
||||||
<Collapsible trigger="Settings">
|
<Collapsible trigger="QR Code">
|
||||||
<Settings />
|
<Settings />
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
<Collapsible trigger="Rendering" defaultOpen>
|
<Collapsible trigger="Rendering" defaultOpen>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { For, type JSX } from "solid-js";
|
import { For } from "solid-js";
|
||||||
import { useQrContext } from "~/lib/QrContext";
|
import { useQrContext } from "~/lib/QrContext";
|
||||||
import {
|
import {
|
||||||
ECL_NAMES,
|
ECL_NAMES,
|
||||||
|
@ -18,7 +18,7 @@ export function Settings() {
|
||||||
const { inputQr, setInputQr } = useQrContext();
|
const { inputQr, setInputQr } = useQrContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div class="flex flex-col gap-2 py-2">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div class="text-sm py-2">Encoding mode</div>
|
<div class="text-sm py-2">Encoding mode</div>
|
||||||
<Select
|
<Select
|
||||||
|
@ -27,15 +27,17 @@ export function Settings() {
|
||||||
setValue={(name) => setInputQr("mode", MODE_VALUE[name])}
|
setValue={(name) => setInputQr("mode", MODE_VALUE[name])}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Row title="Min version">
|
<div class="flex justify-between">
|
||||||
|
<div class="text-sm py-2 w-48">Min version</div>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
min={1}
|
min={1}
|
||||||
max={40}
|
max={40}
|
||||||
value={inputQr.minVersion}
|
value={inputQr.minVersion}
|
||||||
setValue={(v) => setInputQr("minVersion", v)}
|
setValue={(v) => setInputQr("minVersion", v)}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</div>
|
||||||
<Row title="Min error tolerance">
|
<div>
|
||||||
|
<div class="text-sm py-2">Min error tolerance</div>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
value={ECL_NAMES[inputQr.minEcl]}
|
value={ECL_NAMES[inputQr.minEcl]}
|
||||||
setValue={(v) => setInputQr("minEcl", ECL_VALUE[v])}
|
setValue={(v) => setInputQr("minEcl", ECL_VALUE[v])}
|
||||||
|
@ -44,8 +46,9 @@ export function Settings() {
|
||||||
{(name) => <ButtonGroupItem value={name}>{name}</ButtonGroupItem>}
|
{(name) => <ButtonGroupItem value={name}>{name}</ButtonGroupItem>}
|
||||||
</For>
|
</For>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Row>
|
</div>
|
||||||
<Row title="Mask pattern">
|
<div>
|
||||||
|
<div class="text-sm py-2">Mask pattern</div>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
value={MASK_KEY[inputQr.mask!]}
|
value={MASK_KEY[inputQr.mask!]}
|
||||||
setValue={(name) => setInputQr("mask", MASK_VALUE[name])}
|
setValue={(name) => setInputQr("mask", MASK_VALUE[name])}
|
||||||
|
@ -56,32 +59,7 @@ export function Settings() {
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Row>
|
|
||||||
<Row title="Margin">
|
|
||||||
<NumberInput
|
|
||||||
min={0}
|
|
||||||
max={10}
|
|
||||||
step={1}
|
|
||||||
value={inputQr.margin.top}
|
|
||||||
setValue={(v) =>
|
|
||||||
setInputQr("margin", { top: v, bottom: v, left: v, right: v })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Row(props: {
|
|
||||||
title: string;
|
|
||||||
children: JSX.Element;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div class="text-sm py-2">
|
|
||||||
{props.title}
|
|
||||||
</div>
|
</div>
|
||||||
{props.children}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,18 +69,14 @@ export default function QrPreview(props: Props) {
|
||||||
* Running the effect in the ref function caused double rendering for future mounts.
|
* Running the effect in the ref function caused double rendering for future mounts.
|
||||||
*/
|
*/
|
||||||
function RenderedQrCode() {
|
function RenderedQrCode() {
|
||||||
const { outputQr: _outputQr, renderFunc, renderFuncKey, params } = useQrContext();
|
const {
|
||||||
|
outputQr: _outputQr,
|
||||||
|
renderFunc,
|
||||||
|
renderFuncKey,
|
||||||
|
params,
|
||||||
|
} = useQrContext();
|
||||||
const outputQr = _outputQr as () => OutputQr;
|
const outputQr = _outputQr as () => OutputQr;
|
||||||
|
|
||||||
const fullWidth = () => {
|
|
||||||
const output = outputQr();
|
|
||||||
return output.version * 4 + 17 + output.margin.left + output.margin.right;
|
|
||||||
};
|
|
||||||
const fullHeight = () => {
|
|
||||||
const output = outputQr();
|
|
||||||
return output.version * 4 + 17 + output.margin.top + output.margin.bottom;
|
|
||||||
};
|
|
||||||
|
|
||||||
let qrCanvas: HTMLCanvasElement;
|
let qrCanvas: HTMLCanvasElement;
|
||||||
|
|
||||||
const [runtimeError, setRuntimeError] = createSignal<string | null>(null);
|
const [runtimeError, setRuntimeError] = createSignal<string | null>(null);
|
||||||
|
@ -132,9 +128,10 @@ function RenderedQrCode() {
|
||||||
"background-image":
|
"background-image":
|
||||||
"repeating-conic-gradient(#ddd 0% 25%, #aaa 25% 50%)",
|
"repeating-conic-gradient(#ddd 0% 25%, #aaa 25% 50%)",
|
||||||
"background-position": "50%",
|
"background-position": "50%",
|
||||||
"background-size": `${(1 / fullWidth()) * 100}% ${
|
"background-size": `${
|
||||||
(1 / fullHeight()) * 100
|
(1 / (outputQr().version * 4 + 17 + 4)) * 100
|
||||||
}%`,
|
}% ${(1 / (outputQr().version * 4 + 17 + 4)) * 100}%
|
||||||
|
`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<canvas class="w-full h-full" ref={qrCanvas!}></canvas>
|
<canvas class="w-full h-full" ref={qrCanvas!}></canvas>
|
||||||
|
@ -153,9 +150,19 @@ function RenderedQrCode() {
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
{canvasDims().width}x{canvasDims().height} px
|
{canvasDims().width}x{canvasDims().height} px
|
||||||
</div>
|
</div>
|
||||||
<div class="px-2 grid grid-cols-3 gap-y-2 text-sm">
|
<div class="px-2 grid grid-cols-2 gap-y-2 text-sm">
|
||||||
<div class="">
|
<div class="">
|
||||||
Version <span class="font-bold text-base">{outputQr().version}</span>
|
Version
|
||||||
|
<div class="font-bold text-base">
|
||||||
|
{outputQr().version} ({outputQr().version * 4 + 17}x
|
||||||
|
{outputQr().version * 4 + 17} matrix)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
Error tolerance{" "}
|
||||||
|
<div class="font-bold text-base whitespace-pre">
|
||||||
|
{ECL_NAMES[outputQr().ecl]} ({ECL_LABELS[outputQr().ecl]})
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
Mask{" "}
|
Mask{" "}
|
||||||
|
@ -165,24 +172,6 @@ function RenderedQrCode() {
|
||||||
Encoding{" "}
|
Encoding{" "}
|
||||||
<span class="font-bold text-base">{MODE_KEY[outputQr().mode]}</span>
|
<span class="font-bold text-base">{MODE_KEY[outputQr().mode]}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
|
||||||
Symbol size{" "}
|
|
||||||
<div class="font-bold text-base whitespace-pre">
|
|
||||||
{outputQr().version * 4 + 17}x{outputQr().version * 4 + 17}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
Size incl. margin{" "}
|
|
||||||
<div class="font-bold text-base whitespace-pre">
|
|
||||||
{outputQr().matrixWidth}x{outputQr().matrixHeight}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
Error tolerance{" "}
|
|
||||||
<div class="font-bold text-base whitespace-pre">
|
|
||||||
{ECL_NAMES[outputQr().ecl]} ({ECL_LABELS[outputQr().ecl]})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<FlatButton
|
<FlatButton
|
||||||
|
|
|
@ -15,7 +15,6 @@ import {
|
||||||
Module,
|
Module,
|
||||||
QrOptions,
|
QrOptions,
|
||||||
Version,
|
Version,
|
||||||
Margin,
|
|
||||||
get_matrix,
|
get_matrix,
|
||||||
} from "fuqr";
|
} from "fuqr";
|
||||||
import { createStore, type SetStoreFunction } from "solid-js/store";
|
import { createStore, type SetStoreFunction } from "solid-js/store";
|
||||||
|
@ -27,12 +26,6 @@ type InputQr = {
|
||||||
minEcl: ECL;
|
minEcl: ECL;
|
||||||
mode: Mode | null;
|
mode: Mode | null;
|
||||||
mask: Mask | null;
|
mask: Mask | null;
|
||||||
margin: {
|
|
||||||
top: number;
|
|
||||||
right: number;
|
|
||||||
bottom: number;
|
|
||||||
left: number;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OutputQr = {
|
export type OutputQr = {
|
||||||
|
@ -42,17 +35,8 @@ export type OutputQr = {
|
||||||
ecl: ECL;
|
ecl: ECL;
|
||||||
mode: Mode;
|
mode: Mode;
|
||||||
mask: Mask;
|
mask: Mask;
|
||||||
/** Stored as value b/c Margin is a ptr which becomes null after use */
|
|
||||||
margin: {
|
|
||||||
top: number;
|
|
||||||
right: number;
|
|
||||||
bottom: number;
|
|
||||||
left: number;
|
|
||||||
};
|
|
||||||
/** Stored as value b/c Matrix is a ptr which becomes null after use */
|
/** Stored as value b/c Matrix is a ptr which becomes null after use */
|
||||||
matrix: Module[];
|
matrix: Module[];
|
||||||
matrixWidth: number;
|
|
||||||
matrixHeight: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QrContext = createContext<{
|
export const QrContext = createContext<{
|
||||||
|
@ -83,12 +67,6 @@ export function QrContextProvider(props: { children: JSX.Element }) {
|
||||||
minEcl: ECL.Low,
|
minEcl: ECL.Low,
|
||||||
mode: null,
|
mode: null,
|
||||||
mask: null,
|
mask: null,
|
||||||
margin: {
|
|
||||||
top: 2,
|
|
||||||
right: 2,
|
|
||||||
bottom: 2,
|
|
||||||
left: 2,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [outputQr, setOutputQr] = createSignal<OutputQr | QrError>(
|
const [outputQr, setOutputQr] = createSignal<OutputQr | QrError>(
|
||||||
|
@ -109,31 +87,16 @@ export function QrContextProvider(props: { children: JSX.Element }) {
|
||||||
.min_ecl(inputQr.minEcl)
|
.min_ecl(inputQr.minEcl)
|
||||||
.mask(inputQr.mask!) // null makes more sense than undefined
|
.mask(inputQr.mask!) // null makes more sense than undefined
|
||||||
.mode(inputQr.mode!) // null makes more sense than undefined
|
.mode(inputQr.mode!) // null makes more sense than undefined
|
||||||
.margin(
|
|
||||||
new Margin(0)
|
|
||||||
.setTop(inputQr.margin.top)
|
|
||||||
.setRight(inputQr.margin.right)
|
|
||||||
.setBottom(inputQr.margin.bottom)
|
|
||||||
.setLeft(inputQr.margin.left)
|
|
||||||
);
|
|
||||||
|
|
||||||
let m = get_matrix(inputQr.text, qrOptions);
|
let m = get_matrix(inputQr.text, qrOptions);
|
||||||
|
|
||||||
setOutputQr({
|
setOutputQr({
|
||||||
text: inputQr.text,
|
text: inputQr.text,
|
||||||
matrix: m.value,
|
matrix: m.value,
|
||||||
matrixWidth: m.version["0"] * 4 + 17 + m.margin.left + m.margin.right,
|
|
||||||
matrixHeight: m.version["0"] * 4 + 17 + m.margin.top + m.margin.bottom,
|
|
||||||
version: m.version["0"],
|
version: m.version["0"],
|
||||||
ecl: m.ecl,
|
ecl: m.ecl,
|
||||||
mode: m.mode,
|
mode: m.mode,
|
||||||
mask: m.mask,
|
mask: m.mask,
|
||||||
margin: {
|
|
||||||
top: m.margin.top,
|
|
||||||
right: m.margin.right,
|
|
||||||
bottom: m.margin.bottom,
|
|
||||||
left: m.margin.left,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setOutputQr(e as QrError);
|
setOutputQr(e as QrError);
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
|
||||||
export const PRESET_FUNCS = {
|
export const PRESET_FUNCS = {
|
||||||
Square: `export const paramsSchema = {
|
Square: `export const paramsSchema = {
|
||||||
"Pixel size": {
|
"Margin": {
|
||||||
type: "number",
|
type: "number",
|
||||||
min: 1,
|
min: 0,
|
||||||
max: 20,
|
max: 10,
|
||||||
default: 10
|
default: 2,
|
||||||
},
|
},
|
||||||
"Foreground": {
|
"Foreground": {
|
||||||
type: "Color",
|
type: "Color",
|
||||||
|
@ -14,31 +13,49 @@ export const PRESET_FUNCS = {
|
||||||
"Background": {
|
"Background": {
|
||||||
type: "Color",
|
type: "Color",
|
||||||
default: "#ffffff"
|
default: "#ffffff"
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderCanvas(qr, params, ctx) {
|
export function renderCanvas(qr, params, ctx) {
|
||||||
const pixelSize = params["Pixel size"];
|
const pixelSize = 10;
|
||||||
ctx.canvas.width = qr.matrixWidth * pixelSize;
|
const matrixWidth = qr.version * 4 + 17;
|
||||||
ctx.canvas.height = qr.matrixHeight * pixelSize;
|
const canvasSize = (matrixWidth + 2 * params["Margin"]) * pixelSize;
|
||||||
|
|
||||||
|
ctx.canvas.width = canvasSize;
|
||||||
|
ctx.canvas.height = canvasSize;
|
||||||
|
|
||||||
ctx.fillStyle = params["Background"];
|
ctx.fillStyle = params["Background"];
|
||||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
ctx.fillStyle = params["Foreground"];
|
ctx.fillStyle = params["Foreground"];
|
||||||
|
|
||||||
for (let y = 0; y < qr.matrixHeight; y++) {
|
for (let y = 0; y < matrixWidth; y++) {
|
||||||
for (let x = 0; x < qr.matrixWidth; x++) {
|
for (let x = 0; x < matrixWidth; x++) {
|
||||||
const module = qr.matrix[y * qr.matrixWidth + x];
|
const module = qr.matrix[y * matrixWidth + x];
|
||||||
|
|
||||||
if (module & 1) {
|
if (module & 1) {
|
||||||
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
ctx.fillRect(
|
||||||
|
(x + params["Margin"]) * pixelSize,
|
||||||
|
(y + params["Margin"]) * pixelSize,
|
||||||
|
pixelSize,
|
||||||
|
pixelSize
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Circle: `// qr, ctx are args
|
Circle: `export const paramsSchema = {
|
||||||
|
"Circular finder pattern": {
|
||||||
|
type: "boolean",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
"Circular alignment pattern": {
|
||||||
|
type: "boolean",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const Module = {
|
const Module = {
|
||||||
DataOFF: 0,
|
DataOFF: 0,
|
||||||
DataON: 1,
|
DataON: 1,
|
||||||
|
@ -52,12 +69,15 @@ const Module = {
|
||||||
FormatON: 9,
|
FormatON: 9,
|
||||||
VersionOFF: 10,
|
VersionOFF: 10,
|
||||||
VersionON: 11,
|
VersionON: 11,
|
||||||
Unset: 12,
|
SeparatorOFF: 12,
|
||||||
}
|
}
|
||||||
export function renderCanvas(qr, params, ctx) {
|
export function renderCanvas(qr, params, ctx) {
|
||||||
const pixelSize = 10;
|
const pixelSize = 10;
|
||||||
ctx.canvas.width = qr.matrixWidth * pixelSize;
|
const margin = 2;
|
||||||
ctx.canvas.height = qr.matrixHeight * pixelSize;
|
const matrixWidth = qr.version * 4 + 17;
|
||||||
|
const canvasSize = (matrixWidth + 2 * margin) * pixelSize;
|
||||||
|
ctx.canvas.width = canvasSize;
|
||||||
|
ctx.canvas.height = canvasSize;
|
||||||
|
|
||||||
ctx.fillStyle = "rgb(255, 255, 255)";
|
ctx.fillStyle = "rgb(255, 255, 255)";
|
||||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
@ -79,11 +99,12 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
const radius = pixelSize / 2;
|
const radius = pixelSize / 2;
|
||||||
|
|
||||||
const finderPos = [
|
const finderPos = [
|
||||||
[qr.margin.left, qr.margin.top],
|
[margin, margin],
|
||||||
[qr.matrixWidth - qr.margin.right - 7, qr.margin.top],
|
[margin + matrixWidth - 7, margin],
|
||||||
[qr.margin.left, qr.matrixHeight - qr.margin.bottom - 7],
|
[margin, margin + matrixWidth - 7],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (params["Circular finder pattern"]) {
|
||||||
for (const [x, y] of finderPos) {
|
for (const [x, y] of finderPos) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc((x + 3.5) * pixelSize, (y + 3.5) * pixelSize, 3.5 * pixelSize, 0, 2 * Math.PI);
|
ctx.arc((x + 3.5) * pixelSize, (y + 3.5) * pixelSize, 3.5 * pixelSize, 0, 2 * Math.PI);
|
||||||
|
@ -99,42 +120,46 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
ctx.arc((x + 3.5) * pixelSize, (y + 3.5) * pixelSize, 1.5 * pixelSize, 0, 2 * Math.PI);
|
ctx.arc((x + 3.5) * pixelSize, (y + 3.5) * pixelSize, 1.5 * pixelSize, 0, 2 * Math.PI);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const xMid = qr.matrixWidth / 2;
|
const xMid = matrixWidth / 2;
|
||||||
const yMid = qr.matrixHeight / 2;
|
const yMid = matrixWidth / 2;
|
||||||
const maxDist = Math.sqrt(xMid * xMid + yMid + yMid);
|
const maxDist = Math.sqrt(xMid * xMid + yMid * yMid);
|
||||||
|
|
||||||
for (let y = 0; y < qr.matrixHeight; y++) {
|
for (let y = 0; y < matrixWidth; y++) {
|
||||||
for (let x = 0; x < qr.matrixWidth; x++) {
|
for (let x = 0; x < matrixWidth; x++) {
|
||||||
const module = qr.matrix[y * qr.matrixWidth + x];
|
const module = qr.matrix[y * matrixWidth + x];
|
||||||
|
|
||||||
if (module & 1) {
|
if (module & 1) {
|
||||||
if (module === Module.FinderON) continue;
|
if (params["Circular finder pattern"] && module === Module.FinderON) continue;
|
||||||
if (module === Module.AlignmentON) {
|
if (params["Circular alignment pattern"] && module === Module.AlignmentON) {
|
||||||
// Find top left corner of alignment square
|
// Find top left corner of alignment square
|
||||||
if (qr.matrix[(y - 1) * qr.matrixWidth + x] !== Module.AlignmentON &&
|
if (qr.matrix[(y - 1) * matrixWidth + x] !== Module.AlignmentON &&
|
||||||
qr.matrix[y * qr.matrixWidth + x - 1] !== Module.AlignmentON &&
|
qr.matrix[y * matrixWidth + x - 1] !== Module.AlignmentON &&
|
||||||
qr.matrix[y * qr.matrixWidth + x + 1] === Module.AlignmentON
|
qr.matrix[y * matrixWidth + x + 1] === Module.AlignmentON
|
||||||
) {
|
) {
|
||||||
|
const xPos = x + 2.5 + margin;
|
||||||
|
const yPos = y + 2.5 + margin;
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc((x + 2.5) * pixelSize, (y + 2.5) * pixelSize, 2.5 * pixelSize, 0, 2 * Math.PI);
|
ctx.arc(xPos * pixelSize, yPos * pixelSize, 2.5 * pixelSize, 0, 2 * Math.PI);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
ctx.fillStyle = "rgb(255, 255, 255)";
|
ctx.fillStyle = "rgb(255, 255, 255)";
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc((x + 2.5) * pixelSize, (y + 2.5) * pixelSize, 1.5 * pixelSize, 0, 2 * Math.PI);
|
ctx.arc(xPos * pixelSize, yPos * pixelSize, 1.5 * pixelSize, 0, 2 * Math.PI);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
ctx.fillStyle = gradient;
|
ctx.fillStyle = gradient;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc((x + 2.5) * pixelSize, (y + 2.5) * pixelSize, 0.5 * pixelSize, 0, 2 * Math.PI);
|
ctx.arc(xPos * pixelSize, yPos * pixelSize, 0.5 * pixelSize, 0, 2 * Math.PI);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
const xCenter = x * pixelSize + radius;
|
const xCenter = (x + margin) * pixelSize + radius;
|
||||||
const yCenter = y * pixelSize + radius;
|
const yCenter = (y + margin) * pixelSize + radius;
|
||||||
|
|
||||||
const xDist = Math.abs(xMid - x);
|
const xDist = Math.abs(xMid - x);
|
||||||
const yDist = Math.abs(yMid - y);
|
const yDist = Math.abs(yMid - y);
|
||||||
|
@ -148,7 +173,27 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Camouflage: `// qr, ctx are args
|
Camouflage: `export const paramsSchema = {
|
||||||
|
"Margin": {
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
default: 3,
|
||||||
|
},
|
||||||
|
"Quiet zone": {
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
"Seed": {
|
||||||
|
type: "number",
|
||||||
|
min: 1,
|
||||||
|
max: 100,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const Module = {
|
const Module = {
|
||||||
DataOFF: 0,
|
DataOFF: 0,
|
||||||
DataON: 1,
|
DataON: 1,
|
||||||
|
@ -162,7 +207,7 @@ const Module = {
|
||||||
FormatON: 9,
|
FormatON: 9,
|
||||||
VersionOFF: 10,
|
VersionOFF: 10,
|
||||||
VersionON: 11,
|
VersionON: 11,
|
||||||
Unset: 12,
|
SeparatorOFF: 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
function splitmix32(a) {
|
function splitmix32(a) {
|
||||||
|
@ -178,44 +223,63 @@ function splitmix32(a) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderCanvas(qr, params, ctx) {
|
export function renderCanvas(qr, params, ctx) {
|
||||||
const seededRand = splitmix32(1 /* change seed to change pattern */);
|
const seededRand = splitmix32(params["Seed"]);
|
||||||
|
const margin = params["Margin"];
|
||||||
|
const quietZone = params["Quiet zone"];
|
||||||
|
|
||||||
// Randomly set pixels in margin
|
const pixelSize = 10;
|
||||||
for (let y = 0; y < qr.matrixHeight; y++) {
|
|
||||||
for (let x = 0; x < qr.matrixWidth; x++) {
|
|
||||||
if (y > qr.margin.top - 2 &&
|
|
||||||
y < qr.matrixHeight - qr.margin.bottom + 1 &&
|
|
||||||
x > qr.margin.left - 2 &&
|
|
||||||
x < qr.matrixWidth - qr.margin.right + 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seededRand() > 0.5) qr.matrix[y * qr.matrixWidth + x] = Module.DataON;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pixelSize = 20;
|
|
||||||
const radius = pixelSize / 2;
|
const radius = pixelSize / 2;
|
||||||
ctx.canvas.width = qr.matrixWidth * pixelSize;
|
const qrWidth = qr.version * 4 + 17;
|
||||||
ctx.canvas.height = qr.matrixHeight * pixelSize;
|
const matrixWidth = qrWidth + 2 * margin;
|
||||||
|
const canvasSize = matrixWidth * pixelSize;
|
||||||
|
|
||||||
|
const newMatrix = Array(matrixWidth * matrixWidth).fill(Module.SeparatorOFF);
|
||||||
|
|
||||||
|
// Copy qr to matrix with margin and randomly set pixels in margin
|
||||||
|
for (let y = 0; y < margin - quietZone; y++) {
|
||||||
|
for (let x = 0; x < matrixWidth; x++) {
|
||||||
|
if (seededRand() > 0.5) newMatrix[y * matrixWidth + x] = Module.DataON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let y = margin - quietZone; y < margin + qrWidth + quietZone; y++) {
|
||||||
|
for (let x = 0; x < margin - quietZone; x++) {
|
||||||
|
if (seededRand() > 0.5) newMatrix[y * matrixWidth + x] = Module.DataON;
|
||||||
|
}
|
||||||
|
if (y >= margin && y < margin + qrWidth) {
|
||||||
|
for (let x = margin; x < matrixWidth - margin; x++) {
|
||||||
|
newMatrix[y * matrixWidth + x] = qr.matrix[(y - margin) * qrWidth + x - margin];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let x = margin + qrWidth + quietZone; x < matrixWidth; x++) {
|
||||||
|
if (seededRand() > 0.5) newMatrix[y * matrixWidth + x] = Module.DataON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let y = margin + qrWidth + quietZone; y < matrixWidth; y++) {
|
||||||
|
for (let x = 0; x < matrixWidth; x++) {
|
||||||
|
if (seededRand() > 0.5) newMatrix[y * matrixWidth + x] = Module.DataON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const fg = "rgb(40, 70, 10)";
|
const fg = "rgb(40, 70, 10)";
|
||||||
const bg = "rgb(200, 200, 100)";
|
const bg = "rgb(200, 200, 100)";
|
||||||
|
|
||||||
|
ctx.canvas.width = canvasSize;
|
||||||
|
ctx.canvas.height = canvasSize;
|
||||||
|
|
||||||
ctx.fillStyle = bg;
|
ctx.fillStyle = bg;
|
||||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
const xMax = qr.matrixWidth - 1;
|
const xMax = matrixWidth - 1;
|
||||||
const yMax = qr.matrixHeight - 1;
|
const yMax = matrixWidth - 1;
|
||||||
|
|
||||||
for (let y = 0; y < qr.matrixHeight; y++) {
|
for (let y = 0; y < matrixWidth; y++) {
|
||||||
for (let x = 0; x < qr.matrixWidth; x++) {
|
for (let x = 0; x < matrixWidth; x++) {
|
||||||
const module = qr.matrix[y * qr.matrixWidth + x];
|
const module = newMatrix[y * matrixWidth + x];
|
||||||
|
|
||||||
const top = y > 0 && (qr.matrix[(y - 1) * qr.matrixWidth + x] & 1);
|
const top = y > 0 && (newMatrix[(y - 1) * matrixWidth + x] & 1);
|
||||||
const bottom = y < yMax && (qr.matrix[(y + 1) * qr.matrixWidth + x] & 1);
|
const bottom = y < yMax && (newMatrix[(y + 1) * matrixWidth + x] & 1);
|
||||||
const left = x > 0 && (qr.matrix[y * qr.matrixWidth + x - 1] & 1);
|
const left = x > 0 && (newMatrix[y * matrixWidth + x - 1] & 1);
|
||||||
const right = x < xMax && (qr.matrix[y * qr.matrixWidth + x + 1] & 1);
|
const right = x < xMax && (newMatrix[y * matrixWidth + x + 1] & 1);
|
||||||
|
|
||||||
ctx.fillStyle = fg;
|
ctx.fillStyle = fg;
|
||||||
|
|
||||||
|
@ -236,10 +300,10 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
} else {
|
} else {
|
||||||
// Draw rounded concave corners
|
// Draw rounded concave corners
|
||||||
const topLeft = y > 0 && x > 0 && (qr.matrix[(y - 1) * qr.matrixWidth + x - 1] & 1);
|
const topLeft = y > 0 && x > 0 && (newMatrix[(y - 1) * matrixWidth + x - 1] & 1);
|
||||||
const topRight = y > 0 && x < xMax && (qr.matrix[(y - 1) * qr.matrixWidth + x + 1] & 1);
|
const topRight = y > 0 && x < xMax && (newMatrix[(y - 1) * matrixWidth + x + 1] & 1);
|
||||||
const bottomRight = y < yMax && x < xMax && (qr.matrix[(y + 1) * qr.matrixWidth + x + 1] & 1);
|
const bottomRight = y < yMax && x < xMax && (newMatrix[(y + 1) * matrixWidth + x + 1] & 1);
|
||||||
const bottomLeft = y < yMax && x > 0 && (qr.matrix[(y + 1) * qr.matrixWidth + x - 1] & 1);
|
const bottomLeft = y < yMax && x > 0 && (newMatrix[(y + 1) * matrixWidth + x - 1] & 1);
|
||||||
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
@ -262,7 +326,27 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Minimal: `export function renderCanvas(qr, params, ctx) {
|
Minimal: `export const paramsSchema = {
|
||||||
|
"Margin": {
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
default: 2,
|
||||||
|
},
|
||||||
|
"Pixel size": {
|
||||||
|
type: "number",
|
||||||
|
min: 0,
|
||||||
|
max: 20,
|
||||||
|
default: 8,
|
||||||
|
},
|
||||||
|
"Small pixel size": {
|
||||||
|
type: "number",
|
||||||
|
min: 1,
|
||||||
|
max: 20,
|
||||||
|
default: 4,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
export function renderCanvas(qr, params, ctx) {
|
||||||
const Module = {
|
const Module = {
|
||||||
DataOFF: 0,
|
DataOFF: 0,
|
||||||
DataON: 1,
|
DataON: 1,
|
||||||
|
@ -276,17 +360,22 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
FormatON: 9,
|
FormatON: 9,
|
||||||
VersionOFF: 10,
|
VersionOFF: 10,
|
||||||
VersionON: 11,
|
VersionON: 11,
|
||||||
Unset: 12,
|
SeparatorOFF: 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
const pixelSize = 12;
|
const margin = params["Margin"];
|
||||||
ctx.canvas.width = qr.matrixWidth * pixelSize;
|
const pixelSize = params["Pixel size"];
|
||||||
ctx.canvas.height = qr.matrixHeight * pixelSize;
|
const minSize = params["Small pixel size"];
|
||||||
|
|
||||||
|
const matrixWidth = qr.version * 4 + 17;
|
||||||
|
const canvasSize = (matrixWidth + 2 * margin) * pixelSize;
|
||||||
|
ctx.canvas.width = canvasSize;
|
||||||
|
ctx.canvas.height = canvasSize;
|
||||||
|
|
||||||
const finderPos = [
|
const finderPos = [
|
||||||
[qr.margin.left, qr.margin.top],
|
[margin, margin],
|
||||||
[qr.matrixWidth - qr.margin.right - 7, qr.margin.top],
|
[matrixWidth + margin - 7, margin],
|
||||||
[qr.margin.left, qr.matrixHeight - qr.margin.bottom - 7],
|
[margin, matrixWidth + margin - 7],
|
||||||
];
|
];
|
||||||
|
|
||||||
ctx.fillStyle = "rgb(0, 0, 0)";
|
ctx.fillStyle = "rgb(0, 0, 0)";
|
||||||
|
@ -300,18 +389,22 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
ctx.fillRect((x + 2) * pixelSize, (y + 2) * pixelSize, 3 * pixelSize, 3 * pixelSize);
|
ctx.fillRect((x + 2) * pixelSize, (y + 2) * pixelSize, 3 * pixelSize, 3 * pixelSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
const minSize = pixelSize / 2;
|
|
||||||
const offset = (pixelSize - minSize) / 2;
|
const offset = (pixelSize - minSize) / 2;
|
||||||
|
|
||||||
for (let y = 0; y < qr.matrixHeight; y++) {
|
for (let y = 0; y < matrixWidth; y++) {
|
||||||
for (let x = 0; x < qr.matrixWidth; x++) {
|
for (let x = 0; x < matrixWidth; x++) {
|
||||||
const module = qr.matrix[y * qr.matrixWidth + x];
|
const module = qr.matrix[y * matrixWidth + x];
|
||||||
if ((module | 1) === Module.FinderON) {
|
if ((module | 1) === Module.FinderON) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (module & 1) {
|
if (module & 1) {
|
||||||
ctx.fillRect(x * pixelSize + offset, y * pixelSize + offset, minSize, minSize);
|
ctx.fillRect(
|
||||||
|
(x + margin) * pixelSize + offset,
|
||||||
|
(y + margin) * pixelSize + offset,
|
||||||
|
minSize,
|
||||||
|
minSize
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,8 +412,11 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
`,
|
`,
|
||||||
"Lover (Animated)": `export function renderCanvas(qr, params, ctx) {
|
"Lover (Animated)": `export function renderCanvas(qr, params, ctx) {
|
||||||
const pixelSize = 10;
|
const pixelSize = 10;
|
||||||
ctx.canvas.width = qr.matrixWidth * pixelSize;
|
const margin = 2;
|
||||||
ctx.canvas.height = qr.matrixHeight * pixelSize;
|
const matrixWidth = qr.version * 4 + 17;
|
||||||
|
const canvasSize = (matrixWidth + 2 * margin) * pixelSize;
|
||||||
|
ctx.canvas.width = canvasSize;
|
||||||
|
ctx.canvas.height = canvasSize;
|
||||||
|
|
||||||
const period = 3000; // ms
|
const period = 3000; // ms
|
||||||
const amplitude = 0.8; // maxSize - minSize
|
const amplitude = 0.8; // maxSize - minSize
|
||||||
|
@ -345,9 +441,9 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
ctx.fillStyle = "rgb(0, 0, 0)";
|
ctx.fillStyle = "rgb(0, 0, 0)";
|
||||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
for (let y = 0; y < qr.matrixHeight; y++) {
|
for (let y = 0; y < matrixWidth; y++) {
|
||||||
for (let x = 0; x < qr.matrixWidth; x++) {
|
for (let x = 0; x < matrixWidth; x++) {
|
||||||
const module = qr.matrix[y * qr.matrixWidth + x];
|
const module = qr.matrix[y * matrixWidth + x];
|
||||||
if ((module & 1) === 0) continue;
|
if ((module & 1) === 0) continue;
|
||||||
|
|
||||||
const xBias = Math.abs(5 - (x % 10));
|
const xBias = Math.abs(5 - (x % 10));
|
||||||
|
@ -360,7 +456,7 @@ export function renderCanvas(qr, params, ctx) {
|
||||||
const offset = (pixelSize - size) / 2;
|
const offset = (pixelSize - size) / 2;
|
||||||
|
|
||||||
ctx.fillStyle = \`rgb(\${100 + ratio * 150}, \${200 + xBias * 10}, 255)\`;
|
ctx.fillStyle = \`rgb(\${100 + ratio * 150}, \${200 + xBias * 10}, 255)\`;
|
||||||
ctx.fillRect(x * pixelSize + offset, y * pixelSize + offset, size, size);
|
ctx.fillRect((x + margin) * pixelSize + offset, (y + margin) * pixelSize + offset, size, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req = requestAnimationFrame(frame);
|
req = requestAnimationFrame(frame);
|
||||||
|
|
Ładowanie…
Reference in New Issue