Skip to content

Commit

Permalink
fix: fix
Browse files Browse the repository at this point in the history
  • Loading branch information
li-jia-nan committed Jan 8, 2025
1 parent 2b58c4d commit d85a6e7
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 203 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,4 @@
"react-dom": ">=16.9.0"
},
"packageManager": "[email protected]+sha512.8e4c3550fb500e808dbc30bb0ce4dd1eb614e30b1c55245f211591ec2cdf9c611cabd34e1364b42f564bd54b3945ed0f49d61d1bbf2ec9bd74b866fcdc723276"
}
}
36 changes: 21 additions & 15 deletions src/QRCodeCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from './utils';

const QRCodeCanvas = React.forwardRef<HTMLCanvasElement, QRPropsCanvas>(
function QRCodeCanvas(props, forwardedRef) {
(props, ref) => {
const {
value,
size = DEFAULT_SIZE,
Expand All @@ -29,22 +29,21 @@ const QRCodeCanvas = React.forwardRef<HTMLCanvasElement, QRPropsCanvas>(
...otherProps
} = props;
const imgSrc = imageSettings?.src;
const _canvas = React.useRef<HTMLCanvasElement | null>(null);
const _canvas = React.useRef<HTMLCanvasElement>(null);
const _image = React.useRef<HTMLImageElement>(null);

const setCanvasRef = React.useCallback(
(node: HTMLCanvasElement | null) => {
(node: HTMLCanvasElement) => {
_canvas.current = node;
if (typeof forwardedRef === 'function') {
forwardedRef(node);
} else if (forwardedRef) {
forwardedRef.current = node;
if (typeof ref === 'function') {
ref(node);
} else if (ref) {
ref.current = node;
}
},
[forwardedRef],
[ref],
);


const [, setIsImageLoaded] = React.useState(false);

const { margin, cells, numCells, calculatedImageSettings } = useQRCode({
Expand All @@ -58,7 +57,7 @@ const QRCodeCanvas = React.forwardRef<HTMLCanvasElement, QRPropsCanvas>(
});

React.useEffect(() => {
if (_canvas.current != null) {
if (_canvas.current) {
const canvas = _canvas.current;

const ctx = canvas.getContext('2d');
Expand Down Expand Up @@ -89,16 +88,15 @@ const QRCodeCanvas = React.forwardRef<HTMLCanvasElement, QRPropsCanvas>(
const scale = (size / numCells) * pixelRatio;
ctx.scale(scale, scale);


ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, numCells, numCells);

ctx.fillStyle = fgColor;
if (isSupportPath2d) {
ctx.fill(new Path2D(generatePath(cellsToDraw, margin)));
} else {
cells.forEach(function (row, rdx) {
row.forEach(function (cell, cdx) {
cells.forEach((row, rdx) => {
row.forEach((cell, cdx) => {
if (cell) {
ctx.fillRect(cdx + margin, rdx + margin, 1, 1);
}
Expand Down Expand Up @@ -126,7 +124,12 @@ const QRCodeCanvas = React.forwardRef<HTMLCanvasElement, QRPropsCanvas>(
setIsImageLoaded(false);
}, [imgSrc]);

const canvasStyle = { height: size, width: size, ...style };
const canvasStyle: React.CSSProperties = {
height: size,
width: size,
...style,
};

let img = null;
if (imgSrc != null) {
img = (
Expand Down Expand Up @@ -159,6 +162,9 @@ const QRCodeCanvas = React.forwardRef<HTMLCanvasElement, QRPropsCanvas>(
);
},
);
QRCodeCanvas.displayName = 'QRCodeCanvas';

if (process.env.NODE_ENV !== 'production') {
QRCodeCanvas.displayName = 'QRCodeCanvas';
}

export { QRCodeCanvas };
140 changes: 69 additions & 71 deletions src/QRCodeSVG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,81 +12,79 @@ import {
} from './utils';
import { useQRCode } from './hooks/useQRCode';

const QRCodeSVG = React.forwardRef<SVGSVGElement, QRPropsSVG>(
function QRCodeSVG(props, forwardedRef) {
const {
value,
size = DEFAULT_SIZE,
level = DEFAULT_LEVEL,
bgColor = DEFAULT_BACKGROUND_COLOR,
fgColor = DEFAULT_FRONT_COLOR,
includeMargin = DEFAULT_NEED_MARGIN,
minVersion = DEFAULT_MINVERSION,
title,
marginSize,
imageSettings,
...otherProps
} = props;
const QRCodeSVG = React.forwardRef<SVGSVGElement, QRPropsSVG>((props, ref) => {
const {
value,
size = DEFAULT_SIZE,
level = DEFAULT_LEVEL,
bgColor = DEFAULT_BACKGROUND_COLOR,
fgColor = DEFAULT_FRONT_COLOR,
includeMargin = DEFAULT_NEED_MARGIN,
minVersion = DEFAULT_MINVERSION,
title,
marginSize,
imageSettings,
...otherProps
} = props;

const { margin, cells, numCells, calculatedImageSettings } = useQRCode({
value,
level,
minVersion,
includeMargin,
marginSize,
imageSettings,
size,
});
const { margin, cells, numCells, calculatedImageSettings } = useQRCode({
value,
level,
minVersion,
includeMargin,
marginSize,
imageSettings,
size,
});

let cellsToDraw = cells;
let image = null;
if (imageSettings != null && calculatedImageSettings != null) {
if (calculatedImageSettings.excavation != null) {
cellsToDraw = excavateModules(
cells,
calculatedImageSettings.excavation,
);
}

image = (
<image
href={imageSettings.src}
height={calculatedImageSettings.h}
width={calculatedImageSettings.w}
x={calculatedImageSettings.x + margin}
y={calculatedImageSettings.y + margin}
preserveAspectRatio="none"
opacity={calculatedImageSettings.opacity}
// when crossOrigin is not set, the image will be tainted
// and the canvas cannot be exported to an image
crossOrigin={calculatedImageSettings.crossOrigin}
/>
);
let cellsToDraw = cells;
let image = null;
if (imageSettings != null && calculatedImageSettings != null) {
if (calculatedImageSettings.excavation != null) {
cellsToDraw = excavateModules(cells, calculatedImageSettings.excavation);
}

const fgPath = generatePath(cellsToDraw, margin);

return (
<svg
height={size}
width={size}
viewBox={`0 0 ${numCells} ${numCells}`}
ref={forwardedRef}
role="img"
{...otherProps}
>
{!!title && <title>{title}</title>}
<path
fill={bgColor}
d={`M0,0 h${numCells}v${numCells}H0z`}
shapeRendering="crispEdges"
/>
<path fill={fgColor} d={fgPath} shapeRendering="crispEdges" />
{image}
</svg>
image = (
<image
href={imageSettings.src}
height={calculatedImageSettings.h}
width={calculatedImageSettings.w}
x={calculatedImageSettings.x + margin}
y={calculatedImageSettings.y + margin}
preserveAspectRatio="none"
opacity={calculatedImageSettings.opacity}
// when crossOrigin is not set, the image will be tainted
// and the canvas cannot be exported to an image
crossOrigin={calculatedImageSettings.crossOrigin}
/>
);
},
);
QRCodeSVG.displayName = 'QRCodeSVG';
}

const fgPath = generatePath(cellsToDraw, margin);

return (
<svg
height={size}
width={size}
viewBox={`0 0 ${numCells} ${numCells}`}
ref={ref}
role="img"
{...otherProps}
>
{!!title && <title>{title}</title>}
<path
fill={bgColor}
d={`M0,0 h${numCells}v${numCells}H0z`}
shapeRendering="crispEdges"
/>
<path fill={fgColor} d={fgPath} shapeRendering="crispEdges" />
{image}
</svg>
);
});

if (process.env.NODE_ENV !== 'production') {
QRCodeSVG.displayName = 'QRCodeSVG';
}

export { QRCodeSVG };
42 changes: 19 additions & 23 deletions src/hooks/useQRCode.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { QrCode, QrSegment } from '../libs/qrcodegen';
import type { ErrorCorrectionLevel, ImageSettings } from '../interface';
import { ERROR_LEVEL_MAP, getImageSettings, getMarginSize } from '../utils';
import { useMemo } from 'react';
import React from 'react';

export function useQRCode({
export const useQRCode = ({
value,
level,
minVersion,
Expand All @@ -19,29 +19,25 @@ export function useQRCode({
marginSize?: number;
imageSettings?: ImageSettings;
size: number;
}) {
const qrcode = useMemo(() => {
}) => {
const qrcode = React.useMemo<QrCode>(() => {
const segments = QrSegment.makeSegments(value);
return QrCode.encodeSegments(
segments,
ERROR_LEVEL_MAP[level],
minVersion,
);
return QrCode.encodeSegments(segments, ERROR_LEVEL_MAP[level], minVersion);
}, [value, level, minVersion]);

const { cells, margin, numCells, calculatedImageSettings } = useMemo(() => {
const cs = qrcode.getModules();

const mg = getMarginSize(includeMargin, marginSize);
const ncs = cs.length + mg * 2;
const cis = getImageSettings(cs, size, mg, imageSettings);
return {
cells: cs,
margin: mg,
numCells: ncs,
calculatedImageSettings: cis,
};
}, [qrcode, size, imageSettings, includeMargin, marginSize]);
const { cells, margin, numCells, calculatedImageSettings } =
React.useMemo(() => {
const cs = qrcode.getModules();
const mg = getMarginSize(includeMargin, marginSize);
const ncs = cs.length + mg * 2;
const cis = getImageSettings(cs, size, mg, imageSettings);
return {
cells: cs,
margin: mg,
numCells: ncs,
calculatedImageSettings: cis,
};
}, [qrcode, size, imageSettings, includeMargin, marginSize]);

return {
qrcode,
Expand All @@ -50,4 +46,4 @@ export function useQRCode({
numCells,
calculatedImageSettings,
};
}
};
55 changes: 28 additions & 27 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,32 @@ export type ERROR_LEVEL_MAPPED_TYPE = {
[index in ErrorCorrectionLevel]: Ecc;
};


export type ImageSettings = {
src: string;
height: number;
width: number;
excavate: boolean;
x?: number;
y?: number;
opacity?: number;
crossOrigin?: CrossOrigin;
};

export type QRProps = {
value: string;
size?: number;
level?: ErrorCorrectionLevel;
bgColor?: string;
fgColor?: string;
style?: CSSProperties;
includeMargin?: boolean;
marginSize?: number;
imageSettings?: ImageSettings;
title?: string;
minVersion?: number;
};
export type QRPropsCanvas = QRProps & React.CanvasHTMLAttributes<HTMLCanvasElement>;
export type QRPropsSVG = QRProps & React.SVGAttributes<SVGSVGElement>;

src: string;
height: number;
width: number;
excavate: boolean;
x?: number;
y?: number;
opacity?: number;
crossOrigin?: CrossOrigin;
};

export type QRProps = {
value: string;
size?: number;
level?: ErrorCorrectionLevel;
bgColor?: string;
fgColor?: string;
style?: CSSProperties;
includeMargin?: boolean;
marginSize?: number;
imageSettings?: ImageSettings;
title?: string;
minVersion?: number;
};

export type QRPropsCanvas = QRProps &
React.CanvasHTMLAttributes<HTMLCanvasElement>;

export type QRPropsSVG = QRProps & React.SVGAttributes<SVGSVGElement>;
Loading

0 comments on commit d85a6e7

Please sign in to comment.