nxtgauge-frontend-solid/src/components/CaptchaCanvas.tsx
Ashwin Kumar Sivakumar 4c61bcaf31 fix: remove DPR scaling from CaptchaCanvas to fix zoomed-in appearance
- Fixed canvas dimensions to 176x52 pixels (no DPR scaling)
- All drawing operations now use fixed pixel values
- Prevents captcha from appearing zoomed in on high-DPI displays
- Characters, lines, and circles are now properly positioned within bounds
2026-06-12 05:03:54 +05:30

98 lines
2.6 KiB
TypeScript

import { createEffect, onMount } from 'solid-js';
type CaptchaCanvasProps = {
code: string;
class?: string;
};
export default function CaptchaCanvas(props: CaptchaCanvasProps) {
let canvasRef: HTMLCanvasElement | undefined;
const drawCaptcha = () => {
const canvas = canvasRef;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
// Expose captcha code for automated testing
if (typeof window !== 'undefined') {
window.__captchaCode = props.code;
}
const width = 176;
const height = 52;
// Set canvas resolution (fixed at 1x to prevent zoom issues)
canvas.width = width;
canvas.height = height;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
ctx.setTransform(1, 0, 0, 1, 0, 0);
// Clear and fill background
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, width, height);
// Draw decorative lines (within bounds)
for (let i = 0; i < 2; i += 1) {
ctx.strokeStyle = i % 2 === 0 ? 'rgba(253,98,22,0.16)' : 'rgba(27,36,64,0.14)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(Math.random() * width, Math.random() * height);
ctx.lineTo(Math.random() * width, Math.random() * height);
ctx.stroke();
}
// Draw decorative circles (within bounds)
for (let i = 0; i < 3; i += 1) {
ctx.fillStyle = i % 2 === 0 ? 'rgba(253,98,22,0.10)' : 'rgba(27,36,64,0.09)';
ctx.beginPath();
ctx.arc(Math.random() * width, Math.random() * height, Math.random() * 1.8 + 0.6, 0, Math.PI * 2);
ctx.fill();
}
// Draw characters (fixed positioning)
const chars = String(props.code || '').slice(0, 6).split('');
const startX = 16;
const charGap = 24;
chars.forEach((char, index) => {
const x = startX + index * charGap;
const y = height / 2;
const rotation = 0;
ctx.save();
ctx.translate(x, y);
ctx.rotate(rotation);
ctx.textBaseline = 'middle';
ctx.font = `800 22px "Courier New", monospace`;
ctx.fillStyle = index % 2 === 0 ? '#0f172a' : '#c2410c';
ctx.lineWidth = 0;
ctx.fillText(char, 0, 0);
ctx.restore();
});
};
onMount(() => {
drawCaptcha();
});
createEffect(() => {
// Access props.code to track it and redraw when it changes
const _ = props.code;
drawCaptcha();
});
return (
<canvas
ref={canvasRef}
width={176}
height={52}
aria-label="Captcha image"
draggable={false}
onContextMenu={(event) => event.preventDefault()}
class={props.class}
/>
);
}