Rounded QR code
This commit is contained in:
parent
d6ee03f4d8
commit
8ecd58b176
|
|
@ -523,7 +523,27 @@ export default function CreatePage() {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const svg = document.querySelector('#create-qr-preview svg');
|
const svg = document.querySelector('#create-qr-preview svg');
|
||||||
if (!svg) return;
|
if (!svg) return;
|
||||||
const svgData = new XMLSerializer().serializeToString(svg);
|
|
||||||
|
let svgData = new XMLSerializer().serializeToString(svg);
|
||||||
|
|
||||||
|
// If rounded corners, wrap in a clipped SVG
|
||||||
|
if (cornerStyle === 'rounded') {
|
||||||
|
const width = svg.getAttribute('width') || '200';
|
||||||
|
const height = svg.getAttribute('height') || '200';
|
||||||
|
const borderRadius = 20;
|
||||||
|
|
||||||
|
svgData = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
||||||
|
<defs>
|
||||||
|
<clipPath id="rounded-corners">
|
||||||
|
<rect x="0" y="0" width="${width}" height="${height}" rx="${borderRadius}" ry="${borderRadius}"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#rounded-corners)">
|
||||||
|
${svgData}
|
||||||
|
</g>
|
||||||
|
</svg>`;
|
||||||
|
}
|
||||||
|
|
||||||
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
|
|
@ -545,6 +565,8 @@ export default function CreatePage() {
|
||||||
if (!svg) return;
|
if (!svg) return;
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
const svgData = new XMLSerializer().serializeToString(svg);
|
const svgData = new XMLSerializer().serializeToString(svg);
|
||||||
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
||||||
|
|
@ -553,7 +575,25 @@ export default function CreatePage() {
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
canvas.width = 200;
|
canvas.width = 200;
|
||||||
canvas.height = 200;
|
canvas.height = 200;
|
||||||
ctx?.drawImage(img, 0, 0);
|
|
||||||
|
// Apply rounded corners if needed
|
||||||
|
if (cornerStyle === 'rounded') {
|
||||||
|
const borderRadius = 20;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(borderRadius, 0);
|
||||||
|
ctx.lineTo(200 - borderRadius, 0);
|
||||||
|
ctx.quadraticCurveTo(200, 0, 200, borderRadius);
|
||||||
|
ctx.lineTo(200, 200 - borderRadius);
|
||||||
|
ctx.quadraticCurveTo(200, 200, 200 - borderRadius, 200);
|
||||||
|
ctx.lineTo(borderRadius, 200);
|
||||||
|
ctx.quadraticCurveTo(0, 200, 0, 200 - borderRadius);
|
||||||
|
ctx.lineTo(0, borderRadius);
|
||||||
|
ctx.quadraticCurveTo(0, 0, borderRadius, 0);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
canvas.toBlob((blob) => {
|
canvas.toBlob((blob) => {
|
||||||
if (blob) {
|
if (blob) {
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
@ -564,6 +604,7 @@ export default function CreatePage() {
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
};
|
};
|
||||||
img.src = url;
|
img.src = url;
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -354,10 +354,9 @@ export default function SettingsPage() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={handleManageSubscription}
|
onClick={() => window.location.href = '/pricing'}
|
||||||
disabled={loading}
|
|
||||||
>
|
>
|
||||||
{loading ? 'Loading...' : 'Manage Subscription'}
|
Manage Subscription
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,26 @@ END:VCARD`;
|
||||||
if (!svg) return;
|
if (!svg) return;
|
||||||
|
|
||||||
if (format === 'svg') {
|
if (format === 'svg') {
|
||||||
const svgData = new XMLSerializer().serializeToString(svg);
|
let svgData = new XMLSerializer().serializeToString(svg);
|
||||||
|
|
||||||
|
// If rounded corners, wrap in a clipped SVG
|
||||||
|
if (qr.style?.cornerStyle === 'rounded') {
|
||||||
|
const width = svg.getAttribute('width') || '96';
|
||||||
|
const height = svg.getAttribute('height') || '96';
|
||||||
|
const borderRadius = 10; // Smaller radius for dashboard
|
||||||
|
|
||||||
|
svgData = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
||||||
|
<defs>
|
||||||
|
<clipPath id="rounded-corners-${qr.id}">
|
||||||
|
<rect x="0" y="0" width="${width}" height="${height}" rx="${borderRadius}" ry="${borderRadius}"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#rounded-corners-${qr.id})">
|
||||||
|
${svgData}
|
||||||
|
</g>
|
||||||
|
</svg>`;
|
||||||
|
}
|
||||||
|
|
||||||
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
|
|
@ -96,6 +115,8 @@ END:VCARD`;
|
||||||
// Convert SVG to PNG
|
// Convert SVG to PNG
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
const svgData = new XMLSerializer().serializeToString(svg);
|
const svgData = new XMLSerializer().serializeToString(svg);
|
||||||
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
||||||
|
|
@ -104,7 +125,25 @@ END:VCARD`;
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
canvas.width = 300;
|
canvas.width = 300;
|
||||||
canvas.height = 300;
|
canvas.height = 300;
|
||||||
ctx?.drawImage(img, 0, 0, 300, 300);
|
|
||||||
|
// Apply rounded corners if needed
|
||||||
|
if (qr.style?.cornerStyle === 'rounded') {
|
||||||
|
const borderRadius = 30; // Scale up for 300px canvas
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(borderRadius, 0);
|
||||||
|
ctx.lineTo(300 - borderRadius, 0);
|
||||||
|
ctx.quadraticCurveTo(300, 0, 300, borderRadius);
|
||||||
|
ctx.lineTo(300, 300 - borderRadius);
|
||||||
|
ctx.quadraticCurveTo(300, 300, 300 - borderRadius, 300);
|
||||||
|
ctx.lineTo(borderRadius, 300);
|
||||||
|
ctx.quadraticCurveTo(0, 300, 0, 300 - borderRadius);
|
||||||
|
ctx.lineTo(0, borderRadius);
|
||||||
|
ctx.quadraticCurveTo(0, 0, borderRadius, 0);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0, 300, 300);
|
||||||
canvas.toBlob((blob) => {
|
canvas.toBlob((blob) => {
|
||||||
if (blob) {
|
if (blob) {
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
@ -166,6 +205,7 @@ END:VCARD`;
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id={`qr-${qr.id}`} className="flex items-center justify-center bg-gray-50 rounded-lg p-4 mb-3">
|
<div id={`qr-${qr.id}`} className="flex items-center justify-center bg-gray-50 rounded-lg p-4 mb-3">
|
||||||
|
<div className={qr.style?.cornerStyle === 'rounded' ? 'rounded-lg overflow-hidden' : ''}>
|
||||||
<QRCodeSVG
|
<QRCodeSVG
|
||||||
value={qrUrl}
|
value={qrUrl}
|
||||||
size={96}
|
size={96}
|
||||||
|
|
@ -174,6 +214,7 @@ END:VCARD`;
|
||||||
level="M"
|
level="M"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue