Skip to content

Commit a0b60dc

Browse files
author
FlyingT
committed
feat: Textfarbe für Beschriftung und Export-Fix (v1.2.0)
- Neue Option: Textfarbe der Beschriftung anpassbar - Export-Fix: Rahmen wird nun korrekt in PNG/JPG Dateien mit ausgegeben - UI: Kontrollkarten kompakter gestaltet (vertical stretching entfernt) - Rahmendicke Standard auf 10px, Farbe auf Dunkelgrau gesetzt
1 parent f17f276 commit a0b60dc

3 files changed

Lines changed: 78 additions & 47 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
Alle wichtigen Änderungen an diesem Projekt werden in dieser Datei protokolliert.
44

5+
## [1.2.0] - 2026-01-30
6+
7+
### Hinzugefügt
8+
- Neue Funktion: Textfarbe für die Beschriftung nun frei wählbar.
9+
10+
### Behoben
11+
- Export-Fix: Der Rahmen wird nun korrekt in heruntergeladenen Bildern (PNG/JPG) mit exportiert.
12+
- UI-Optimierung: Die Kontrollkarten sind nun kompakter gestaltet, um unnötigen Leerraum zu vermeiden.
13+
- Standardeinstellungen: Rahmendicke auf 10px und Rahmenfarbe auf Dunkelgrau (#374151) angepasst.
14+
515
## [1.1.1] - 2026-01-30
616

717
### Behoben

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
# Mosaik QR-Code Generator
22

3-
Ein einfacher, moderner QR-Code Generator, der optisch an das [belegt](https://github.com/FlyingT/belegt) Tool angepasst ist. Das Interface ist vollständig auf Deutsch.
3+
Ein einfacher, moderner QR-Code Generator, reduziert auf die wesentlichen Funktionen.
44

5-
![QR-Generator Vorschau](https://raw.githubusercontent.com/FlyingT/mosaik/main/preview.png)
5+
![]()
66

77
## Funktionen
88

99
- **Individueller Inhalt**: Erstellt QR-Codes aus URLs oder beliebigem Text.
1010
- **Design-Anpassung**:
1111
- Farben für QR-Code und Hintergrund frei wählbar.
12+
- **Beschriftung**: Text unter dem Code mit frei wählbarer Farbe.
1213
- Umfangreicher Rahmen (**Dicke** und **Farbe** wählbar, perfekt für Branding).
13-
- Textzeile unter dem QR-Code hinzufügbar.
1414
- **Vorschau**: Live-Vorschau der Änderungen im Browser in Echtzeit.
1515
- **Export**: Hochauflösender Download als PNG, JPG oder SVG mit automatischer Dateibenennung.
1616
- **Lokal & Sicher**: Die Generierung erfolgt vollständig im Browser, es werden keine Daten an externe Server gesendet.

src/App.tsx

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ function App() {
77
const [fgColor, setFgColor] = useState('#000000');
88
const [bgColor, setBgColor] = useState('#ffffff');
99
const [hasBorder, setHasBorder] = useState(false);
10-
const [borderThickness, setBorderThickness] = useState(20);
11-
const [borderColor, setBorderColor] = useState('#000000');
10+
const [borderThickness, setBorderThickness] = useState(10);
11+
const [borderColor, setBorderColor] = useState('#374151');
1212
const [bottomText, setBottomText] = useState('');
13+
const [textColor, setTextColor] = useState('#000000');
1314

14-
const version = "v1.1.1";
15+
const version = "v1.2.0";
1516
const author = "TK";
1617

1718
const qrRef = useRef<HTMLDivElement>(null);
@@ -42,34 +43,38 @@ function App() {
4243
const ctx = tempCanvas.getContext('2d');
4344
if (!ctx) return;
4445

45-
const padding = hasBorder ? Number(borderThickness) : 20;
46-
const textHeight = bottomText ? 40 : 0;
46+
const borderPadding = hasBorder ? Number(borderThickness) : 0;
47+
const innerPadding = 10; // Corresponds to the p-2 in preview
48+
const totalPadding = borderPadding + innerPadding;
49+
const textSpace = bottomText ? 60 : 0;
4750

48-
tempCanvas.width = canvas.width + padding * 2;
49-
tempCanvas.height = canvas.height + padding * 2 + textHeight;
51+
tempCanvas.width = canvas.width + totalPadding * 2;
52+
tempCanvas.height = canvas.height + totalPadding * 2 + textSpace;
5053

51-
// Fill background with border color if border is on, otherwise background color
52-
ctx.fillStyle = hasBorder ? borderColor : bgColor;
53-
ctx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
54-
55-
// If border is on, draw a background for the QR code itself using the bgColor
56-
if (hasBorder) {
57-
ctx.fillStyle = bgColor;
58-
ctx.fillRect(padding - 10, padding - 10, canvas.width + 20, canvas.height + 20);
54+
// 1. Background / Border color
55+
ctx.fillStyle = hasBorder ? borderColor : (format === 'jpg' ? '#ffffff' : 'transparent');
56+
if (format === 'png' && !hasBorder) {
57+
ctx.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
58+
} else {
59+
ctx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
5960
}
6061

61-
// Draw QR Code
62-
ctx.drawImage(canvas, padding, padding);
62+
// 2. QR Background Box
63+
ctx.fillStyle = bgColor;
64+
ctx.fillRect(borderPadding, borderPadding, canvas.width + innerPadding * 2, canvas.height + innerPadding * 2 + textSpace);
65+
66+
// 3. Draw QR Code
67+
ctx.drawImage(canvas, totalPadding, totalPadding);
6368

64-
// Draw optional text
69+
// 4. Draw Label text
6570
if (bottomText) {
66-
ctx.fillStyle = fgColor;
67-
ctx.font = 'bold 16px Inter, sans-serif';
71+
ctx.fillStyle = textColor;
72+
ctx.font = 'bold 20px Inter, sans-serif';
6873
ctx.textAlign = 'center';
69-
ctx.fillText(bottomText, tempCanvas.width / 2, canvas.height + padding + textHeight / 2 + 5);
74+
ctx.fillText(bottomText, tempCanvas.width / 2, canvas.height + totalPadding + 40);
7075
}
7176

72-
const url = tempCanvas.toDataURL(`image/${format === 'jpg' ? 'jpeg' : 'png'}`);
77+
const url = tempCanvas.toDataURL(`image/${format === 'jpg' ? 'jpeg' : 'png'}`, 1.0);
7378
const link = document.createElement('a');
7479
link.download = `qrcode_${Date.now()}.${format}`;
7580
link.href = url;
@@ -97,10 +102,10 @@ function App() {
97102
</div>
98103
</header>
99104

100-
<main className="max-w-5xl mx-auto w-full grid grid-cols-1 lg:grid-cols-3 gap-8 flex-grow">
105+
<main className="max-w-5xl mx-auto w-full grid grid-cols-1 lg:grid-cols-3 gap-8 items-start">
101106
{/* Controls */}
102-
<div className="lg:col-span-1 flex flex-col gap-6 h-full">
103-
<div className="card p-6 space-y-4 flex-grow">
107+
<div className="lg:col-span-1 flex flex-col gap-6">
108+
<div className="card p-6 space-y-4">
104109
<h2 className="text-lg font-semibold flex items-center gap-2">
105110
<Type className="w-5 h-5 text-primary" />
106111
Inhalt & Text
@@ -119,17 +124,30 @@ function App() {
119124

120125
<div>
121126
<label className="label">Beschriftung</label>
122-
<input
123-
type="text"
124-
className="input-field"
125-
value={bottomText}
126-
onChange={(e) => setBottomText(e.target.value)}
127-
placeholder="Scan mich!, Hinweis, ..."
128-
/>
127+
<div className="flex flex-col gap-3">
128+
<input
129+
type="text"
130+
className="input-field"
131+
value={bottomText}
132+
onChange={(e) => setBottomText(e.target.value)}
133+
placeholder="Scan mich!, Hinweis, ..."
134+
/>
135+
{bottomText && (
136+
<div className="flex items-center gap-2 animate-in fade-in duration-200">
137+
<label className="text-xs font-medium text-gray-500 whitespace-nowrap">Textfarbe:</label>
138+
<input
139+
type="color"
140+
className="h-8 w-12 cursor-pointer rounded border border-gray-200"
141+
value={textColor}
142+
onChange={(e) => setTextColor(e.target.value)}
143+
/>
144+
</div>
145+
)}
146+
</div>
129147
</div>
130148
</div>
131149

132-
<div className="card p-6 space-y-4 flex-grow">
150+
<div className="card p-6 space-y-4">
133151
<h2 className="text-lg font-semibold flex items-center gap-2">
134152
<Palette className="w-5 h-5 text-primary" />
135153
Farben & Design
@@ -202,7 +220,7 @@ function App() {
202220

203221
{/* Preview */}
204222
<div className="lg:col-span-2 flex flex-col">
205-
<div className="card p-8 flex flex-col items-center justify-between min-h-[500px] bg-white relative flex-grow">
223+
<div className="card p-8 flex flex-col items-center justify-between bg-white relative">
206224
<div className="text-sm font-medium text-gray-400 uppercase tracking-widest mb-4">Vorschau</div>
207225

208226
<div className="flex-grow flex items-center justify-center w-full">
@@ -211,12 +229,15 @@ function App() {
211229
className={`transition-all duration-300 flex flex-col items-center justify-center`}
212230
style={{
213231
backgroundColor: hasBorder ? borderColor : 'transparent',
214-
padding: hasBorder ? `${borderThickness}px` : '1rem',
232+
padding: hasBorder ? `${borderThickness}px` : '0',
215233
borderRadius: '0.75rem',
216234
boxShadow: hasBorder ? '0 10px 15px -3px rgb(0 0 0 / 0.1)' : 'none'
217235
}}
218236
>
219-
<div className="bg-white p-2 rounded shadow-sm" style={{ backgroundColor: bgColor }}>
237+
<div
238+
className="flex flex-col items-center p-2 rounded shadow-sm"
239+
style={{ backgroundColor: bgColor }}
240+
>
220241
<div className="hidden">
221242
<QRCodeCanvas
222243
value={text || ' '}
@@ -233,15 +254,15 @@ function App() {
233254
bgColor={bgColor}
234255
level="H"
235256
/>
257+
{bottomText && (
258+
<div
259+
className="mt-6 font-bold text-xl"
260+
style={{ color: textColor }}
261+
>
262+
{bottomText}
263+
</div>
264+
)}
236265
</div>
237-
{bottomText && (
238-
<div
239-
className="mt-6 font-bold text-xl"
240-
style={{ color: fgColor }}
241-
>
242-
{bottomText}
243-
</div>
244-
)}
245266
</div>
246267
</div>
247268

0 commit comments

Comments
 (0)