项目需求
租车场景:需要用户使用微信扫一扫直接打开小程序,携带车辆参数。
界面展示
前景颜色就是文字的样子,背景颜色就是整体背景色、把正方的太阳码图片裁剪成圆形方法中有实现过程(注:我没有看详细看文档不知道能否生成圆形的图片,我的后端是给我我一个正方的╮(╯▽╰)╭ )。
整体架构流程
前端使用到了jszip、file-saver俩个包需要要自行下载
由于这是服务器那边去微信换取多个二维码(太阳码),然后返回过来再处理。所以取到的值就是一个太阳码的base64格式的字符串。
我这里是传给后端数量,后端生成好统一返还过来了。如果您的后端告你需要一个个换取的话,获取哪里自行改成循环即可,也很简单。
由于太阳码只能小程序调用接口生成,如果想要自己生成的二维码的话,自行下载qrcode然后根据参数自行生成二维码,循环即可。
上代码
弹框的样式代码
<!-- 二维码下载前设置弹框 -->
<el-dialog
:title="title"
:visible.sync="openDataScope"
width="1000px"
append-to-body
center
>
<el-row>
<el-col :span="12">
<div class="qrcode_title">二维码预览</div>
<div class="qrcode_box">
<img
class="qrcode_canvas"
id="qrcode_canvas"
ref="qrcode_canvas"
alt="二维码"
/>
<canvas
v-show="false"
:width="qrSize"
:height="qrSize"
class="canvas"
ref="canvas"
></canvas>
</div>
</el-col>
<el-col :span="12">
<div class="qrcode_title">设置</div>
<el-form :model="codeForm" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="前景颜色">
<el-color-picker
@change="setColor"
v-model="codeForm.frontColor"
></el-color-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="背景颜色">
<el-color-picker
@change="setColor"
v-model="codeForm.backColor"
></el-color-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-form-item label="下载数量">
<el-input-number
v-model="codeForm.num"
:min="1"
:max="100"
label="二维码数量"
></el-input-number>
</el-form-item>
</el-row>
</el-form>
</el-col>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitDataScope">下载</el-button>
<el-button @click="cancelDataScope">取 消</el-button>
</div>
</el-dialog>
用到的变量
data() {
return {
codeForm: {
id: null,
frontColor: "#FFFFFF",
backColor: "#28A7A7",
num: 1,
},
qrSize: 400,
qrTextSize: 16,
codeImgList: [],
ceishiQrLogo:'', // 这个就放预览时的base64格式的图片
}
}
实现方法
// 设置前景色或者后景色重绘二维码
setColor() {
this.initCanvasImg();
},
initCanvasImg(arText, qrLogo) {
arText = arText ? arText : "202401010001";
let topText = "微信搜索“****”小程序"; //顶部文字
let qrcode_canvas = this.$refs.qrcode_canvas;
let canvas = this.$refs.canvas;
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = this.codeForm.backColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
var image = new Image();
image.src = qrLogo ? qrLogo : this.ceishiQrLogo;
image.onload = () => {
ctx.save();
ctx.beginPath();
ctx.fillStyle = "#FFFFFF";
ctx.arc(200, 200, 140, 0, Math.PI * 2, false);
ctx.fill();
ctx.clip();
ctx.drawImage(image, 80, 80, 240, 240);
ctx.restore();
};
// ctx.save(); ctx.clip(); ctx.restore(); 共同实现了把图片裁剪成圆形显示。
//设置底部编码
if (arText) {
ctx.font = "bold " + this.qrTextSize + "px Arial";
let tw = ctx.measureText(arText).width; //文字真实宽度
let ftop = this.qrSize - this.qrTextSize - 20; //根据字体大小计算文字top
let fleft = (this.qrSize - tw) / 2; //根据字体大小计算文字left
let tp = this.qrTextSize / 2; //字体边距为字体大小的一半
ctx.fillStyle = this.codeForm.backColor;
ctx.fillRect(
fleft - tp / 2,
ftop - tp / 2,
tw + tp,
this.qrTextSize + tp
);
ctx.textBaseline = "top"; //绘制文本时的文本基线
ctx.fillStyle = this.codeForm.frontColor;
ctx.fillText(arText, fleft, ftop); //canvas填充文字
}
//设置顶部提示
if (topText) {
ctx.font = "bold " + this.qrTextSize + "px Arial";
let tw = ctx.measureText(topText).width; //文字真实宽度
let ftop = 25;
let fleft = (this.qrSize - tw) / 2; //根据字体大小计算文字left
let tp = this.qrTextSize / 2; //字体边距为字体大小的一半
ctx.fillStyle = this.codeForm.backColor;
ctx.fillRect(
fleft - tp / 2,
ftop - tp / 2,
tw + tp,
this.qrTextSize + tp
);
ctx.textBaseline = "top"; //绘制文本时的文本基线
ctx.fillStyle = this.codeForm.frontColor;
ctx.fillText(topText, fleft, ftop); //canvas填充文字
}
//同步执行时图片会丢失,需要异步
setTimeout(() => {
qrcode_canvas.src = canvas.toDataURL("image/jpeg");
this.codeImgList.push({
code: arText,
imgBase: canvas.toDataURL(),
});
}, 10);
},
// base64 转 二进制流(blob)
dataURLtoBlob(dataurl) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime,
});
},
/** 下载二维吗 */
submitDataScope() {
vehicleAdd({
vehicleId: this.codeForm.id,
num: this.codeForm.num,
}).then((response) => {
this.codeImgList = [];
for (let i = 0; i < response.rows.length; i++) {
let qrLogo = "data:image/png;base64," + response.rows[i].url;
this.initCanvasImg(response.rows[i].showCode, qrLogo);
}
const loading = this.$loading({
lock: true,
text: "正在生成二维码,请勿刷新页面",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)",
});
setTimeout(() => {
let zip = new JSZip();
let index = 0;
for (let k = 0; k < this.codeImgList.length; k++) {
index++;
console.log(this.codeImgList[k]);
let blob = this.dataURLtoBlob(this.codeImgList[k].imgBase); //转二进制
// let blob = this.codeImgList[k].imgBase;
zip.file(this.codeImgList[k].code + ".png", blob, { blob: true }); //图片名称(这里以编号命名)
if (index === this.codeImgList.length) {
zip.generateAsync({ type: "blob" }).then(
(blob) => {
FileSaver.saveAs(blob, "车辆二维码.zip"); //下载之后的文件名
this.openDataScope = false;
loading.close();
},
(err) => {
loading.close();
consle.log(err);
}
);
}
}
}, 2000);
});
},
小程序端取到参数
由于项目要求,用户不仅可以通过微信扫一扫进入,还可以在小程序内部的扫码。
所以接收就出出现两种形式
// 第一种:用户通过微信扫一扫进来的
onLoad(query) {
let scene = decodeURIComponent(query.scene)
if(scene != "undefined"){
uni.setStorageSync('sceneCode',scene)
// scene 就是值,后续逻辑根据场景使用自行处理
}
},
// 第二种:用户在小程序内扫码
scanCode() {
uni.scanCode({
onlyFromCamera: true,
success: res => {
let scene = decodeURIComponent(res.path.split('=')[1])
// scene 就是值。后续逻辑根据场景使用自行处理
}
});
},
总结
整体都是拼接而成的,感谢各位大佬放出的代码。