最近接觸到的需求,前端生成一個帶企業logo的二維碼,並支持點擊下載它。javascript
在前面的文章有講到如何用 canvas 畫二維碼,在此基礎上再畫一個公司logo,並提供下載的方法供調用,再封裝成 npm 插件html
模塊名稱: qrcode-with-logos
前端
github地址:github.com/zxpsuper/qr…java
npm地址:www.npmjs.com/package/qrc…node
將整個封裝成一個 QrCodeWithLogo
類,並提供三個方法:webpack
interface IQrCodeWithLogo {
toCanvas(): Promise<any>;
toImage(): Promise<any>;
downloadImage(name: string): void;
}
class QrCodeWithLogo implements IQrCodeWithLogo {
option: BaseOptions;
constructor(option: BaseOptions) {
this.option = option;
return this;
}
toCanvas = () => {
return toCanvas.call(this, this.option);
};
toImage = () => {
return toImage.call(this, this.option);
};
downloadImage = (name: string) => {
saveImage(this.option.image, name);
};
}
複製代碼
此方法用到了庫qrcode
的toCanvas
方法git
export const toCanvas = (options: BaseOptions) => {
return renderQrCode(options)
.then(() => options)
.then(drawLogo);
};
複製代碼
這裏先用qrcode
庫畫出二維碼的canvasgithub
import QRCode = require("qrcode");
const toCanvas = promisify(QRCode.toCanvas);
export const renderQrCode = ({
canvas,
content,
width = 0,
nodeQrCodeOptions = {}
}: BaseOptions) => {
// 容錯率,默認對內容少的二維碼採用高容錯率,內容多的二維碼採用低容錯率
// according to the content length to choose different errorCorrectionLevel
nodeQrCodeOptions.errorCorrectionLevel =
nodeQrCodeOptions.errorCorrectionLevel || getErrorCorrectionLevel(content);
return getOriginWidth(content, nodeQrCodeOptions).then((_width: number) => {
// 獲得原始比例後還原至設定值,再放大4倍以獲取高清圖
// Restore to the set value according to the original ratio, and then zoom in 4 times to get the HD image.
nodeQrCodeOptions.scale = width === 0 ? undefined : (width / _width) * 4;
// @ts-ignore
return toCanvas(canvas, content, nodeQrCodeOptions);
});
};
複製代碼
promisify()是封裝的一個方法,用於減小return promise時的代碼,方便書寫web
export const promisify = (f: Function): Function => {
return function() {
const args = Array.prototype.slice.call(arguments);
return new Promise(function(resolve, reject) {
args.push(function(err: object, result: object) {
if (err) reject(err);
else resolve(result);
});
f.apply(null, args);
});
};
};
複製代碼
畫出canvas,緊接着判斷是否有logo, 若是有就畫logo,這裏有兩種模式:typescript
一種是直接畫圖 ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
,可拓展性不強。
一種是canvas疊加,使用 ctx.createPattern(canvasImage, "no-repeat");
能夠實現更多複雜的效果
export const drawLogo = ({ canvas, content, logo }: BaseOptions) => {
if (!logo) {
return;
}
// @ts-ignore
const canvasWidth = canvas.width;
const {
logoSize = 0.15,
borderColor = "#ffffff",
bgColor = borderColor || "#ffffff",
borderSize = 0.05,
crossOrigin,
borderRadius = 8,
logoRadius = 0
} = logo;
let logoSrc = typeof logo === "string" ? logo : logo.src;
let logoWidth = canvasWidth * logoSize;
let logoXY = (canvasWidth * (1 - logoSize)) / 2;
let logoBgWidth = canvasWidth * (logoSize + borderSize);
let logoBgXY = (canvasWidth * (1 - logoSize - borderSize)) / 2;
// @ts-ignore
const ctx = canvas.getContext("2d");
// logo 底色, draw logo background color
canvasRoundRect(ctx)(
logoBgXY,
logoBgXY,
logoBgWidth,
logoBgWidth,
borderRadius
);
ctx.fillStyle = bgColor;
ctx.fill();
// logo
const image = new Image();
if (crossOrigin || logoRadius) {
image.setAttribute("crossOrigin", crossOrigin || "anonymous");
}
image.src = logoSrc;
// 使用image繪製能夠避免某些跨域狀況
// Use image drawing to avoid some cross-domain situations
const drawLogoWithImage = (image: any) => {
ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
};
// 使用canvas繪製以得到更多的功能
// Use canvas to draw more features, such as borderRadius
const drawLogoWithCanvas = (image: any) => {
const canvasImage = document.createElement("canvas");
canvasImage.width = logoXY + logoWidth;
canvasImage.height = logoXY + logoWidth;
canvasImage
.getContext("2d")
.drawImage(image, logoXY, logoXY, logoWidth, logoWidth);
canvasRoundRect(ctx)(logoXY, logoXY, logoWidth, logoWidth, logoRadius);
ctx.fillStyle = ctx.createPattern(canvasImage, "no-repeat");
ctx.fill();
};
// 將 logo繪製到 canvas上
// Draw the logo on the canvas
return new Promise((resolve, reject) => {
image.onload = () => {
logoRadius ? drawLogoWithCanvas(image) : drawLogoWithImage(image);
resolve();
};
});
};
複製代碼
此方法利用以前的toCanvas()
方法,生成canvas後拿到 canvas.toDataURL()
的值,賦給<img>
的 src
便可。這裏,咱們加入了 download
downloadName
屬性用於下載,所以在 toImage()
方法中判斷,代碼以下:
export const toImage = (options: BaseOptions) => {
const canvas = document.createElement("canvas");
console.log("options", options);
options.canvas = canvas;
if (options.logo) {
if (isString(options.logo)) {
// @ts-ignore
options.logo = { src: options.logo };
}
// @ts-ignore
options.logo.crossOrigin = "Anonymous";
}
// @ts-ignore
return toCanvas(options).then(() => {
const { image = new Image(), downloadName = "qr-code" } = options;
let { download } = options;
// @ts-ignore
image.src = canvas.toDataURL();
if (download !== true && !isFunction(download)) {
return;
}
download = download === true ? (start: Function) => start() : download;
const startDownload: Function = () => {
saveImage(image, downloadName);
};
download && download(startDownload);
return new Promise((resolve, reject) => {
resolve();
});
});
};
export const saveImage = (image: Element, name: string) => {
// @ts-ignore
const dataURL = image.src;
const link = document.createElement("a");
link.download = name;
link.href = dataURL;
link.dispatchEvent(new MouseEvent("click"));
};
複製代碼
提供一個主動調用下載圖片的方法,傳入文件名name, 其中用到 saveImage()
方法,這個在 toImage()
中也有用到。
下載文件的流程是:生成一個<a>
標籤, 設置 href
值爲 image
的 src
值,download
屬性賦值文件名,而後給 <a>
主動一次點擊事件便可。
downloadImage = (name: string) => {
saveImage(this.option.image, name);
};
複製代碼
初次嘗試 typescript
, 用的不夠優美但無妨使用。 npm run build
構建出支持 umd 的文件,而後 npm login
, npm publish
便可。webpack 配置可查看 github 代碼。
下面是詳細代碼
<canvas id="canvas"></canvas> <img src="" alt="" id="image" />
<img id="image" alt="">
複製代碼
npm 模塊導入:
import QrCodeWithLogo from "qrcode-with-logos";
let qrcode = new QrCodeWithLogo({
canvas: document.getElementById("canvas"),
content: "https://github.com/zxpsuper",
width: 380,
// download: true,
image: document.getElementById("image"),
logo: {
src: "https://avatars1.githubusercontent.com/u/28730619?s=460&v=4"
}
});
qrcode.toCanvas().then(() => {
qrcode.toImage().then(() => {
setTimeout(() => {
qrcode.downloadImage("hello world");
}, 2000);
});
});
複製代碼
固然你也能夠<script>
引入使用
<script src="https://zxpsuper.github.io/qrcode-with-logos/dist/QRcode-with-logo.js"></script>
<script> let qrcode = new QrCodeWithLogo({ canvas: document.getElementById("canvas"), content: "https://github.com/zxpsuper", width: 380, // download: true, image: document.getElementById("image"), logo: { src: "https://avatars1.githubusercontent.com/u/28730619?s=460&v=4" } }); qrcode.toCanvas().then(() => { qrcode.toImage().then(() => { setTimeout(() => { qrcode.downloadImage("hello world"); }, 2000); }); }); </script>
複製代碼
That is all.