背景
最近开发了一个应用包管理平台,主要提供给内部人员使用,可以扫码下载App安装包,因为还要平台外的分发,所以需要提供个二维码保存的功能。
分析
针对网页元素保存为图片,我可以用的是html2canvas,二维码生成我原来用的qrcode-react 库,但是发现这个图无法绘制到Canvas中,且这个库没有提供生成图片的回调,因此要实现我的功能只能二次开发下这个库了。
开始
生成二维码并回传图片信息
建一个二维码处理组件,可以看到依赖qr.js:
npm install qr.js
安装好后,创建文件 qrcode-reactjs.js
'use strict'
var React = require('react');
var PropTypes = require('prop-types');
var ReactDOM = require('react-dom');
var qr = require('qr.js');
function getBackingStorePixelRatio(ctx) {
return (
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1
);
}
var getDOMNode;
if (/^0\.14/.test(React.version)) {
getDOMNode = function(ref) {
return ref;
}
} else {
getDOMNode = function(ref) {
return ReactDOM.findDOMNode(ref);
}
}
class QRCode extends React.Component {
shouldComponentUpdate(nextProps) {
var that = this;
return Object.keys(QRCode.propTypes).some(function(k) {
return that.props[k] !== nextProps[k];
});
}
componentDidMount() {
this.update();
}
componentDidUpdate() {
this.update();
}
utf16to8(str) {
var out, i, len, c;
out = "";
len = str.length;
for (i = 0; i < len; i++) {
c = str.charCodeAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out += str.charAt(i);
} else if (c > 0x07FF) {
out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
} else {
out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
}
}
return out;
}
update() {
var value = this.utf16to8(this.props.value);
var qrcode = qr(value);
var canvas = getDOMNode(this.refs.canvas);
var ctx = canvas.getContext('2d');
var cells = qrcode.modules;
var tileW = this.props.size / cells.length;
var tileH = this.props.size / cells.length;
var scale = (window.devicePixelRatio || 1) / getBackingStorePixelRatio(ctx);
canvas.height = canvas.width = this.props.size * scale;
ctx.scale(scale, scale);
cells.forEach(function(row, rdx) {
row.forEach(function(cell, cdx) {
ctx.fillStyle = cell ? this.props.fgColor : this.props.bgColor;
var w = (Math.ceil((cdx + 1) * tileW) - Math.floor(cdx * tileW));
var h = (Math.ceil((rdx + 1) * tileH) - Math.floor(rdx * tileH));
ctx.fillRect(Math.round(cdx * tileW), Math.round(rdx * tileH), w, h);
}, this);
}, this);
if (this.props.logo) {
var self = this
var size = this.props.size;
var image = document.createElement('img');
image.src = this.props.logo;
image.onload = function() {
var dwidth = self.props.logoWidth || size * 0.2;
var dheight = self.props.logoHeight || image.height / image.width * dwidth;
var dx = (size - dwidth) / 2;
var dy = (size - dheight) / 2;
image.width = dwidth;
image.height = dheight;
console.log('dwidth', dwidth)
console.log('dheight', dheight)
ctx.drawImage(image, dx, dy, dwidth, dheight);
}
}
if (this.props.onCanvasLoad){
this.props.onCanvasLoad(canvas.toDataURL('image/png'))
}
}
render() {
return React.createElement('canvas', {
style: { height: this.props.size, width: this.props.size },
height: this.props.size,
width: this.props.size,
ref: 'canvas',
});
}
}
QRCode.propTypes = {
value: PropTypes.string.isRequired,
size: PropTypes.number,
bgColor: PropTypes.string,
fgColor: PropTypes.string,
logo: PropTypes.string,
logoWidth: PropTypes.number,
logoHeight: PropTypes.number,
onCanvasLoad: PropTypes.func,
};
QRCode.defaultProps = {
size: 128,
bgColor: '#FFFFFF',
fgColor: '#000000',
value: 'http://facebook.github.io/react/'
};
module.exports = QRCode;
调用方式
import QRCode from '@/utils/qrcode-reactjs'
this.handleImageLoad(dataUrl)} />
根据Base64内容创建图片Dom
这里做了个1秒延迟,防止二维码初始化时的报错,图片加载后注意把原来的QRCode节点隐藏。
handleImageLoad = dataUrl => {
const { imgUrl } = this.state;
if (imgUrl !== dataUrl) {
setTimeout(() => {
this.setState({
imgUrl: dataUrl,
})
}, 1000)
}
}
{imgUrl && ( )}
保存图片到本地
保存元素到图片我用的html2canvas
,支持保存整个页面,也支持保存指定的元素Dom。我们实际只需要二维码和下面的描述信息。所以直接给需要的元素加上ref
即可,也可以给个id,自己查找下。
handleSaveImg = packageName => {
html2canvas(this.qrCodeImg, { allowTaint: true, useCORS: true }).then(canvas => {
const a = document.createElement('a');
a.href = canvas.toDataURL('image/png');
a.download = `${packageName}_${new Date().getTime().toString()}`;
a.click();
});
}
{ this.qrCodeImg = node }} className={styles.flavorList} >
...
结语
至此功能已经完成了,需要的试试吧。