From 1d9dc9a57efe8611d9e2a72aa7ba31601b2ff8d2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Sep 2025 04:40:44 +0000
Subject: [PATCH 1/2] Initial plan
From 269bd46d084171f3b1e966964ee83c583d7742fd Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Sep 2025 04:49:48 +0000
Subject: [PATCH 2/2] Implement device pixel ratio detection for QRCodeCanvas
zoom support
Co-authored-by: zpao <8445+zpao@users.noreply.github.com>
---
src/__test__/index.test.tsx | 39 +++++++++++++++++++++++++++++++++++++
src/index.tsx | 36 ++++++++++++++++++++++++++++++++--
2 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/src/__test__/index.test.tsx b/src/__test__/index.test.tsx
index 24687c1..27dd497 100644
--- a/src/__test__/index.test.tsx
+++ b/src/__test__/index.test.tsx
@@ -170,3 +170,42 @@ describe('`style` is passed to rendered nodes and merged correctly', () => {
expect(container.firstChild).toMatchSnapshot();
});
});
+
+describe('Device Pixel Ratio Detection', () => {
+ test('QRCodeCanvas reacts to devicePixelRatio changes', async () => {
+ // Import act from testing library
+ const { act } = await import('@testing-library/react');
+
+ // Mock window.devicePixelRatio
+ const originalDevicePixelRatio = window.devicePixelRatio;
+ Object.defineProperty(window, 'devicePixelRatio', {
+ writable: true,
+ value: 1,
+ });
+
+ const {rerender} = render();
+
+ // Simulate zoom change by changing devicePixelRatio and triggering resize
+ Object.defineProperty(window, 'devicePixelRatio', {
+ writable: true,
+ value: 2,
+ });
+
+ // Trigger resize event to simulate zoom wrapped in act
+ await act(async () => {
+ window.dispatchEvent(new Event('resize'));
+ });
+
+ // Re-render to trigger effect
+ rerender();
+
+ // The component should handle the change gracefully
+ expect(window.devicePixelRatio).toBe(2);
+
+ // Restore original value
+ Object.defineProperty(window, 'devicePixelRatio', {
+ writable: true,
+ value: originalDevicePixelRatio,
+ });
+ });
+});
diff --git a/src/index.tsx b/src/index.tsx
index b6dd37a..8c62956 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -384,6 +384,11 @@ const QRCodeCanvas = React.forwardRef(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [isImgLoaded, setIsImageLoaded] = React.useState(false);
+ // Track devicePixelRatio to re-render when zoom changes
+ const [devicePixelRatio, setDevicePixelRatio] = React.useState(
+ () => (typeof window !== 'undefined' ? window.devicePixelRatio : 1) || 1
+ );
+
const {margin, cells, numCells, calculatedImageSettings} = useQRCode({
value,
level,
@@ -395,6 +400,23 @@ const QRCodeCanvas = React.forwardRef(
size,
});
+ // Listen for resize events to detect zoom changes
+ React.useEffect(() => {
+ if (typeof window === 'undefined') return;
+
+ const handleResize = () => {
+ const newPixelRatio = window.devicePixelRatio || 1;
+ if (newPixelRatio !== devicePixelRatio) {
+ setDevicePixelRatio(newPixelRatio);
+ }
+ };
+
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, [devicePixelRatio]);
+
React.useEffect(() => {
// Always update the canvas. It's cheap enough and we want to be correct
// with the current state.
@@ -428,7 +450,7 @@ const QRCodeCanvas = React.forwardRef(
// matches the number of cells. This avoids rounding issues, but does
// result in some potentially unwanted single pixel issues between
// blocks, only in environments that don't support Path2D.
- const pixelRatio = window.devicePixelRatio || 1;
+ const pixelRatio = devicePixelRatio;
canvas.height = canvas.width = size * pixelRatio;
const scale = (size / numCells) * pixelRatio;
ctx.scale(scale, scale);
@@ -465,7 +487,17 @@ const QRCodeCanvas = React.forwardRef(
);
}
}
- });
+ }, [
+ cells,
+ numCells,
+ size,
+ bgColor,
+ fgColor,
+ margin,
+ calculatedImageSettings,
+ isImgLoaded,
+ devicePixelRatio,
+ ]);
// Ensure we mark image loaded as false here so we trigger updating the
// canvas in our other effect.