kopia lustrzana https://github.com/zhengkyl/qrframe
lowercase param types + replace square with basic and tutorial
rodzic
f6bafd0e0f
commit
7a7c3853a3
|
@ -9,15 +9,15 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Dots: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Lines: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
export const paramsSchema = {
|
||||
Margin: {
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 0.1,
|
||||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Roundness: {
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
},
|
||||
"Data size": {
|
||||
type: "number",
|
||||
min: 0.5,
|
||||
max: 1.5,
|
||||
step: 0.1,
|
||||
default: 1,
|
||||
},
|
||||
Logo: {
|
||||
type: "file",
|
||||
accept: ".jpeg, .jpg, .png, .svg",
|
||||
},
|
||||
"Logo size": {
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.25,
|
||||
},
|
||||
};
|
||||
|
||||
const Module = {
|
||||
DataOFF: 0,
|
||||
DataON: 1,
|
||||
FinderOFF: 2,
|
||||
FinderON: 3,
|
||||
AlignmentOFF: 4,
|
||||
AlignmentON: 5,
|
||||
TimingOFF: 6,
|
||||
TimingON: 7,
|
||||
FormatOFF: 8,
|
||||
FormatON: 9,
|
||||
VersionOFF: 10,
|
||||
VersionON: 11,
|
||||
SeparatorOFF: 12,
|
||||
};
|
||||
|
||||
// unformatted floats can bloat file size
|
||||
const fmt = (n) => n.toFixed(2).replace(/\.00$/, "");
|
||||
|
||||
export async function renderSVG(qr, params) {
|
||||
const matrixWidth = qr.version * 4 + 17;
|
||||
const margin = params["Margin"];
|
||||
const fg = params["Foreground"];
|
||||
const bg = params["Background"];
|
||||
const roundness = params["Roundness"];
|
||||
const file = params["Logo"];
|
||||
|
||||
const size = matrixWidth + 2 * margin;
|
||||
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${-margin} ${-margin} ${size} ${size}">`;
|
||||
svg += `<rect x="${-margin}" y="${-margin}" width="${size}" height="${size}" fill="${bg}"/>`;
|
||||
|
||||
svg += `<g fill="${fg}">`;
|
||||
svg += `<path d="`;
|
||||
const lgRadius = 3.5 * roundness;
|
||||
const mdRadius = 2.5 * roundness;
|
||||
const smRadius = 1.5 * roundness;
|
||||
const lgSide = fmt(7 - 2 * lgRadius);
|
||||
const mdSide = fmt(5 - 2 * mdRadius);
|
||||
const smSide = fmt(3 - 2 * smRadius);
|
||||
|
||||
const corner = (radius, xDir, yDir, cw) =>
|
||||
`a${fmt(radius)},${fmt(radius)} 0,0,${cw ? "1" : "0"} ${fmt(xDir * radius)},${fmt(yDir * radius)}`;
|
||||
|
||||
for (const [x, y] of [
|
||||
[0, 0],
|
||||
[matrixWidth - 7, 0],
|
||||
[0, matrixWidth - 7],
|
||||
]) {
|
||||
svg += `M${fmt(x + lgRadius)},${y}h${lgSide}${corner(lgRadius, 1, 1, true)}v${lgSide}${corner(lgRadius, -1, 1, true)}h-${lgSide}${corner(lgRadius, -1, -1, true)}v-${lgSide}${corner(lgRadius, 1, -1, true)}`;
|
||||
|
||||
svg += `M${fmt(x + 1 + mdRadius)},${y + 1}${corner(mdRadius, -1, 1, false)}v${mdSide}${corner(mdRadius, 1, 1, false)}h${mdSide}${corner(mdRadius, 1, -1, false)}v-${mdSide}${corner(mdRadius, -1, -1, false)}`;
|
||||
|
||||
svg += `M${fmt(x + 2 + smRadius)},${y + 2}h${smSide}${corner(smRadius, 1, 1, true)}v${smSide}${corner(smRadius, -1, 1, true)}h-${smSide}${corner(smRadius, -1, -1, true)}v-${smSide}${corner(smRadius, 1, -1, true)}`;
|
||||
}
|
||||
svg += `"/>`;
|
||||
|
||||
const dataSize = params["Data size"];
|
||||
const dataRadius = fmt((roundness * dataSize) / 2);
|
||||
const dataOffset = (1 - dataSize) / 2;
|
||||
for (let y = 0; y < matrixWidth; y++) {
|
||||
for (let x = 0; x < matrixWidth; x++) {
|
||||
const module = qr.matrix[y * matrixWidth + x];
|
||||
if (module & 1) {
|
||||
if (module === Module.FinderON) continue;
|
||||
svg += `<rect x="${fmt(x + dataOffset)}" y="${fmt(y + dataOffset)}" width="${dataSize}" height="${dataSize}" rx="${dataRadius}"/>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
svg += `</g>`;
|
||||
|
||||
if (file != null) {
|
||||
const bytes = await file.bytes();
|
||||
const b64 = btoa(
|
||||
Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("")
|
||||
);
|
||||
const logoSize = fmt(params["Logo size"] * size);
|
||||
const logoOffset = fmt(((1 - params["Logo size"]) * size) / 2 - margin);
|
||||
svg += `<image x="${logoOffset}" y="${logoOffset}" width="${logoSize}" height="${logoSize}" href="data:${file.type};base64,${b64}"/>`;
|
||||
}
|
||||
|
||||
svg += `</svg>`;
|
||||
return svg;
|
||||
}
|
|
@ -9,23 +9,23 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Finder: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#131d87",
|
||||
},
|
||||
Horizontal: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#dc9c07",
|
||||
},
|
||||
Vertical: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#d21313",
|
||||
},
|
||||
Cross: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#131d87",
|
||||
},
|
||||
"Horizontal thickness": {
|
||||
|
|
|
@ -9,27 +9,27 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Finder: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#141e92",
|
||||
},
|
||||
"Large circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#10a8e9",
|
||||
},
|
||||
"Medium circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#1aa8cc",
|
||||
},
|
||||
"Small circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#0f8bdd",
|
||||
},
|
||||
"Tiny circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#012c8f",
|
||||
},
|
||||
"Randomize circle size": {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
export const paramsSchema = {
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#1c4a1a",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#e3d68a",
|
||||
},
|
||||
Margin: {
|
||||
|
|
|
@ -13,23 +13,23 @@ export const paramsSchema = {
|
|||
default: 0,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
"Finder pattern": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Default", "Circle", "Square"],
|
||||
},
|
||||
"Alignment pattern": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Default", "Circle", "Square"],
|
||||
},
|
||||
"Scale direction": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["None", "Center", "Edge"],
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -20,21 +20,21 @@ export const paramsSchema = {
|
|||
default: 1.3,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Array",
|
||||
type: "array",
|
||||
resizable: true,
|
||||
props: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
},
|
||||
default: ["#f7158b", "#02d1fd", "#1f014b"],
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
// See browser compatibility issues here
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
|
||||
"Mix blend mode": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: [
|
||||
"normal",
|
||||
"multiply",
|
||||
|
|
|
@ -8,7 +8,7 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
"Fill style": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: [
|
||||
"Hachure",
|
||||
"Solid",
|
||||
|
@ -21,7 +21,7 @@ export const paramsSchema = {
|
|||
default: "Zigzag",
|
||||
},
|
||||
Fill: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
"Fill weight": {
|
||||
|
@ -37,7 +37,7 @@ export const paramsSchema = {
|
|||
default: 4,
|
||||
},
|
||||
Stroke: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
"Stroke width": {
|
||||
|
@ -63,7 +63,7 @@ export const paramsSchema = {
|
|||
default: 1,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#222222",
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -6,11 +6,11 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#fcb9ff",
|
||||
},
|
||||
Shapes: {
|
||||
|
@ -40,7 +40,7 @@ export const paramsSchema = {
|
|||
default: 0.3,
|
||||
},
|
||||
"QR layer": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Above", "Below"],
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Halftone is a misnomer, but that's what this type of QR code is known as
|
||||
export const paramsSchema = {
|
||||
Image: {
|
||||
type: "File",
|
||||
type: "file",
|
||||
},
|
||||
Contrast: {
|
||||
type: "number",
|
||||
|
@ -34,11 +34,11 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
};
|
||||
|
@ -75,7 +75,7 @@ export async function renderCanvas(qr, params, canvas) {
|
|||
"https://upload.wikimedia.org/wikipedia/commons/1/14/The_Widow_%28Boston_Public_Library%29_%28cropped%29.jpg"
|
||||
).then((res) => res.blob());
|
||||
}
|
||||
const image = await createImageBitmap(file)
|
||||
const image = await createImageBitmap(file);
|
||||
|
||||
const pixelWidth = matrixWidth + 2 * margin;
|
||||
const canvasSize = pixelWidth * unit;
|
||||
|
|
|
@ -6,19 +6,19 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Array",
|
||||
type: "array",
|
||||
props: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
},
|
||||
resizable: true,
|
||||
default: ["#860909","#0e21a0","#95800f"]
|
||||
default: ["#860909", "#0e21a0", "#95800f"],
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Lines: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
"Line thickness": {
|
||||
|
@ -62,7 +62,9 @@ export function renderSVG(qr, params) {
|
|||
svg += `<rect width="${size}" height="${size}" fill="${params["Lines"]}"/>`;
|
||||
|
||||
let lightLayer = `<path fill="${params["Background"]}" d="`;
|
||||
const darkLayers = params["Foreground"].map((color) => `<path fill="${color}" d="`)
|
||||
const darkLayers = params["Foreground"].map(
|
||||
(color) => `<path fill="${color}" d="`
|
||||
);
|
||||
|
||||
const visited = Array.from({ length: matrixWidth * matrixWidth }).fill(false);
|
||||
const matrix = Array.from({ length: matrixWidth * matrixWidth }).fill(0);
|
||||
|
@ -127,7 +129,7 @@ export function renderSVG(qr, params) {
|
|||
}
|
||||
}
|
||||
}
|
||||
darkLayers.forEach((layer) => svg += layer + `"/>`)
|
||||
darkLayers.forEach((layer) => (svg += layer + `"/>`));
|
||||
svg += lightLayer + `"/>`;
|
||||
svg += `</svg>`;
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
export const paramsSchema = {
|
||||
Foreground: {
|
||||
type: "Array" ,
|
||||
type: "array",
|
||||
props: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
},
|
||||
resizable: true,
|
||||
default: [ "#fb51dd", "#f2cffa", "#aefdfd", "#54a9fe" ]
|
||||
default: ["#fb51dd", "#f2cffa", "#aefdfd", "#54a9fe"],
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
default: "#101529"
|
||||
type: "color",
|
||||
default: "#101529",
|
||||
},
|
||||
Margin: {
|
||||
type: "number",
|
||||
|
@ -18,7 +18,7 @@ export const paramsSchema = {
|
|||
default: 4,
|
||||
},
|
||||
"Quiet zone": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Minimal", "Full"],
|
||||
},
|
||||
Invert: {
|
||||
|
|
|
@ -9,15 +9,15 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
"Finder pattern": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Atom", "Planet"],
|
||||
},
|
||||
Particles: {
|
||||
|
|
|
@ -18,15 +18,15 @@ export const paramsSchema = {
|
|||
default: 6,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Grout: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#b3b8fd",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,11 +7,11 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
};
|
||||
|
@ -39,3 +39,28 @@ export function renderSVG(qr, params) {
|
|||
|
||||
return svg;
|
||||
}
|
||||
|
||||
// export function renderCanvas(qr, params, canvas) {
|
||||
// const matrixWidth = qr.version * 4 + 17;
|
||||
// const margin = params["Margin"];
|
||||
// const fg = params["Foreground"];
|
||||
// const bg = params["Background"];
|
||||
// const unit = 10;
|
||||
// const size = (matrixWidth + 2 * margin) * unit;
|
||||
// canvas.width = size;
|
||||
// canvas.height = size;
|
||||
|
||||
// const ctx = canvas.getContext("2d");
|
||||
// ctx.fillStyle = bg;
|
||||
// ctx.fillRect(0, 0, size, size)
|
||||
|
||||
// ctx.fillStyle = fg;
|
||||
// for (let y = 0; y < matrixWidth; y++) {
|
||||
// for (let x = 0; x < matrixWidth; x++) {
|
||||
// const module = qr.matrix[y * matrixWidth + x];
|
||||
// if (module & 1) {
|
||||
// ctx.fillRect((x + margin) * unit, (y + margin) * unit, unit, unit)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -5,9 +5,10 @@ import { FlatButton } from "./Button";
|
|||
type Props = {
|
||||
value: File | null;
|
||||
setValue: (f: File | null) => void;
|
||||
accept?: string;
|
||||
};
|
||||
|
||||
export function ImageInput(props: Props) {
|
||||
export function FileInput(props: Props) {
|
||||
let input: HTMLInputElement;
|
||||
return (
|
||||
<div class="inline-flex items-center gap-1">
|
||||
|
@ -15,7 +16,7 @@ export function ImageInput(props: Props) {
|
|||
class="border rounded-md text-sm px-1 py-2 file:(bg-transparent border-none text-fore-base) bg-back-base hover:bg-fore-base/5 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)"
|
||||
ref={input!}
|
||||
type="file"
|
||||
accept=".jpeg, .jpg, .png"
|
||||
accept={props.accept}
|
||||
onChange={(e) => {
|
||||
// @ts-expect-error onChange is called so files exists
|
||||
props.setValue(e.target.files[0]);
|
||||
|
|
|
@ -7,20 +7,23 @@ import { createSignal } from "solid-js";
|
|||
import { FillButton } from "./Button";
|
||||
|
||||
type Props = {
|
||||
onClick: (width, height) => void;
|
||||
onClick: (resizeWidth, resizeHeight) => void;
|
||||
};
|
||||
export function SplitButton(props: Props) {
|
||||
const [customWidth, setCustomWidth] = createSignal(1000);
|
||||
const [customHeight, setCustomHeight] = createSignal(1000);
|
||||
|
||||
const onClick = (width, height) => {
|
||||
props.onClick(width, height);
|
||||
const onClick = (resizeWidth, resizeHeight) => {
|
||||
props.onClick(resizeWidth, resizeHeight);
|
||||
setOpen(false);
|
||||
};
|
||||
const [open, setOpen] = createSignal(false);
|
||||
return (
|
||||
<div class="leading-tight flex">
|
||||
<Button class="border border-e-none rounded-md rounded-e-none hover:bg-fore-base/5 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) inline-flex justify-center items-center gap-1 flex-1 px-6 py-2">
|
||||
<Button
|
||||
class="border border-e-none rounded-md rounded-e-none hover:bg-fore-base/5 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) inline-flex justify-center items-center gap-1 flex-1 px-6 py-2"
|
||||
onClick={() => onClick(0, 0)}
|
||||
>
|
||||
<Download size={20} />
|
||||
PNG
|
||||
</Button>
|
||||
|
|
|
@ -23,7 +23,7 @@ export function ParamsEditor() {
|
|||
<div class="flex flex-col gap-2 mb-4">
|
||||
<For each={Object.entries(paramsSchema())}>
|
||||
{([label, { type, ...other }]) => {
|
||||
if (type === "Array") {
|
||||
if (type === "array") {
|
||||
return <ArrayParam label={label} other={other} />;
|
||||
}
|
||||
return (
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
import Pencil from "lucide-solid/icons/pencil";
|
||||
import Trash2 from "lucide-solid/icons/trash-2";
|
||||
import {
|
||||
For,
|
||||
Index,
|
||||
Show,
|
||||
batch,
|
||||
createSignal,
|
||||
onMount,
|
||||
type JSX,
|
||||
} from "solid-js";
|
||||
import { For, Show, batch, createSignal, onMount, type JSX } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { Dynamic } from "solid-js/web";
|
||||
import {
|
||||
PARAM_COMPONENTS,
|
||||
defaultParams,
|
||||
deepEqualObj,
|
||||
parseParamsSchema,
|
||||
|
@ -27,9 +17,8 @@ import { TextInput, TextareaInput } from "../TextInput";
|
|||
import { CodeEditor } from "./CodeEditor";
|
||||
import { Settings } from "./Settings";
|
||||
import { clearToasts, toastError } from "../ErrorToasts";
|
||||
import Minus from "lucide-solid/icons/minus";
|
||||
import Plus from "lucide-solid/icons/plus";
|
||||
import { ParamsEditor } from "./ParamsEditor";
|
||||
import { Tutorial } from "~/lib/presets/Tutorial";
|
||||
|
||||
type Props = {
|
||||
class?: string;
|
||||
|
@ -62,7 +51,7 @@ export function Editor(props: Props) {
|
|||
setRender,
|
||||
} = useQrContext();
|
||||
|
||||
const [code, setCode] = createSignal(PRESET_CODE.Square);
|
||||
const [code, setCode] = createSignal(PRESET_CODE.Basic);
|
||||
const [funcKeys, _setFuncKeys] = createStore<string[]>([]);
|
||||
const [thumbs, setThumbs] = createStore<Thumbs>({} as Thumbs);
|
||||
|
||||
|
@ -413,9 +402,7 @@ export function Editor(props: Props) {
|
|||
)}
|
||||
</For>
|
||||
<Preview
|
||||
onClick={() =>
|
||||
createAndSelectFunc("custom", PRESET_CODE.Square)
|
||||
}
|
||||
onClick={() => createAndSelectFunc("custom", Tutorial)}
|
||||
label="Create new"
|
||||
active={false}
|
||||
>
|
||||
|
@ -429,7 +416,7 @@ export function Editor(props: Props) {
|
|||
</Preview>
|
||||
</div>
|
||||
</div>
|
||||
<ParamsEditor/>
|
||||
<ParamsEditor />
|
||||
<CodeEditor
|
||||
initialValue={code()}
|
||||
onSave={(code, updateThumbnail) => {
|
||||
|
|
|
@ -185,14 +185,34 @@ function RenderedQrCode() {
|
|||
<div class="font-bold text-sm pb-2">Downloads</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<SplitButton
|
||||
onClick={async (width, height) => {
|
||||
onClick={async (resizeWidth, resizeHeight) => {
|
||||
let outCanvas;
|
||||
if (render()?.type === "canvas") {
|
||||
download(canvas.toDataURL("image/png"), `${filename()}.png`);
|
||||
if (resizeWidth === 0 && resizeHeight === 0) {
|
||||
outCanvas = canvas;
|
||||
} else {
|
||||
const bmp = await createImageBitmap(canvas, {
|
||||
// resizeQuality unsupported in ff 8/31/24
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/createImageBitmap
|
||||
resizeQuality: "pixelated",
|
||||
resizeWidth,
|
||||
resizeHeight,
|
||||
});
|
||||
outCanvas = document.createElement("canvas");
|
||||
outCanvas.width = resizeWidth;
|
||||
outCanvas.height = resizeHeight;
|
||||
const ctx = outCanvas.getContext("bitmaprenderer")!;
|
||||
ctx.transferFromImageBitmap(bmp);
|
||||
}
|
||||
} else {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
if (resizeWidth === 0 && resizeHeight === 0) {
|
||||
resizeWidth = 300;
|
||||
resizeHeight = 300;
|
||||
}
|
||||
outCanvas = document.createElement("canvas");
|
||||
outCanvas.width = resizeWidth;
|
||||
outCanvas.height = resizeHeight;
|
||||
const ctx = outCanvas.getContext("2d")!;
|
||||
|
||||
const url = URL.createObjectURL(
|
||||
new Blob([svgParent.innerHTML], { type: "image/svg+xml" })
|
||||
|
@ -200,11 +220,22 @@ function RenderedQrCode() {
|
|||
const img = new Image();
|
||||
img.src = url;
|
||||
await img.decode();
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
|
||||
download(canvas.toDataURL("image/png"), `${filename()}.png`);
|
||||
ctx.drawImage(img, 0, 0, resizeWidth, resizeHeight);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
outCanvas.toBlob((blob) => {
|
||||
if (blob == null) {
|
||||
toastError(
|
||||
"Failed to create image",
|
||||
"idk bro this shouldn't happen"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const url = URL.createObjectURL(blob);
|
||||
download(url, `${filename()}.png`);
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Show when={render()?.type === "svg"}>
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
import { ColorInput } from "~/components/ColorInput";
|
||||
import { ImageInput } from "~/components/ImageInput";
|
||||
import { FileInput } from "~/components/ImageInput";
|
||||
import { NumberInput } from "~/components/NumberInput";
|
||||
import { Select } from "~/components/Select";
|
||||
import { Switch } from "~/components/Switch";
|
||||
|
||||
const PARAM_TYPES = ["boolean", "number", "Color", "Select", "File", "Array"];
|
||||
const PARAM_TYPES = ["boolean", "number", "color", "select", "file", "array"];
|
||||
|
||||
export const PARAM_COMPONENTS = {
|
||||
boolean: Switch,
|
||||
number: NumberInput,
|
||||
Color: ColorInput,
|
||||
Select: Select,
|
||||
File: ImageInput,
|
||||
color: ColorInput,
|
||||
select: Select,
|
||||
file: FileInput,
|
||||
};
|
||||
|
||||
export const PARAM_DEFAULTS = {
|
||||
boolean: false,
|
||||
number: 0,
|
||||
Color: "#000000",
|
||||
// Select: //default is first option
|
||||
File: null,
|
||||
Array: [],
|
||||
color: "#000000",
|
||||
// select: //default is first option
|
||||
file: null,
|
||||
array: [],
|
||||
};
|
||||
|
||||
type PARAM_VALUE_TYPES = {
|
||||
boolean: boolean;
|
||||
number: number;
|
||||
Color: string;
|
||||
Select: any;
|
||||
File: File | null;
|
||||
Array: any[];
|
||||
color: string;
|
||||
select: any;
|
||||
file: File | null;
|
||||
array: any[];
|
||||
};
|
||||
|
||||
export type Params = {
|
||||
|
@ -94,7 +94,7 @@ function parseField(value: any) {
|
|||
!PARAM_TYPES.includes(value.type)
|
||||
) {
|
||||
return null;
|
||||
} else if (value.type === "Select") {
|
||||
} else if (value.type === "select") {
|
||||
if (
|
||||
!("options" in value) ||
|
||||
!Array.isArray(value.options) ||
|
||||
|
@ -102,7 +102,7 @@ function parseField(value: any) {
|
|||
) {
|
||||
return null;
|
||||
}
|
||||
} else if (value.type === "Array") {
|
||||
} else if (value.type === "array") {
|
||||
if (!("props" in value)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -115,9 +115,9 @@ function parseField(value: any) {
|
|||
|
||||
// === undefined b/c null is a valid default value for ImageInput
|
||||
if (value.default === undefined) {
|
||||
if (value.type === "Select") {
|
||||
if (value.type === "select") {
|
||||
value.default = value.options[0];
|
||||
} else if (value.type === "Array") {
|
||||
} else if (value.type === "array") {
|
||||
value.default = Array.from(
|
||||
{ length: value.defaultLength ?? 1 },
|
||||
() => value.props.default
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Alien } from "./presets/Alien";
|
||||
import { Basic } from "./presets/Basic";
|
||||
import { Blocks } from "./presets/Blocks";
|
||||
import { Bubbles } from "./presets/Bubbles";
|
||||
import { Camo } from "./presets/Camo";
|
||||
|
@ -11,11 +12,10 @@ import { Minimal } from "./presets/Minimal";
|
|||
import { Mondrian } from "./presets/Mondrian";
|
||||
import { Neon } from "./presets/Neon";
|
||||
import { Quantum } from "./presets/Quantum";
|
||||
import { Square } from "./presets/Square";
|
||||
import { Tile } from "./presets/Tile";
|
||||
|
||||
export const PRESET_CODE = {
|
||||
Square,
|
||||
Basic,
|
||||
Circle,
|
||||
Camo,
|
||||
Neon,
|
||||
|
|
|
@ -9,15 +9,15 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Dots: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Lines: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
export const Basic = `export const paramsSchema = {
|
||||
Margin: {
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 0.1,
|
||||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Roundness: {
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
},
|
||||
"Data size": {
|
||||
type: "number",
|
||||
min: 0.5,
|
||||
max: 1.5,
|
||||
step: 0.1,
|
||||
default: 1,
|
||||
},
|
||||
Logo: {
|
||||
type: "file",
|
||||
accept: ".jpeg, .jpg, .png, .svg",
|
||||
},
|
||||
"Logo size": {
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.25,
|
||||
},
|
||||
};
|
||||
|
||||
const Module = {
|
||||
DataOFF: 0,
|
||||
DataON: 1,
|
||||
FinderOFF: 2,
|
||||
FinderON: 3,
|
||||
AlignmentOFF: 4,
|
||||
AlignmentON: 5,
|
||||
TimingOFF: 6,
|
||||
TimingON: 7,
|
||||
FormatOFF: 8,
|
||||
FormatON: 9,
|
||||
VersionOFF: 10,
|
||||
VersionON: 11,
|
||||
SeparatorOFF: 12,
|
||||
};
|
||||
|
||||
// unformatted floats can bloat file size
|
||||
const fmt = (n) => n.toFixed(2).replace(/\.00$/, "");
|
||||
|
||||
export async function renderSVG(qr, params) {
|
||||
const matrixWidth = qr.version * 4 + 17;
|
||||
const margin = params["Margin"];
|
||||
const fg = params["Foreground"];
|
||||
const bg = params["Background"];
|
||||
const roundness = params["Roundness"];
|
||||
const file = params["Logo"];
|
||||
|
||||
const size = matrixWidth + 2 * margin;
|
||||
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
|
||||
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
|
||||
|
||||
svg += \`<g fill="\${fg}">\`;
|
||||
svg += \`<path d="\`;
|
||||
const lgRadius = 3.5 * roundness;
|
||||
const mdRadius = 2.5 * roundness;
|
||||
const smRadius = 1.5 * roundness;
|
||||
const lgSide = fmt(7 - 2 * lgRadius);
|
||||
const mdSide = fmt(5 - 2 * mdRadius);
|
||||
const smSide = fmt(3 - 2 * smRadius);
|
||||
|
||||
const corner = (radius, xDir, yDir, cw) =>
|
||||
\`a\${fmt(radius)},\${fmt(radius)} 0,0,\${cw ? "1" : "0"} \${fmt(xDir * radius)},\${fmt(yDir * radius)}\`;
|
||||
|
||||
for (const [x, y] of [
|
||||
[0, 0],
|
||||
[matrixWidth - 7, 0],
|
||||
[0, matrixWidth - 7],
|
||||
]) {
|
||||
svg += \`M\${fmt(x + lgRadius)},\${y}h\${lgSide}\${corner(lgRadius, 1, 1, true)}v\${lgSide}\${corner(lgRadius, -1, 1, true)}h-\${lgSide}\${corner(lgRadius, -1, -1, true)}v-\${lgSide}\${corner(lgRadius, 1, -1, true)}\`;
|
||||
|
||||
svg += \`M\${fmt(x + 1 + mdRadius)},\${y + 1}\${corner(mdRadius, -1, 1, false)}v\${mdSide}\${corner(mdRadius, 1, 1, false)}h\${mdSide}\${corner(mdRadius, 1, -1, false)}v-\${mdSide}\${corner(mdRadius, -1, -1, false)}\`;
|
||||
|
||||
svg += \`M\${fmt(x + 2 + smRadius)},\${y + 2}h\${smSide}\${corner(smRadius, 1, 1, true)}v\${smSide}\${corner(smRadius, -1, 1, true)}h-\${smSide}\${corner(smRadius, -1, -1, true)}v-\${smSide}\${corner(smRadius, 1, -1, true)}\`;
|
||||
}
|
||||
svg += \`"/>\`;
|
||||
|
||||
const dataSize = params["Data size"];
|
||||
const dataRadius = fmt((roundness * dataSize) / 2);
|
||||
const dataOffset = (1 - dataSize) / 2;
|
||||
for (let y = 0; y < matrixWidth; y++) {
|
||||
for (let x = 0; x < matrixWidth; x++) {
|
||||
const module = qr.matrix[y * matrixWidth + x];
|
||||
if (module & 1) {
|
||||
if (module === Module.FinderON) continue;
|
||||
svg += \`<rect x="\${fmt(x + dataOffset)}" y="\${fmt(y + dataOffset)}" width="\${dataSize}" height="\${dataSize}" rx="\${dataRadius}"/>\`;
|
||||
}
|
||||
}
|
||||
}
|
||||
svg += \`</g>\`;
|
||||
|
||||
if (file != null) {
|
||||
const bytes = await file.bytes();
|
||||
const b64 = btoa(
|
||||
Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("")
|
||||
);
|
||||
const logoSize = fmt(params["Logo size"] * size);
|
||||
const logoOffset = fmt(((1 - params["Logo size"]) * size) / 2 - margin);
|
||||
svg += \`<image x="\${logoOffset}" y="\${logoOffset}" width="\${logoSize}" height="\${logoSize}" href="data:\${file.type};base64,\${b64}"/>\`;
|
||||
}
|
||||
|
||||
svg += \`</svg>\`;
|
||||
return svg;
|
||||
}
|
||||
`
|
|
@ -9,23 +9,23 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Finder: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#131d87",
|
||||
},
|
||||
Horizontal: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#dc9c07",
|
||||
},
|
||||
Vertical: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#d21313",
|
||||
},
|
||||
Cross: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#131d87",
|
||||
},
|
||||
"Horizontal thickness": {
|
||||
|
|
|
@ -9,27 +9,27 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Finder: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#141e92",
|
||||
},
|
||||
"Large circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#10a8e9",
|
||||
},
|
||||
"Medium circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#1aa8cc",
|
||||
},
|
||||
"Small circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#0f8bdd",
|
||||
},
|
||||
"Tiny circle": {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#012c8f",
|
||||
},
|
||||
"Randomize circle size": {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
export const Camo = `export const paramsSchema = {
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#1c4a1a",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#e3d68a",
|
||||
},
|
||||
Margin: {
|
||||
|
|
|
@ -13,23 +13,23 @@ export const Circle = `export const paramsSchema = {
|
|||
default: 0,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
"Finder pattern": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Default", "Circle", "Square"],
|
||||
},
|
||||
"Alignment pattern": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Default", "Circle", "Square"],
|
||||
},
|
||||
"Scale direction": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["None", "Center", "Edge"],
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -20,21 +20,21 @@ export const Dots = `export const paramsSchema = {
|
|||
default: 1.3,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Array",
|
||||
type: "array",
|
||||
resizable: true,
|
||||
props: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
},
|
||||
default: ["#f7158b", "#02d1fd", "#1f014b"],
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
// See browser compatibility issues here
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
|
||||
"Mix blend mode": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: [
|
||||
"normal",
|
||||
"multiply",
|
||||
|
|
|
@ -8,7 +8,7 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
"Fill style": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: [
|
||||
"Hachure",
|
||||
"Solid",
|
||||
|
@ -21,7 +21,7 @@ export const paramsSchema = {
|
|||
default: "Zigzag",
|
||||
},
|
||||
Fill: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
"Fill weight": {
|
||||
|
@ -37,7 +37,7 @@ export const paramsSchema = {
|
|||
default: 4,
|
||||
},
|
||||
Stroke: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
"Stroke width": {
|
||||
|
@ -63,7 +63,7 @@ export const paramsSchema = {
|
|||
default: 1,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#222222",
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -6,11 +6,11 @@ export const Glass = `export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#fcb9ff",
|
||||
},
|
||||
Shapes: {
|
||||
|
@ -40,7 +40,7 @@ export const Glass = `export const paramsSchema = {
|
|||
default: 0.3,
|
||||
},
|
||||
"QR layer": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Above", "Below"],
|
||||
},
|
||||
Seed: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export const Halftone = `// Halftone is a misnomer, but that's what this type of QR code is known as
|
||||
export const paramsSchema = {
|
||||
Image: {
|
||||
type: "File",
|
||||
type: "file",
|
||||
},
|
||||
Contrast: {
|
||||
type: "number",
|
||||
|
@ -34,11 +34,11 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
};
|
||||
|
@ -75,7 +75,7 @@ export async function renderCanvas(qr, params, canvas) {
|
|||
"https://upload.wikimedia.org/wikipedia/commons/1/14/The_Widow_%28Boston_Public_Library%29_%28cropped%29.jpg"
|
||||
).then((res) => res.blob());
|
||||
}
|
||||
const image = await createImageBitmap(file)
|
||||
const image = await createImageBitmap(file);
|
||||
|
||||
const pixelWidth = matrixWidth + 2 * margin;
|
||||
const canvasSize = pixelWidth * unit;
|
||||
|
|
|
@ -6,26 +6,26 @@ export const Mondrian = `export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Array",
|
||||
type: "array",
|
||||
props: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
},
|
||||
resizable: true,
|
||||
default: ["#ad1010","#172fce","#b39906"]
|
||||
default: ["#860909", "#0e21a0", "#95800f"],
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Lines: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
"Line thickness": {
|
||||
type: "number",
|
||||
min: 0,
|
||||
min: -10,
|
||||
max: 10,
|
||||
default: 4,
|
||||
default: 2,
|
||||
},
|
||||
Seed: {
|
||||
type: "number",
|
||||
|
@ -62,7 +62,9 @@ export function renderSVG(qr, params) {
|
|||
svg += \`<rect width="\${size}" height="\${size}" fill="\${params["Lines"]}"/>\`;
|
||||
|
||||
let lightLayer = \`<path fill="\${params["Background"]}" d="\`;
|
||||
const darkLayers = params["Foreground"].map((color) => \`<path fill="\${color}" d="\`)
|
||||
const darkLayers = params["Foreground"].map(
|
||||
(color) => \`<path fill="\${color}" d="\`
|
||||
);
|
||||
|
||||
const visited = Array.from({ length: matrixWidth * matrixWidth }).fill(false);
|
||||
const matrix = Array.from({ length: matrixWidth * matrixWidth }).fill(0);
|
||||
|
@ -127,7 +129,7 @@ export function renderSVG(qr, params) {
|
|||
}
|
||||
}
|
||||
}
|
||||
darkLayers.forEach((layer) => svg += layer + \`"/>\`)
|
||||
darkLayers.forEach((layer) => (svg += layer + \`"/>\`));
|
||||
svg += lightLayer + \`"/>\`;
|
||||
svg += \`</svg>\`;
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
export const Neon = `export const paramsSchema = {
|
||||
Foreground: {
|
||||
type: "Array" ,
|
||||
type: "array",
|
||||
props: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
},
|
||||
resizable: true,
|
||||
default: [ "#fb51dd", "#f2cffa", "#aefdfd", "#54a9fe" ]
|
||||
default: ["#fb51dd", "#f2cffa", "#aefdfd", "#54a9fe"],
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
default: "#101529"
|
||||
type: "color",
|
||||
default: "#101529",
|
||||
},
|
||||
Margin: {
|
||||
type: "number",
|
||||
|
@ -18,7 +18,7 @@ export const Neon = `export const paramsSchema = {
|
|||
default: 4,
|
||||
},
|
||||
"Quiet zone": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Minimal", "Full"],
|
||||
},
|
||||
Invert: {
|
||||
|
|
|
@ -9,15 +9,15 @@ export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
"Finder pattern": {
|
||||
type: "Select",
|
||||
type: "select",
|
||||
options: ["Atom", "Planet"],
|
||||
},
|
||||
Particles: {
|
||||
|
|
|
@ -18,15 +18,15 @@ export const Tile = `export const paramsSchema = {
|
|||
default: 6,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
Grout: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#b3b8fd",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export const Square = `export const paramsSchema = {
|
||||
export const Tutorial = `export const paramsSchema = {
|
||||
Margin: {
|
||||
type: "number",
|
||||
min: 0,
|
||||
|
@ -7,11 +7,11 @@ export const Square = `export const paramsSchema = {
|
|||
default: 2,
|
||||
},
|
||||
Foreground: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#000000",
|
||||
},
|
||||
Background: {
|
||||
type: "Color",
|
||||
type: "color",
|
||||
default: "#ffffff",
|
||||
},
|
||||
};
|
||||
|
@ -39,4 +39,29 @@ export function renderSVG(qr, params) {
|
|||
|
||||
return svg;
|
||||
}
|
||||
|
||||
// export function renderCanvas(qr, params, canvas) {
|
||||
// const matrixWidth = qr.version * 4 + 17;
|
||||
// const margin = params["Margin"];
|
||||
// const fg = params["Foreground"];
|
||||
// const bg = params["Background"];
|
||||
// const unit = 10;
|
||||
// const size = (matrixWidth + 2 * margin) * unit;
|
||||
// canvas.width = size;
|
||||
// canvas.height = size;
|
||||
|
||||
// const ctx = canvas.getContext("2d");
|
||||
// ctx.fillStyle = bg;
|
||||
// ctx.fillRect(0, 0, size, size)
|
||||
|
||||
// ctx.fillStyle = fg;
|
||||
// for (let y = 0; y < matrixWidth; y++) {
|
||||
// for (let x = 0; x < matrixWidth; x++) {
|
||||
// const module = qr.matrix[y * matrixWidth + x];
|
||||
// if (module & 1) {
|
||||
// ctx.fillRect((x + margin) * unit, (y + margin) * unit, unit, unit)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
`
|
Ładowanie…
Reference in New Issue