QRCode
製作一款適合自己使用的QRCode插件,基於QRCode代碼更改
qrcode源碼:https://github.com/davidshimjs/qrcodejs
比起qrcode擴展的功能的有
- Css選擇器(Qrcode僅支持id選擇器)
- 二維碼背景顏色漸變
- 支持中心圖標(可調整圖片的大小)
QRCode是如何製作二維碼
三種方式:
-
Canvas :繪製成功後生成img標籤,可通過css控制樣式
-
SVG:
需添加useSVG:ture屬性,生成的二維碼是一張 svg 矢量圖 無img標籤
-
Table構造點陣
var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
// Drawing in DOM by using Table tag
var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () { ... }
這段代碼說的是先判斷是否支持SVG,如果支持則使用SVG;否則判斷是否支持Canvas,如果支持則使用Canvas,如果不支持,就使用Table來構造點陣
插件擴展功能是如何實現
兩種實現方式(只針對canvas生成的二維碼)
一種是純畫Canvas,沒有img標籤,所有的屬性只能通過代碼控制,無法通過css控制
一種是利用canvas生成兩張圖片,一張二維碼,一張中心圖標,中心圖標覆蓋在二維碼上方,可通過css控制
中心圖標的生成
可直接參考https://blog.csdn.net/chy555chy/article/details/85785819的文章
可通過iconWidth ,iconHeight設置icon的大小
但這個方法是純畫Canvas的
QRCode = function (el, vOption) {
const ratio = 0.3;
this._htOption = {
width : 256,
height : 256,
typeNumber : 4,
colorDark : "#000000",
colorLight : "#ffffff",
};
this._htOption.iconWidth=this._htOption.width * ratio;
this._htOption.iconHeight=this._htOption.width * ratio;
}
定義默認的圖標大小爲二維碼的三分之一,默認爲正方形的圖標
/**
* Draw the icon of QR code Center
* @param {QRCode} oQRCode
*/
Drawing.prototype.addIcon = async function (iconSrc) {
//通過ES6的async/await語法將異步的Promise轉爲同步方法
const image = await new Promise((resolve, reject) => {
const image = new Image();
image.src = iconSrc;
image.crossOrigin='Anonymous';
image.onload=()=>{resolve(image);};
});
const ratio = 0.3;//三分之二開始
const marginRatio = (1-ratio)/2;
const x = this._htOption.width * marginRatio;
const y = this._htOption.height * marginRatio;
const width = this._htOption.iconWidth ;
const height = this._htOption.iconHeight;
this._oContext.beginPath();
this._oContext.arc(x , y ,0, Math.PI * 3 / 2, Math.PI * 2);
this._oContext.lineTo(width + x, y);
this._oContext.arc(width + x, y,0, Math.PI * 3 / 2, Math.PI * 2);
this._oContext.lineTo(width + x, height + y );
this._oContext.arc(width + x, height + y,0, Math.PI * 3 / 2, Math.PI * 2);
this._oContext.lineTo( x, height +y);
this._oContext.arc(x, height + y,0, Math.PI * 3 / 2, Math.PI * 2);
this._oContext.closePath();
this._oContext.save();
this._oContext.clip();
this._oContext.drawImage(image, x, y, width, height);
this._oContext.restore();
};
但是上訴代碼有個問題
生成的圖標並非是位於二維碼的中心,而是從3分之2處開始
圖標位於中心需要重新定位x,y座標
//起始座標
const x = (this._htOption.width -this._htOption.iconWidth) / 2;
//終止座標
const y = (this._htOption.height - this._htOption.iconHeight) / 2;
顏色漸變
qrcode設置顏色的代碼如下
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
var bIsDark = oQRCode.isDark(row, col);
var nLeft = col * nWidth;
var nTop = row * nHeight;
//根據isDark的判斷繪製固定顏色
_oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
_oContext.lineWidth = 1;
_oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
//防滑處理 ...
}
}
因爲漸變是一個顏色,本來以爲需要計算每行每列的固定顏色,也就是說需要知道初始顏色,到末尾顏色的差值,然後除以每行每列變化的閾值,加註在初始顏色上,這樣每個像素的顏色纔會不同
這樣實現起來真的太麻煩,首先是#dddddd顏色16進制的運算,然後是方向,如果只有一個方向還好說,可以允許設置方向的,計算方式又不同
幸好!
畫布的筆觸是針對整體而言,並非如上訴代碼針對某個像素,可通過設置一個顏色漸變的筆觸達到效果
var gradient=ctx.createLinearGradient(0,0,170,0);
gradient.addColorStop("0","magenta");
gradient.addColorStop("0.5","blue");
gradient.addColorStop("1.0","red");
_oContext.strokeStyle = bIsDark ? gradient : _htOption.colorLight;
_oContext.lineWidth = 1;
_oContext.fillStyle = bIsDark ? gradient : _htOption.colorLight;
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
整理之後的代碼
可通過linearGradientDirection設置方向,linearGradientFrom起始顏色,linearGradientTo終止顏色,linearGradientMiddle中間顏色
方向常量
var QRGradientDirection = {
LEFT: 'to left',
RIGHT: 'to right',
TOP: 'to top',
TOPRIGHT:'to top right',
TOPLEFT:'to top left',
BOTTOM: 'to bottom',
BOTTOMRIGHT:'to bottom right',
BOTTOMLEFT:'to bottom left'
};
根據方向生成不同的漸變對象
/**
* Different gradient objects are generated according to the incoming gradient direction
* @constructor zoe
* @param directionType Gradient direction type
* @returns {CanvasGradient}
*/
Drawing.prototype.getDirection = function(directionType){
var _oContext = this._oContext;
var _htOption = this._htOption;
switch (directionType){
case QRGradientDirection.LEFT:
return _oContext.createLinearGradient(_htOption.width,0,0,0);
case QRGradientDirection.TOP:
return _oContext.createLinearGradient(0,_htOption.height,0,0);
case QRGradientDirection.TOPLEFT:
return _oContext.createLinearGradient(_htOption.width,_htOption.height,0,0);
case QRGradientDirection.TOPRIGHT:
return _oContext.createLinearGradient(0,_htOption.height,_htOption.width,0);
case QRGradientDirection.BOTTOM:
return _oContext.createLinearGradient(0,0,0,_htOption.height);
case QRGradientDirection.BOTTOMLEFT:
return _oContext.createLinearGradient(_htOption.width,0,0,_htOption.height);
case QRGradientDirection.BOTTOMRIGHT:
return _oContext.createLinearGradient(0,0,_htOption.width,_htOption.height);
case QRGradientDirection.RIGHT:
default:
return _oContext.createLinearGradient(0,0,_htOption.width,0);
}
默認爲黑色筆觸,如果配置linearGradient爲true 則 生成漸變筆觸
//Auther by zoe
//默認爲黑色
var gradient = _htOption.colorDark;
// 如果允許漸變
if(this._htOption.linearGradient){
var gradient=this.getDirection(_htOption.linearGradientDirection);
gradient.addColorStop("0",_htOption.linearGradientFrom);
gradient.addColorStop("1.0",_htOption.linearGradientTo);
if(_htOption.linearGradientMiddle){
gradient.addColorStop("0.5",_htOption.linearGradientMiddle);
}
}
// 純色二維碼
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
var bIsDark = oQRCode.isDark(row, col);
var nLeft = col * nWidth;
var nTop = row * nHeight;
_oContext.strokeStyle = bIsDark ? gradient : _htOption.colorLight;
_oContext.lineWidth = 1;
_oContext.fillStyle = bIsDark ? gradient : _htOption.colorLight;
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
// 안티 앨리어싱 방지 처리 防滑處理
_oContext.strokeRect(
Math.floor(nLeft) + 0.5,
Math.floor(nTop) + 0.5,
nRoundedWidth,
nRoundedHeight
);
_oContext.strokeRect(
Math.ceil(nLeft) - 0.5,
Math.ceil(nTop) - 0.5,
nRoundedWidth,
nRoundedHeight
);
}
}
代碼可通過npm i peachill-qrcode下載