Skip to content

Commit

Permalink
Merge pull request rosskhanas#218 from strindhaug/feature/square-pixe…
Browse files Browse the repository at this point in the history
…ls-and-viewBox

Make the code readable at any size + dom size optimization
  • Loading branch information
rosskhanas committed Dec 11, 2022
2 parents 1c9a792 + fafaf1e commit 7a9e03b
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 34 deletions.
6 changes: 2 additions & 4 deletions src/components/QRCodeCell/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import React from "react";
const propTypes = {
d: PropTypes.string.isRequired,
fill: PropTypes.string.isRequired,
transformX: PropTypes.number.isRequired,
transformY: PropTypes.number.isRequired,
};

const defaultProps = {};

const QRCodeCell = ({ d, fill, transformX, transformY }) => (
<path d={d} fill={fill} transform={`matrix(${[1, 0, 0, 1, transformX, transformY]})`} />
const QRCodeCell = ({ d, fill }) => (
<path d={d} fill={fill} />
);

QRCodeCell.propTypes = propTypes;
Expand Down
4 changes: 1 addition & 3 deletions src/components/QRCodeCell/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import { Path } from "react-native-svg";
const propTypes = {
d: PropTypes.string.isRequired,
fill: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
transformX: PropTypes.number.isRequired,
transformY: PropTypes.number.isRequired,
};

const defaultProps = {};

const QRCodeCell = ({ d, fill, transformX, transformY }) => <Path d={d} fill={fill} x={transformX} y={transformY} />;
const QRCodeCell = ({ d, fill }) => <Path d={d} fill={fill} />;

QRCodeCell.propTypes = propTypes;
QRCodeCell.defaultProps = defaultProps;
Expand Down
14 changes: 12 additions & 2 deletions src/components/QRCodeSurface/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import React, { forwardRef } from "react";
const propTypes = {
children: PropTypes.array.isRequired,
size: PropTypes.number.isRequired,
dataSize: PropTypes.number.isRequired,
bgColor: PropTypes.string.isRequired,
title: PropTypes.string,
xmlns: PropTypes.string,
};
Expand All @@ -13,9 +15,17 @@ const defaultProps = {
xmlns: "http://www.w3.org/2000/svg",
};

const QRCodeSurface = forwardRef(({ children, size, title, xmlns, ...props }, ref) => (
<svg {...props} height={size} ref={ref} width={size} xmlns={xmlns}>
const QRCodeSurface = forwardRef(({ bgColor, dataSize, size, title, xmlns, children, ...props }, ref) => (
<svg
{...props}
ref={ref}
height={size}
width={size}
viewBox={`0 0 ${dataSize} ${dataSize}`}
xmlns={xmlns}
>
{title ? <title>{title}</title> : null}
<rect x={0} y={0} width={dataSize} height={dataSize} fill={bgColor}/>
{children}
</svg>
));
Expand Down
16 changes: 13 additions & 3 deletions src/components/QRCodeSurface/index.native.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import PropTypes from "prop-types";
import React, { forwardRef } from "react";
import { Svg } from "react-native-svg";
import { Svg, Rect } from "react-native-svg";

const propTypes = {
children: PropTypes.array.isRequired,
size: PropTypes.number.isRequired,
dataSize: PropTypes.number.isRequired,
bgColor: PropTypes.string.isRequired,
};

const defaultProps = {};

const QRCodeSurface = forwardRef(({ children, size, ...props }, ref) => (
<Svg {...props} height={size} ref={ref} style={{ height: size, width: size }} width={size}>
const QRCodeSurface = forwardRef(({ bgColor, dataSize, size, children, ...props }, ref) => (
<Svg
{...props}
ref={ref}
height={size}
width={size}
style={{ height: size, width: size }}
viewBox={`0 0 ${dataSize} ${dataSize}`}
>
<Rect x={0} y={0} width={dataSize} height={dataSize} fill={bgColor}/>
{children}
</Svg>
));
Expand Down
45 changes: 23 additions & 22 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,39 @@ const defaultProps = {
size: 256,
};

function makePath(cells) {
/** How each cell is drawn:
*
* M x y // absolute move to x and y coordinate
* l 1 0 // relative line to x+1
* 0 1 // relative line to y+1
* -1 0 // relative line to x-1
* Z // close path
*/
return cells.map((row, rowIndex) =>
row.map((cell, cellIndex) => {
const posX = cellIndex
const posY = rowIndex
return cell ? `M ${posX} ${posY} l 1 0 0 1 -1 0 Z` : "" // Return nothing if empty pixel
}).join(" "),
).join(" ")
}

const QRCode = forwardRef(({ bgColor, fgColor, level, size, value, ...props }, ref) => {
// We'll use type === -1 to force QRCode to automatically pick the best type.
const qrcode = new QRCodeImpl(-1, ErrorCorrectLevel[level]);
qrcode.addData(value);
qrcode.make();
const cells = qrcode.modules;
const tileSize = size / cells.length;
return (
<QRCodeSurface {...props} size={size} ref={ref}>
{cells.map((row, rowIndex) =>
row.map((cell, cellIndex) => {
const transformX = Math.round(cellIndex * tileSize);
const transformY = Math.round(rowIndex * tileSize);
const qrItemWidth = Math.round((cellIndex + 1) * tileSize) - transformX;
const qrItemHeight = Math.round((rowIndex + 1) * tileSize) - transformY;
return (
<QRCodeCell
/* eslint-disable react/no-array-index-key */
key={`rectangle-${rowIndex}-${cellIndex}`}
/* eslint-enable react/no-array-index-key */
d={`M 0 0 L ${qrItemWidth} 0 L ${qrItemWidth} ${qrItemHeight} L 0 ${qrItemHeight} Z`}
fill={cell ? fgColor : bgColor}
transformX={transformX}
transformY={transformY}
/>
);
})
)}
<QRCodeSurface {...props} size={size} ref={ref} dataSize={cells.length} bgColor={bgColor}>
<QRCodeCell
d={makePath(cells)}
fill={fgColor}
/>
</QRCodeSurface>
);
});

QRCode.displayName = "QRCode";
QRCode.propTypes = propTypes;
QRCode.defaultProps = defaultProps;
Expand Down

0 comments on commit 7a9e03b

Please sign in to comment.