编写样式
react-qrbtf 提供了一些基本的API来帮助开发者在一定程度上制作属于自己的样式生成规则
本文以制作一个以圆形为主题的最简单的二维码样式来帮助读者了解编写二维码样式的基本流程
样式的基本结构
React推荐使用函数组件(Function Component),因此以下教程以编写函数组件为例
首先新建我们需要的组件文件 QRRound.js
import React from 'react';
import {defaultViewBox, RendererWrapper} from "react-qrbtf";
const QRRound = (props) => {
const { qrcode, className, styles } = props;
return (
<svg className={className} style={styles.svg} width="100%" height="100%" viewBox={defaultViewBox(qrcode)} fill="white"
xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
{listPoints(props)}
</svg>
);
}
function listPoints({ qrcode, size, dataColor }) {
return [];
}
export default RendererWrapper(QRRound);
如你所见,react-qrbtf的二维码样式最终的生成是对<svg>
相关标签(<rect>
<round>
等)组合
如果你还不太了解svg相关标签的基本参数和使用,可以参考文档:https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/svg
下面是对代码中出现的一些API的解释
defaultViewBox
: react-qrbtf默认对所有生成的二维码的四边有留白,如果您不喜欢,可以自行调整
RendererWrapper
: react-qrbtf提供的高阶组件(HOC),封装了根据value
计算qrcode
,styles
与defaultCSS
的合并等等
listPoints
: 实际上只是为了代码的结构性而独立设置出来的函数,他根据传入的props
来计算并返回一个包含<svg>
相关标签的数组用于显示
样式参数的默认值
由第一步listPoints
的参数列表可以看出,除了每一个样式生成都需要的qrcode
点阵信息外,这个样式还需要size
和dataColor
两个支持自定义的参数,同时,由于设置了style={styles.svg}
,我们也可以对样式组件默认的css样式进行调整
QRRound.defaultCSS = {
svg: {
width: '500px'
}
}
QRRound.defaultProps = {
styles: {},
size: 100,
dataColor: '#20ceb7'
}
如上,我们设置svg默认的宽度为500px,设置信息点的大小(size
)为100(取决于你下面如何使用),默认信息点的颜色为#20ceb7
生成二维码样式
最后的重头戏就是listPoints
的编写了
import {defaultViewBox, getTypeTable, QRPointType, RendererWrapper} from "react-qrbtf";
function listPoints({ qrcode, size, dataColor }) {
size = size / 100;
let id = 0;
const nCount = qrcode.getModuleCount();
const typeTable = getTypeTable(qrcode);
const pointList = [];
for (let x = 0; x < nCount; x++) {
for (let y = 0; y < nCount; y++) {
if (qrcode.isDark(x, y) == false) continue;
if (typeTable[x][y] == QRPointType.POS_CENTER) {
// 在3个定位点的中心生成同心的圆和环
pointList.push(<circle key={id++} fill="#000000" cx={x + 0.5} cy={y + 0.5} r={1.5} />)
pointList.push(<circle key={id++} fill="none" strokeWidth="1" stroke="#000000" cx={x + 0.5} cy={y + 0.5} r={3} />)
} else if (typeTable[x][y] == QRPointType.ALIGN_CENTER || typeTable[x][y] == QRPointType.ALIGN_OTHER || typeTable[x][y] == QRPointType.TIMING) {
// 特殊点使用黑色的圆点
pointList.push(<circle opacity={1.0} r={size / 2} key={id++} fill="#000000" cx={x + 0.5} cy={y + 0.5}/>)
} else if (typeTable[x][y] != QRPointType.POS_OTHER) {
// 其他的信息点使用dataColor色的圆点
pointList.push(<circle opacity={1.0} r={size / 2} key={id++} fill={dataColor} cx={x + 0.5} cy={y + 0.5}/>)
}
}
}
return pointList;
}
首先我们将 size = size / 100
这说明我们赋予size
的意思是相对于1
的百分比(当然也可以直接使用)
let id = 0;
React 要求我们在循环生成组件元素的时候为每个标签提供key
,这里就简单的用数字id
递增作为key
了
const nCount = qrcode.getModuleCount();
这里引用维基百科上对二维码版本的解释
QR码一共提供40种不同版本存储密度的结构,对应指示图的“版本信息”,版本1为21×21模块(模块为QR码中的最小单元),每增加一个版本,长宽各增加4个模块,最大的版本40为177×177模块。
所以这里nCount
就是计算出来的二维码的模块大小
const typeTable = getTypeTable(qrcode);
getTypeTable
对二维码进行逐点分类,返回一个记录了每一点类型的二维数组,具体的类型可以参考下图(之后的代码需要根据这个类型生成标签)
const pointList = [];
最后创建一个空的数组存储生成结果
for (let x = 0; x < nCount; x++) {
for (let y = 0; y < nCount; y++) {
if (qrcode.isDark(x, y) == false) continue;
if (typeTable[x][y] == QRPointType.POS_CENTER) {
// 在3个定位点的中心生成同心的圆和环
pointList.push(<circle key={id++} fill="#000000" cx={x + 0.5} cy={y + 0.5} r={1.5} />)
pointList.push(<circle key={id++} fill="none" strokeWidth="1" stroke="#000000" cx={x + 0.5} cy={y + 0.5} r={3}/>)
} else if (typeTable[x][y] == QRPointType.ALIGN_CENTER ||
typeTable[x][y] == QRPointType.ALIGN_OTHER ||
typeTable[x][y] == QRPointType.TIMING) {
// 特殊点使用黑色的圆点
pointList.push(<circle opacity={1.0} r={size / 2} key={id++} fill="#000000" cx={x + 0.5} cy={y + 0.5}/>)
} else if (typeTable[x][y] != QRPointType.POS_OTHER) {
// 其他的信息点使用dataColor色的圆点
pointList.push(<circle opacity={1.0} r={size / 2} key={id++} fill={dataColor} cx={x + 0.5} cy={y + 0.5}/>)
}
}
}
遍历点阵的每一点,根据这一点的类型生成不同的svg图像
QRPointType
是一个对不同点类型的枚举类型(Enum),类型名称可以对应上图 (CENTER
后缀代表中心,OTHER
后缀代表中心以外四周的点)
在加入JSX标签时,使用size
dataColor
等参数,就可以了
最后就能在其他组件中使用啦
<QRRound value={"https://qrbtf.com"} size={80} dataColor="#c95eb7"/>