UNPKG

react-native-qrcode-styled

Version:

A fully customizable QR Code generator for React Native based on react-native-svg and javascript-qrcode.

505 lines (454 loc) 16.4 kB
import { INNER_EYE_SIZE_IN_BITS, EYES_POSITIONS, OUTER_EYE_SIZE_IN_BITS } from './constants'; import type { AllEyesOptions, Bit, BitArray, BitMatrix, BorderRadius, EyeOptions, EyePosition, CornerType, } from './types'; export function transformBitArrayToMatrix(bitArray: BitArray, qrCodeSize: number): BitMatrix { const matrix: BitArray[] = []; let row: BitArray = []; for (let i: Bit = 0; i < bitArray.length; i++) { row.push(bitArray[i] || 0); if ((i + 1) % qrCodeSize === 0) { matrix.push([...row]); row = []; } } return matrix; } export function transformBorderRadiusToArray(borderRadius?: BorderRadius): number[] | undefined { if (!borderRadius) { return undefined; } if (Array.isArray(borderRadius)) { return borderRadius.length === 0 ? undefined : borderRadius; } return Array.from({ length: 4 }, () => borderRadius); } export function transformEyeOptionsToCommonPattern( options?: EyeOptions | AllEyesOptions ): AllEyesOptions | undefined { if (!options) { return undefined; } if (Object.keys(options).find((key) => EYES_POSITIONS.includes(key))) { return options as AllEyesOptions; } return EYES_POSITIONS.reduce((res, position) => ({ ...res, [position]: options }), {}); } // x, y - indexes in matrix export function getPieceSquarePathData(x: number, y: number, size: number): string { const _x = x * size; const _y = y * size; return ` M${_x} ${_y} ${_x + size} ${_y} ${_x + size} ${_y + size} ${_x} ${_y + size} z `; } // x, y - indexes in matrix export function getPieceRoundedSquarePathData({ x, y, size, cornerType, borderRadius, isGlued, isLiquid, bitMatrix, }: { x: number; y: number; size: number; cornerType?: CornerType; borderRadius?: number[]; isGlued?: boolean; isLiquid?: boolean; bitMatrix: BitMatrix; }): string { const _x = x * size; const _y = y * size; const isCornerTypeCut = cornerType === 'cut'; let [topLeftR = 0, topRightR = 0, bottomRightR = 0, bottomLeftR = 0] = borderRadius || []; const generateArcStart = (cornerPosition: number) => isCornerTypeCut ? 'L' : `A${cornerPosition} ${cornerPosition} 0 0 1`; // check for surrounding pieces & remove related corner border radius if (isGlued) { if (bitMatrix[y]?.[x - 1] === 1) { topLeftR = 0; bottomLeftR = 0; } if (bitMatrix[y - 1]?.[x] === 1) { topLeftR = 0; topRightR = 0; } if (bitMatrix[y]?.[x + 1] === 1) { topRightR = 0; bottomRightR = 0; } if (bitMatrix[y + 1]?.[x] === 1) { bottomLeftR = 0; bottomRightR = 0; } } if (isLiquid) { if (bitMatrix[y - 1]?.[x - 1] === 1) { topLeftR = 0; } if (bitMatrix[y - 1]?.[x + 1] === 1) { topRightR = 0; } if (bitMatrix[y + 1]?.[x + 1] === 1) { bottomRightR = 0; } if (bitMatrix[y + 1]?.[x - 1] === 1) { bottomLeftR = 0; } } // render svg if we have list of different border radius return ` M${_x} ${_y + topLeftR} ${generateArcStart(topLeftR)} ${_x + topLeftR} ${_y} L${_x + size - topRightR} ${_y} ${generateArcStart(topRightR)} ${_x + size} ${_y + topRightR} L${_x + size} ${_y + size - bottomRightR} ${generateArcStart(bottomRightR)} ${_x + size - bottomRightR} ${_y + size} L${_x + bottomLeftR} ${_y + size} ${generateArcStart(bottomLeftR)} ${_x} ${_y + size - bottomLeftR} z `; } export function getPieceLiquidPathData( x: number, y: number, size: number, borderRadius: number ): string { const _x = x * size; const _y = y * size; const r = borderRadius > size ? size : borderRadius; return ` M${_x} ${_y} L${_x + r} ${_y} A${r} ${r} 0 0 0 ${_x} ${_y + r} z`; } export function getOuterEyePathData( position: EyePosition, pieceSize: number, qrSize: number ): string { const outerEyeSize = OUTER_EYE_SIZE_IN_BITS * pieceSize; if (position === 'topLeft') { return ` M0 0 ${outerEyeSize} 0 ${outerEyeSize} ${outerEyeSize} 0 ${outerEyeSize} z M${pieceSize} ${pieceSize} ${outerEyeSize - pieceSize} ${pieceSize} ${outerEyeSize - pieceSize} ${outerEyeSize - pieceSize} ${pieceSize} ${outerEyeSize - pieceSize} z `; } if (position === 'topRight') { return ` M${qrSize - outerEyeSize} 0 ${qrSize} 0 ${qrSize} ${outerEyeSize} ${qrSize - outerEyeSize} ${outerEyeSize} z M${qrSize - outerEyeSize + pieceSize} ${pieceSize} ${qrSize - pieceSize} ${pieceSize} ${qrSize - pieceSize} ${outerEyeSize - pieceSize} ${qrSize - outerEyeSize + pieceSize} ${outerEyeSize - pieceSize} z `; } if (position === 'bottomLeft') { return ` M0 ${qrSize - outerEyeSize} ${outerEyeSize} ${qrSize - outerEyeSize} ${outerEyeSize} ${qrSize} 0 ${qrSize} z M${pieceSize} ${qrSize - outerEyeSize + pieceSize} ${outerEyeSize - pieceSize} ${qrSize - outerEyeSize + pieceSize} ${outerEyeSize - pieceSize} ${qrSize - pieceSize} ${pieceSize} ${qrSize - pieceSize} z `; } return ''; } export function getRoundedOuterEyePathData( position: EyePosition, borderRadius: number[], pieceSize: number, qrSize: number ): string { const outerEyeSize = OUTER_EYE_SIZE_IN_BITS * pieceSize; let [topLeftR = 0, topRightR = 0, bottomRightR = 0, bottomLeftR = 0] = borderRadius || []; let topLeftInnerR = pieceSize < topLeftR ? topLeftR - pieceSize : 0; let topRightInnerR = pieceSize < topRightR ? topRightR - pieceSize : 0; let bottomRightInnerR = pieceSize < bottomRightR ? bottomRightR - pieceSize : 0; let bottomLeftInnerR = pieceSize < bottomLeftR ? bottomLeftR - pieceSize : 0; if (position === 'topLeft') { return ` M0 ${topLeftR} A${topLeftR} ${topLeftR} 0 0 1 ${topLeftR} 0 L${outerEyeSize - topRightR} 0 A${topRightR} ${topRightR} 0 0 1 ${outerEyeSize} ${topRightR} L${outerEyeSize} ${outerEyeSize - bottomRightR} A${bottomRightR} ${bottomRightR} 0 0 1 ${outerEyeSize - bottomRightR} ${outerEyeSize} L${bottomLeftR} ${outerEyeSize} ${bottomLeftR ? `A${bottomLeftR} ${bottomLeftR} 0 0 1 0 ${outerEyeSize - bottomLeftR}` : ''} z M${pieceSize} ${pieceSize + topLeftInnerR} A${topLeftInnerR} ${topLeftInnerR} 0 0 1 ${pieceSize + topLeftInnerR} ${pieceSize} L${outerEyeSize - pieceSize - topRightInnerR} ${pieceSize} A${topRightInnerR} ${topRightInnerR} 0 0 1 ${outerEyeSize - pieceSize} ${ pieceSize + topRightInnerR } L${outerEyeSize - pieceSize} ${outerEyeSize - pieceSize - bottomRightInnerR} A${bottomRightInnerR} ${bottomRightInnerR} 0 0 1 ${ outerEyeSize - pieceSize - bottomRightInnerR } ${outerEyeSize - pieceSize} L${pieceSize + bottomLeftInnerR} ${outerEyeSize - pieceSize} A${bottomLeftInnerR} ${bottomLeftInnerR} 0 0 1 ${pieceSize} ${ outerEyeSize - pieceSize - bottomLeftInnerR } z `; } if (position === 'topRight') { return ` M${qrSize - outerEyeSize} ${topLeftR} ${topLeftR ? `A${topLeftR} ${topLeftR} 0 0 1 ${qrSize - outerEyeSize + topLeftR} 0` : ''} L${qrSize - topRightR} 0 ${topRightR ? `A${topRightR} ${topRightR} 0 0 1 ${qrSize} ${topRightR}` : ''} L${qrSize} ${outerEyeSize - bottomRightR} A${bottomRightR} ${bottomRightR} 0 0 1 ${qrSize - bottomRightR} ${outerEyeSize} L${qrSize - outerEyeSize + bottomLeftR} ${outerEyeSize} A${bottomLeftR} ${bottomLeftR} 0 0 1 ${qrSize - outerEyeSize} ${outerEyeSize - bottomLeftR} z M${qrSize - outerEyeSize + pieceSize} ${pieceSize + topLeftInnerR} A${topLeftInnerR} ${topLeftInnerR} 0 0 1 ${ qrSize - outerEyeSize + pieceSize + topLeftInnerR } ${pieceSize} L${qrSize - pieceSize - topRightInnerR} ${pieceSize} A${topRightInnerR} ${topRightInnerR} 0 0 1 ${qrSize - pieceSize} ${pieceSize + topRightInnerR} L${qrSize - pieceSize} ${outerEyeSize - pieceSize - bottomRightInnerR} A${bottomRightInnerR} ${bottomRightInnerR} 0 0 1 ${qrSize - pieceSize - bottomRightInnerR} ${ outerEyeSize - pieceSize } L${qrSize - outerEyeSize + pieceSize + bottomLeftInnerR} ${outerEyeSize - pieceSize} A${bottomLeftInnerR} ${bottomLeftInnerR} 0 0 1 ${qrSize - outerEyeSize + pieceSize} ${ outerEyeSize - pieceSize - bottomLeftInnerR } z `; } if (position === 'bottomLeft') { return ` M0 ${qrSize - outerEyeSize + topLeftR} A${topLeftR} ${topLeftR} 0 0 1 ${topLeftR} ${qrSize - outerEyeSize} L${outerEyeSize - topRightR} ${qrSize - outerEyeSize} A${topRightR} ${topRightR} 0 0 1 ${outerEyeSize} ${qrSize - outerEyeSize + topRightR} L${outerEyeSize} ${qrSize - bottomRightR} A${bottomRightR} ${bottomRightR} 0 0 1 ${outerEyeSize - bottomRightR} ${qrSize} L${bottomLeftR} ${qrSize} A${bottomLeftR} ${bottomLeftR} 0 0 1 0 ${qrSize - bottomLeftR} z M${pieceSize} ${qrSize - outerEyeSize + pieceSize + topLeftInnerR} A${topLeftInnerR} ${topLeftInnerR} 0 0 1 ${pieceSize + topLeftInnerR} ${ qrSize - outerEyeSize + pieceSize } L${outerEyeSize - pieceSize - topRightInnerR} ${qrSize - outerEyeSize + pieceSize} A${topRightInnerR} ${topRightInnerR} 0 0 1 ${outerEyeSize - pieceSize} ${ qrSize - outerEyeSize + pieceSize + topRightInnerR } L${outerEyeSize - pieceSize} ${qrSize - pieceSize - bottomRightInnerR} A${bottomRightInnerR} ${bottomRightInnerR} 0 0 1 ${ outerEyeSize - pieceSize - bottomRightInnerR } ${qrSize - pieceSize} L${pieceSize + bottomLeftInnerR} ${qrSize - pieceSize} A${bottomLeftInnerR} ${bottomLeftInnerR} 0 0 1 ${pieceSize} ${ qrSize - pieceSize - bottomLeftInnerR } z `; } return ''; } export function getInnerEyePathData( position: EyePosition, pieceSize: number, qrSize: number ): string { const outerSize = OUTER_EYE_SIZE_IN_BITS * pieceSize; const innerSize = INNER_EYE_SIZE_IN_BITS * pieceSize; const offset = 2 * pieceSize; if (position === 'topLeft') { return ` M${offset} ${offset} ${offset + innerSize} ${offset} ${offset + innerSize} ${offset + innerSize} ${offset} ${offset + innerSize} z `; } if (position === 'topRight') { return ` M${qrSize - outerSize + offset} ${offset} ${qrSize - offset} ${offset} ${qrSize - offset} ${offset + innerSize} ${qrSize - outerSize + offset} ${offset + innerSize} z `; } if (position === 'bottomLeft') { return ` M${offset} ${qrSize - outerSize + offset} ${offset + innerSize} ${qrSize - outerSize + offset} ${offset + innerSize} ${qrSize - offset} ${offset} ${qrSize - offset} z `; } return ''; } export function getRoundedInnerEyePathData( position: EyePosition, borderRadius: number[], pieceSize: number, qrSize: number ): string { const outerSize = OUTER_EYE_SIZE_IN_BITS * pieceSize; const innerSize = INNER_EYE_SIZE_IN_BITS * pieceSize; const offset = 2 * pieceSize; const [topLeftR = 0, topRightR = 0, bottomRightR = 0, bottomLeftR = 0] = borderRadius || []; if (position === 'topLeft') { return ` M${offset} ${offset + topLeftR} A${topLeftR} ${topLeftR} 0 0 1 ${offset + topLeftR} ${offset} L${offset + innerSize - topRightR} ${offset} A${topRightR} ${topRightR} 0 0 1 ${offset + innerSize} ${offset + topRightR} L${offset + innerSize} ${offset + innerSize - bottomRightR} A${bottomRightR} ${bottomRightR} 0 0 1 ${offset + innerSize - bottomRightR} ${ offset + innerSize } L${offset + bottomLeftR} ${offset + innerSize} A${bottomLeftR} ${bottomLeftR} 0 0 1 ${offset} ${offset + innerSize - bottomLeftR} z `; } if (position === 'topRight') { return ` M${qrSize - outerSize + offset} ${offset + topLeftR} A${topLeftR} ${topLeftR} 0 0 1 ${qrSize - outerSize + offset + topLeftR} ${offset} L${qrSize - offset - topRightR} ${offset} A${topRightR} ${topRightR} 0 0 1 ${qrSize - offset} ${offset + topRightR} L${qrSize - offset} ${offset + innerSize - bottomRightR} A${bottomRightR} ${bottomRightR} 0 0 1 ${qrSize - offset - bottomRightR} ${offset + innerSize} L${qrSize - outerSize + offset + bottomLeftR} ${offset + innerSize} A${bottomLeftR} ${bottomLeftR} 0 0 1 ${qrSize - outerSize + offset} ${ offset + innerSize - bottomLeftR } z `; } if (position === 'bottomLeft') { return ` M${offset} ${qrSize - outerSize + offset + topLeftR} A${topLeftR} ${topLeftR} 0 0 1 ${offset + topLeftR} ${qrSize - outerSize + offset} L${offset + innerSize - topRightR} ${qrSize - outerSize + offset} A${topRightR} ${topRightR} 0 0 1 ${offset + innerSize} ${ qrSize - outerSize + offset + topRightR } L${offset + innerSize} ${qrSize - offset - bottomRightR} A${bottomRightR} ${bottomRightR} 0 0 1 ${offset + innerSize - bottomRightR} ${qrSize - offset} L${offset + bottomLeftR} ${qrSize - offset} A${bottomLeftR} ${bottomLeftR} 0 0 1 ${offset} ${qrSize - offset - bottomLeftR} z `; } return ''; } export function isLiquidPieceInEyes(x: number, y: number, qrSize: number): boolean { return ( // top left square (x >= 1 && x < 6 && y >= 1 && y < 6) || // top right square (x >= qrSize - 6 && x < qrSize && y >= 1 && y < 6) || // bottom left square (x >= 1 && x < 6 && y >= qrSize - 6 && y < qrSize) ); } export function isCoordsOfTopLeftOuterEye(x: number, y: number): boolean { return ( (x >= 0 && x < OUTER_EYE_SIZE_IN_BITS && y === 0) || (x >= 0 && x < OUTER_EYE_SIZE_IN_BITS && y === OUTER_EYE_SIZE_IN_BITS - 1) || (y > 0 && y < OUTER_EYE_SIZE_IN_BITS - 1 && x === 0) || (y > 0 && y < OUTER_EYE_SIZE_IN_BITS - 1 && x === OUTER_EYE_SIZE_IN_BITS - 1) ); } export function isCoordsOfTopRightOuterEye(x: number, y: number, qrSize: number): boolean { return ( (x >= qrSize - OUTER_EYE_SIZE_IN_BITS && x < qrSize && y === 0) || (x >= qrSize - OUTER_EYE_SIZE_IN_BITS && x < qrSize && y === OUTER_EYE_SIZE_IN_BITS - 1) || (y > 0 && y < OUTER_EYE_SIZE_IN_BITS - 1 && x === qrSize - OUTER_EYE_SIZE_IN_BITS) || (y > 0 && y < OUTER_EYE_SIZE_IN_BITS - 1 && x === qrSize - 1) ); } export function isCoordsOfBottomLeftOuterEye(x: number, y: number, qrSize: number): boolean { return ( (x >= 0 && x < OUTER_EYE_SIZE_IN_BITS && y === qrSize - OUTER_EYE_SIZE_IN_BITS) || (x >= 0 && x < OUTER_EYE_SIZE_IN_BITS && y === qrSize - 1) || (y > qrSize - OUTER_EYE_SIZE_IN_BITS && y < qrSize && x === 0) || (y > qrSize - OUTER_EYE_SIZE_IN_BITS && y < qrSize && x === OUTER_EYE_SIZE_IN_BITS - 1) ); } // x, y is amount of matrix bits export function isCoordsOfOuterEyes(x: number, y: number, qrSize: number): boolean { return ( // top left square isCoordsOfTopLeftOuterEye(x, y) || // top right square isCoordsOfTopRightOuterEye(x, y, qrSize) || // bottom left square isCoordsOfBottomLeftOuterEye(x, y, qrSize) ); } export function isCoordsOfTopLeftInnerEye(x: number, y: number): boolean { return x >= 2 && x < INNER_EYE_SIZE_IN_BITS + 2 && y >= 2 && y < INNER_EYE_SIZE_IN_BITS + 2; } export function isCoordsOfTopRightInnerEye(x: number, y: number, qrSize: number): boolean { return ( x >= qrSize - OUTER_EYE_SIZE_IN_BITS + 2 && x < qrSize - 2 && y >= 2 && y < INNER_EYE_SIZE_IN_BITS + 2 ); } export function isCoordsOfBottomLeftInnerEye(x: number, y: number, qrSize: number): boolean { return ( x >= 2 && x < INNER_EYE_SIZE_IN_BITS + 2 && y >= qrSize - OUTER_EYE_SIZE_IN_BITS + 2 && y < qrSize - 2 ); } // x, y is amount of matrix bits export function isCoordsOfInnerEyes(x: number, y: number, qrSize: number): boolean { return ( // top left square isCoordsOfTopLeftInnerEye(x, y) || // top right square isCoordsOfTopRightInnerEye(x, y, qrSize) || // bottom left square isCoordsOfBottomLeftInnerEye(x, y, qrSize) ); } export function consoleWarn(message: string | unknown) { console.warn('QRCode warning: ' + message); } export function consoleError(message: string | unknown) { console.error('QRCode error: ' + message); }