一、概述:
看了很多小程序的canvas,很多人在骂街,跟浏览器的操作实在相差太多,API极少。所以有些功能基本上没办法实现。但是基本的还是可以的。比如我要实现 生成商品图片(包含产品的:标题,价格,图片,小程序商品详情的二维码等)点击生成后的canvas保存到手机。虽然效果实现了,但是走了不少弯路。所以有必要在此纪念一下。
需要用到的生成二维码组件(可自行下载添加到小程序根目录utils里):https://github.com/demi520/wxapp-qrcode/blob/master/utils/qrcode.js
以上二维码组件默认是有空白边的的,如果不需要空白区域,也可以修改draw方法(如需要修改请进:https://github.com/demi520/wxapp-qrcode/issues/11)
二、需求分析:
效果图是750*1140大小的PSD效果图,如下:(点击可查看大图)
我的思路:
在小程序中先创建三个canvas:
第一个是画二维码;
第二个是画与PSD相同大小的画布可用于保存到手机,样做的原因是单位不需要变,图片,文字,等 位置和大小就可以完全按PSD的尺寸了,不需要去计算;
第三个是最后用来显示效果的(其实就是将第二个再重新画到这个新的画布上面大小就按缩放比例显示)。
多走了很多弯路:开始效果是实现了(画与画布相同大小canvas是需要去计算很多的比如文字大小与位置,图片的大小与位置,起始坐标,都是要去计算的)。但是canvas尺寸太小,保存的时候也就屏幕那么大,不得不得新想办法。
JD的小程序也保存的分享的图片相当大(2550px*4002px),不知道是不是用canvas画的还是后台生成的。我现在保存的大小与PSD(750*1140)大小一样。不会太小也不会太大。
三、实现代码:
WXML代码:
<view class="container">
<view class="canvasarea">
<canvas canvas-id="mycanvas" class="qrcode"/>
<canvas canvas-id="mycanvas2" style="width:{{crentwidth}}px;height:{{crentheight}}px;" class="qrcode2"/>
</view>
<div class="canvasshow" style="width:{{ncrentwidth}}px;height:{{ncrentheight}}px;overflow:hidden;">
<canvas bindtap="previewImg2" canvas-id="mycanvas3" class="qrcode3" style="width:{{ncrentwidth}}px;height:{{ncrentheight}}px;"/>
</div>
</view>
js代码:
var QR = require("../../utils/qrcode.js");
Page({
data: {
},
onLoad: function (options) {
console.log("页面加载事件");
var that = this;
wx.getSystemInfo({
success: function (res) {
that.setData({
scrollWidth: res.windowWidth,
scrollHeight: res.windowHeight,
savewidth:750,//保存的图片宽
saveheight: 1140,//保存的图片高
});
//console.log(crentwidth, crentheight);
}
});
let shareurl="https://youdomain.com/b?id=8559";
QR.api.draw(shareurl, "mycanvas", 300, 300);
var ctx1 = wx.createCanvasContext("mycanvas");
var leftwidth = 30;//两边间距
var imgwidth = 750 - leftwidth * 2;
console.log(imgwidth);
//生成分享商品图片 避免二维码还没生成,等个三秒钟再开始画
setTimeout(function () {
wx.showLoading({
title: "生成中......",
})
wx.canvasToTempFilePath({
canvasId: "mycanvas",
success: function (res) {
var ctx = wx.createCanvasContext("mycanvas2");
ctx.drawImage(res.tempFilePath, 0, 330, 100, 100);
ctx.beginPath();
ctx.setFillStyle("white")
ctx.rect(0, 0, that.data.savewidth, that.data.saveheight);
ctx.setFillStyle("#fff");
ctx.fill();
console.log("画标题");
ctx.setFontSize(32);
ctx.setFillStyle("#333");
var chr = "汽车鲨鱼腮仿真出风口侧风口引擎盖装饰进风口假风口车贴改装用品";
var temp = "";
//截取文只显示一行
for (var a = 0; a < chr.length; a++) {
if (ctx.measureText(temp+"......").width < imgwidth) {
temp += chr[a];
}
else {
break;
}
}
var titlevtop = 60;//标题距顶部高度
ctx.fillText(temp + "...", leftwidth, titlevtop);
ctx.setFontSize(36);
ctx.setFillStyle("#e51a1c");
var pricevtop = 115;//价格距顶部高度
ctx.fillText("¥1.00", leftwidth, pricevtop);
wx.getImageInfo({
src: "https://youdomain.com/upload/20170214/s201702140521515718.jpg",
success: (res) => {
var vtop = 156;//产品图距离顶部位置
// 向画布上绘制图像
ctx.drawImage(res.path, leftwidth, vtop, imgwidth, imgwidth);
// 返回之前保存过的路径状态和属性
ctx.restore();
//画二维码
wx.canvasToTempFilePath({
canvasId: "mycanvas",
success: function (res) {
var vtop2 = 876;//二维码距顶部位置
var ewmwidth = 232;//二维码的宽高
ctx.drawImage(res.tempFilePath, leftwidth - 2, vtop2, ewmwidth, ewmwidth);
//画LOGO
wx.getImageInfo({
src: "https://youdomain.com/images/logo.png",
success: (res) => {
console.log("给二维码添加logo:");
var logowidth = 73;//logo宽高
ctx.drawImage(res.path, leftwidth+ ewmwidth / 2 - logowidth / 2, vtop2 + ewmwidth/2 - logowidth / 2, logowidth, logowidth);
var vtop3 = 970;//文字距顶部的高度
ctx.setFontSize(36);
ctx.setFillStyle("#333");
ctx.fillText("百易商小程序", leftwidth + ewmwidth + logowidth, vtop3);
var vtop4 = 1020;//文字距顶部的高度
ctx.setFontSize(26);
ctx.setFillStyle("#999");
ctx.fillText("长按或扫一扫识别小程序", leftwidth + ewmwidth + logowidth, vtop4);
//ctx.save();
ctx.draw();
//ctx.save();
console.log("大图生成完");
setTimeout(function(){
var radius = that.data.savewidth / that.data.saveheight;//原大小比率
var crentheight = parseInt((that.data.scrollWidth / radius).toFixed(0));//当前屏幕比例的高
var x = that.data.scrollWidth / that.data.savewidth;//宽缩小比例
var y = crentheight / that.data.saveheight;//高缩小比例
var ncrentwidth = that.data.scrollWidth;//屏幕的宽
that.setData({ x: x, y: y, ncrentwidth: ncrentwidth, ncrentheight: crentheight });
wx.canvasToTempFilePath({
canvasId: "mycanvas2",
success: function (res) {
console.log(res);
var ctx3 = wx.createCanvasContext("mycanvas3");
ctx3.scale(x, y);
ctx3.beginPath();
ctx3.drawImage(res.tempFilePath, 0, 0, that.data.savewidth,that.data.saveheight);
ctx3.draw();
ctx3.save();
console.log("缩略图生成成功,显示!")
wx.showToast({
title: "生成完成!",
})
setTimeout(function () {
wx.hideLoading();
}, 1000)
}
});
},100)
}
})
}
});
}
})
}, fail:function(res){
console.log(res);
}
});
}, 3000)
},
//点击保存分享图片
previewImg2: function (e) {
wx.showActionSheet({
itemList: ["保存图片"],
success: function (res) {
console.log(res.tapIndex)
if (res.tapIndex == 0) {
console.log("保存图片")
wx.canvasToTempFilePath({
canvasId: "mycanvas2",
success: function (res) {
var tempFilePath = encodeURI(res.tempFilePath)
console.log(res)
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function success(res) {
console.log("saved::" + res.savedFilePath);
wx.showToast({
title: "保存成功",
})
}
})
},
fail: function (res) {
console.log(res);
wx.showToast({
title: "保存失败",
icon: "loading"
}),
setTimeout(function () {
wx.hideLoading()
}, 2000)
}
});
}
},
fail: function (res) {
console.log(res.errMsg)
}
})
}
});
特别说明:
1、wx.getImageInfo使用时图片必须是:downloadFile 合法域名才能画出来,不然会报错。
2、二维码生成后扫描或识别需要登录 小程序公众平台,在《设置中》->《开发设置》->《扫普通链接二维码打开小程序》中开通二维码 并添加相应规则。
添加规则如下:
详情页面获取方式:
if (typeof (options.q) != "undefined") {
var getq = decodeURIComponent(options.q).replace("https://yourdomain.com/b?id=","");//替换规则后,获取的就是商品ID
//从图片识别或扫一扫接收的参数
that.setData({
id: getq
})
}else{
//默认接收参数
that.setData({
id: options.id
})
}
四、总结:
针对于小程序有时候很不想写博文,因为涉及了很多东西,要写的话也没办法整个代码给你放上来,也只能写这么多了,所以有什么不懂的地方可以联系我。