一个电子商务公司需要一个支付功能,该支付功能通过微信扫码或者支付宝扫码实现的,并且该二维码商户可以下载下来,类似于微信商家码。如下图,鉴于公司相关的保密协议,我马赛克了头部和中间的商标相关说明文字。由于是用的uni-app,原生的js操作是无法实现的,因为安卓和ios是没有dom这个概念的也没有实现canvas的一些底层API,为此需要我们自己手撕uni-app的canvas相关的API。
上图呢是一个中间二维码,为了生成这个二维码,我才用了uni-app的第三方插件库“tki-qrcode”这个插件。背景是一个绿色的正方形,这个可以用图片或者自己用canvas画布绘制一个。顶部是一个公司的logo加说明文字,底部呢是支付宝和微信二维码的logo,要把图片绘制到canvas画布上,就只需调用uni-app的drawImage这个方法就行。把文字绘制到canvas上就调用fillText就行了。绘制完所有的部分,然后调用uni-app的uni.canvasToTempFilePath(object, component)这个方法就行了。
要实现生成商家码并保存到相册的功能有很多方法,这里我介绍一种方法,自认为是最快的方法。具体方法如下。(窃笑)
tki-qrcode组件地址 插件下载地址
import tkiQrcode from "@/components/tki-qrcode/tki-qrcode.vue"
export default {
components: {tkiQrcode}
}
<tki-qrcode v-if="ifShow"
cid="qrcode1"
ref="qrcode"
:val="val"
:size="size"
:unit="unit"
:icon="icon"
:iconSize="iconsize"
:lv="lv" //这里填入你要生成的链接地址
:onval="true"
:loadMake="true" //组件加载完成后自动生成二维码
:usingComponents="true"
@result="resultFunc" //生成二维码base64编码格式的图片
/>
// 编写二维码生成函数
resultFunc:function(path){
this.path = path;//二维码base64编码
if(path!==undefined){
//只有当二维码生成时,我们才去绘制收款码否则收款码中间的二维码将无法绘制到canvas画布上
this.drawSKM();//绘制整个商家码
}
console.log('this.path',this.path);
}
// 编写收款码顶部、底部、中间背景图
drawSKM:function(){
if(this.path){
var context = uni.createCanvasContext('firstCanvas');
context.setFillStyle("#fff");
context.fillRect(20,0,380,60);
context.setTextBaseline('middle');
context.setFillStyle('#00A757');
context.setFontSize(20);
context.fillText('顶部商家说明文字',175,30);
context.drawImage('商家logo地址',145,18,30,26);
context.setFillStyle('#00A757');
context.fillRect(20,60,380,380);
context.closePath();
//开启新的绘制
context.setFillStyle('#fff');
context.setFontSize(40);
context.fillText('请扫码付款',105,100);
context.fillRect(90,130,240,260);
context.drawImage(this.path,110,150,200,200);//this.path就是中间二维码的base64编码
context.setFontSize(18);
context.setFillStyle('#000');
context.setTextAlign('center');
context.setTextBaseline('middle');
context.fillText(`商家名字收款码`,210,370);
//绘制底部logo
context.setFillStyle('#fff');
context.fillRect(20,440,380,60);
context.drawImage('支付宝logo地址',85,453,36.5,36.5);
context.beginPath();
context.setStrokeStyle('#EEEEEE');
context.moveTo(210,450);
context.lineTo(210,490);
context.stroke();
context.closePath();
context.drawImage('微信logo地址',290,453,36,31);
context.draw();
}
},
1、由于我们需要等待中间二维码生成后,才能去绘制整个商家码,所以需要在二维码生成base64编码的时候进行判断,判断base64编码有没有产生,产生了则去执行绘制整个商家码。
2、鉴于各个手机屏幕尺寸不一,所以大家可以叫美工将二维码的背景交由美工绘制出来,生成无损压缩图片(这里需要注意图片格式,否则将图片填充到二维码上将有点儿模糊)。
3、我的方案在2340*1080像素的手机上测试通过了,ios也测试通过了,如果大家对生成的商家码质量要求很高,可以参考我的写法进行修改!
<template>
<view class="pay-contanier">
<view class="pay-box">
<view class="code-title">
<view class="code-logo">
<image src="../../../static/images/icon_logo.png" mode=""></image>
<span>小厨子</span>
</view>
</view>
<view class="scan-code">
<view class="scan-span">请扫码付款</view>
<view class="scan-box">
<tki-qrcode v-if="ifShow"
cid="qrcode1"
ref="qrcode"
:val="val"
:size="size"
:unit="unit"
:icon="icon"
:iconSize="iconsize"
:lv="lv"
:onval="onval"
:loadMake="loadMake"
:usingComponents="true"
@result="resultFunc"
/>
<view class="scan-user">小厨子收款码</view>
</view>
</view>
<view class="code-zf">
<view class="zf-zfb">
<image src="../../../static/images/jingying/icon_zfbzf.png" mode=""></image>
</view>
<view class="zf-wx">
<image src="../../../static/images/jingying/icon_wxzf.png" mode=""></image>
</view>
</view>
</view>
<view id="canvasImage">
<canvas style="width: 750rpx; height: 600px;margin: 0 auto;" canvas-id="firstCanvas" id="firstCanvas"></canvas>
</view>
<button @click="saveSKMImage">保存到相册</button>
</view>
</template>
<script>
import tkiQrcode from "@/components/tki-qrcode/tki-qrcode.vue";
import qr from "@/components/tki-qrcode/wxqrcode.js"
export default {
components: {
tkiQrcode
},
data() {
return {
ifShow: true,
val: '', // 要生成的二维码值
size: 400, // 二维码大小
unit: 'upx', // 单位
icon: '../../../static/images/icon_logo.png', // 二维码图标
iconsize: 60, // 二维码图标大小
lv: 3, // 二维码容错级别 , 一般不用设置,默认就行
onval: true, // val值变化时自动重新生成二维码
loadMake: true, // 组件加载完成后自动生成二维码
src: '' ,// 二维码生成后的图片地址或base64
};
},
onReady() {
this.resultFunc();
},
onLoad() {
var shopId = uni.getStorageSync('shopId');
var merchantsId = uni.getStorageSync('merchantsId');
this.shopName = uni.getStorageSync("shopName");
this.val ='这里填写自己的地址和参数';
},
methods:{
saveSKMImage:function(){
uni.canvasToTempFilePath({
x:20,
y:0,
width:370,
height:500,
canvasId:'firstCanvas',
success(res) {
uni.saveImageToPhotosAlbum({
filePath:res.tempFilePath,
success:function(){
uni.showToast({
icon:'success',
title:'保存成功,请到相册中查看!'
})
}
})
console.log('生成的图片base64',res.tempFilePath);
}
})
},
drawSKM:function(){
if(this.path){
var context = uni.createCanvasContext('firstCanvas');
context.setFillStyle("#fff");
context.fillRect(20,0,380,60);
context.setTextBaseline('middle');
context.setFillStyle('#00A757');
context.setFontSize(20);
context.fillText('小厨子',175,30);
context.drawImage('../../../static/images/icon_logo.png',145,18,30,26);
context.setFillStyle('#00A757');
context.fillRect(20,60,380,380);
context.closePath();
//开启新的绘制
context.setFillStyle('#fff');
context.setFontSize(40);
context.fillText('请扫码付款',105,100);
context.fillRect(90,130,240,260);
context.drawImage(this.path,110,150,200,200);
context.setFontSize(18);
context.setFillStyle('#000');
context.setTextAlign('center');
context.setTextBaseline('middle');
context.fillText(`小厨子收款码`,210,370);
//绘制底部logo
context.setFillStyle('#fff');
context.fillRect(20,440,380,60);
context.drawImage('../../../static/images/jingying/icon_zfbzf.png',85,453,36.5,36.5);
context.beginPath();
context.setStrokeStyle('#EEEEEE');
context.moveTo(210,450);
context.lineTo(210,490);
context.stroke();
context.closePath();
context.drawImage('../../../static/images/jingying/icon_wxzf.png',290,453,36,31);
context.draw();
}
},
resultFunc:function(path){
this.path = path;
if(path!==undefined){
this.drawSKM();
}
console.log('this.path',this.path);
}
}
}
</script>
<style lang="scss" scoped>
page{
background-color: rgb(245,245,245);
}
.pay-contanier{
// text-align: center;
.pay-box{
width: 686rpx;
height: 956rpx;
background: #FFFFFF;
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.16);
opacity: 1;
border-radius: 8px;
margin: 20rpx auto;
.code-title{
width: 100%;
height: 108rpx;
display: flex;
justify-content: center;
align-items: center;
.code-logo{
display: flex;
justify-content: center;
align-items: center;
image{
width: 52rpx;
height: 52rpx;
}
span{
font-size: 36rpx;
font-weight: 600;
color: #00A757;
}
}
.code-txt{
margin-left: 20rpx;
font-weight: normal;
font-size: 36rpx;
color: #000000;
opacity: 1;
}
}
.scan-code{
width: 100%;
height: calc(956rpx - 228rpx);
background-color: #00A757;
display: flex;
align-items: center;
flex-direction: column;
.scan-span{
font-size: 80rpx;
color: #FFFFFF;
opacity: 1;
}
.scan-box{
width: 460rpx;
height: 520rpx;
background: #FFFFFF !important;
opacity: 1;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
margin-top: 40rpx;
.tki-qrcode{
margin-top: 10rpx;
}
.scan-user{
font-size: 32rpx;
font-weight: 600;
padding-top: 10rpx;
}
}
}
.code-zf{
width: 100%;
height: 120rpx;
display: flex;
justify-content: center;
align-items: center;
.zf-zfb{
width: 50%;
height: 68rpx;
border-right: 4rpx solid #EEEEEE;
text-align: center;
image{
width: 65rpx;
height: 65rpx;
}
}
.zf-wx{
width: 50%;
height: 68rpx;
text-align: center;
image{
width: 65rpx;
height: 65rpx;
}
}
}
}
}
#canvasImage{
position: fixed;
top: -9999999999999rpx;
}
button{
width: 80%;
height: 100rpx;
background: #2FAF48;
color: #FFFFFF;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
bottom: 50rpx;
margin-left: 80rpx;
border-radius: 20rpx;
}
</style>
如果该篇文章帮助到了你,还请给个三连,原创不易,转载请注明来处。博主身世贫寒,欢迎大家打赏!如遇遇到了问题,可以加我qq1822497204联系解决。