废话不说,直接上干货~
前端代码
- xml代码
//只需看生成商品图片内容 其他可忽略
<action-sheet hidden="{{ actionSheetHidden }}" bindchange="listenerActionSheet">
<action-sheet-item>
<button open-type="share" hover-class='none'>发送给朋友</button>
</action-sheet-item>
<action-sheet-item>
<button catchtap="createposter">生成商品图片</button>
</action-sheet-item>
<action-sheet-item wx:if="{{salesperson_product == 1}}">
<button catchtap="earning">推荐赚佣金</button>
</action-sheet-item>
//自动隐藏action-sheet
<action-sheet-cancel>取消</action-sheet-cancel>
</action-sheet>
// imagePath 是重点 海报的路径 canvas_show 为控制是否显示
<view hidden="{{ canvas_show ? true : false }}" class='bm_show' bindtap="closephoto" >
<view class='pics'>
<image src="/images/closecsv.png" class="closex" catchtap="closecsv" mode="widthFix" />
<image src="{{ imagePath }}" mode="widthFix"></image>
<button class='save_box' catchtap='savephoto'>保存图片</button>
</view>
</view>
<canvas class='mycanvas' canvas-id="mycanvas"></canvas>
- js代码
/**
* 页面的初始数据
*/
data: {
actionSheetHidden:true, //底部按钮显示
canvas_show:true //生成海报
},
/**
* 生命周期函数--监听页面加载
* 接收参数方式
*/
onLoad: function (options) {
//需要用decodeURIComponent转换所接的参数 仅供参考
if (options.scene) {
var ps = decodeURIComponent(options.scene).split("&");
productid = ps[0];
if (ps.length == 2) {
unique_code = ps[1];
} else if (ps.length == 3) {
coupon_code = ps[2];
}
}
},
//生成海报
createposter: function () {
let that = this;
that.createNewImg(); // 执行绘图
},
//也可以直接为一个方法生成海报
createNewImg: function () {
let that = this;
if (that.data.swiperImgs.length == 0) {
return false;
}
let top_pics = that.data.swiperImgs[0].real_path; //获取图片数据
let product_name = that.data.product_name; //获取商品名字
var context = wx.createCanvasContext('mycanvas'); //创建 canvas 绘图上下文(指定 canvasId)
let productid = that.data.productid; //商品id
//准备encodeURIComponent之后传参数 当前参数 1 product_id 2 unique_code 3 appid
let Params_productid = encodeURIComponent(productid);
let is_recruit = that.data.is_recruit; //是否是销售员
let unique_code = is_recruit == 1 ? "" : that.data.unique_code; //生成的随机字符 根据当前需求决定参数
let appid = app.globalData.sysParams.ext_app_id; //根据appid去后台获取access_token 必须的参数
//传参数之前需要 encodeURIComponent 一下
let Params_unique_code = encodeURIComponent(unique_code);
wx.showLoading({title: "图片生成中…"});
//注:invokeServer 方法为app.js文件中的一个封装调用接口方法 不用抄袭,可以按自己的来写
//此接口只是为了获取小程序的二维码(带参数的)
app.invokeServer('mall.product.product.getqrcode', {"appid":appid,"product_id":Params_productid,"unique_code":Params_unique_code}).then(function(res) {
if (res.data.code != 0) {
app.showToast(res.data.msg, 'none', 1000)
return;
}
var qrcode = res.data.data.url;
console.log("qrcode",qrcode); //打印返回二维码图片的路径
//获取商品的图片信息
that.getImageInfo(top_pics).then(function(obj){
//获取小程序二维码的图片信息
that.getImageInfo(qrcode).then(function(codeobj){
console.log("codeobj:",codeobj);
context.setFillStyle("#fff") //背景颜色
context.fillRect(0, 0, 320, 450) // 画布坐标/大小
context.stroke();
//商品图片
context.beginPath();
context.drawImage(obj.path, 20, 20, 280, 280);
context.stroke();
context.beginPath();
context.font = '14px Microsoft Yahei' //设置字体
context.setTextAlign('left');
context.setFillStyle('#333');
that.drawText(context,product_name, 20, 330, 80, 280);
context.stroke();
let price = "";
let price_icon = "";
//这块逻辑可以忽略,只是写显示商品的价格 start
let productAll = that.data.productAll;
if (productAll.pur_price.min_price == productAll.pur_price.max_price) {
price = productAll.pur_price.min_price;
price_icon = "¥";
} else {
price = productAll.pur_price.min_price + "~ ¥" + productAll.pur_price.max_price;
}
//end
context.beginPath();
context.font = '14px Microsoft Yahei' //设置字体
context.setTextAlign('left');
context.setFillStyle('#f00');
context.fillText(price_icon, 20, 410);
context.stroke();
context.beginPath();
context.font = '18px Microsoft Yahei' //设置字体
context.setTextAlign('left');
context.setFillStyle('#f00');
context.fillText(price, 35, 410);
context.stroke();
// 二维码信息
context.beginPath();
context.drawImage(codeobj.path, 210, 360, 80, 80);
context.stroke();
context.draw() //开始进行绘图
//将生成好的图片保存到本地,需要延迟一会,绘制期间耗时
setTimeout(function() {
wx.canvasToTempFilePath({
canvasId: 'mycanvas', //必须和wx.createCanvasContext 保持一致
success: function (con) {
wx.hideLoading();
var tempFilePaths = con.tempFilePath;
that.setData({
imagePath: tempFilePaths, //海报展示的路径(--很关键--)
canvas_show:false,
});
},
fail: function (resere) {
console.log(resere);
}
});
}, 2000);
setTimeout(function () {
that.setData({
canvas_show: false
})
}, 1500);
}).catch(function(msg) {
app.tip(msg)
});
}).catch(function(msg) {
app.tip(msg)
});
}).catch(function(msg) {
wx.hideLoading();
});
},
//文本换行 参数:1、canvas对象,2、文本 3、距离左侧的距离 4、距离顶部的距离 5、文本的宽度
drawText: function (ctx, str, leftWidth, initHeight, titleHeight, canvasWidth) {
let lineWidth = 0;
let lastSubStrIndex = 0; //每次开始截取的字符串的索引
for (let i = 0; i < str.length; i++) {
lineWidth += ctx.measureText(str[i]).width;
if (lineWidth > canvasWidth) {
ctx.fillText(str.substring(lastSubStrIndex, i), leftWidth, initHeight); //绘制截取部分
initHeight += 24; //26为字体的高度
lineWidth = 0;
lastSubStrIndex = i;
titleHeight += 24;
}
if (i == str.length - 1) { //绘制剩余部分
ctx.fillText(str.substring(lastSubStrIndex, i + 1), leftWidth, initHeight);
}
}
// 标题border-bottom 线距顶部距离
titleHeight = titleHeight + 10;
return titleHeight
},
//获取商品图片信息
getImageInfo: function (imagesrc) {
return new Promise(function (resolve, reject) {
wx.getImageInfo({
src: imagesrc,
success(res) {
resolve(res);
},
fail(ere) {
this.tip(ere)
}
})
});
},
//保存海报图片
savephoto: function () {
var that = this;
wx.showLoading({ title: '保存中...' });
wx.saveImageToPhotosAlbum({
filePath: that.data.imagePath,
success(res) {
console.log("*res*",res);
wx.showModal({
content: '图片已保存到相册,赶紧晒一下吧~',
showCancel: false,
confirmText: '好的',
confirmColor: '#333',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
/* 该隐藏的隐藏 */
that.setData({
canvas_show: true,
})
}
},fail:function(res){
console.log(11111)
}
})
},fail:function(ere){
//授权失败-保存失败 这边微信做过调整,必须要在按钮中触发,因此需要在弹框回调中进行调用
wx.showModal({
title: '提示',
content: '您未授权,保存失败!',
showCancel: false,
success:modalSuccess=>{
wx.openSetting({
success(settingdata) {
console.log("settingdata", settingdata)
if (settingdata.authSetting['scope.writePhotosAlbum']) {
wx.showModal({
title: '提示',
content: '获取权限成功,再次点击图片即可保存',
showCancel: false,
})
} else {
wx.showModal({
title: '提示',
content: '获取权限失败,将无法保存到相册哦~',
showCancel: false,
})
}
},
fail(failData) {
console.log("failData",failData)
},
complete(finishData) {
console.log("finishData", finishData)
}
})
}
})
},
complete(res) {
wx.hideLoading()
}
})
},
//关闭画布
closecsv: function () {
this.setData({ canvas_show: true })
},
附:获取小程序二维码也可请参考官网接口 请点击 这里
关于画布问题请参考 这里
关于保存图片到系统相册里的请参考 这里
关于获取图片信息的请参考 这里
其他的请自行查询百度
后端代码
- PHP代码
/**
* @return array
* 获取商品详情页小程序二维码
*/
public function getqrcode()
{
$appid = trim($this->input('appid'));
$product_id = trim($this->input('product_id'));
$unique_code = trim($this->input('unique_code'));
//获取access_token
$access_token = WxmAppService::getComponentAccessToken($this->supplier_id, $appid);
if (!$access_token) {
return alert_info(1, 'access_token 获取失败!');
}
if ($unique_code) {
$scene = $product_id . '&' . $unique_code;
} else {
$scene = $product_id . "&";
}
$page = "pages/shop/shopdetail/shopdetail";
//微信官方接口 根据access_token获取小程序二维码 参数必须是json_encode加工 然后通过curl请求
$url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=". $access_token;
try {
$res = curl_post($url, json_encode([
'scene' => $scene,
'page' => $page,
'width' => 280,
]));
} catch (\Exception $ex) {
return alert_info(1, '调用异常!' . $ex->getMessage());
}
$res_arr = json_decode($res, true);
if (isset($res_arr['errcode'])) {
return alert_info($res['errcode'], $res['errmsg']);
}
$ym = date('Ym');
$filepath = attached_path('wechat/scene/'.$ym);
if (!is_dir($filepath)) mkdir($filepath, 0777, true);
$filename = uniqid(substr(md5($scene), 3, 10)).'.jpg';
//写入文件
file_put_contents($filepath.'/'.$filename, $res);
//alert_info是返回接口的封装方法,也可以自行封装。
return alert_info(0, 'ok!', [
'url' => url('attached/wechat/scene/'.$ym.'/'.$filename),
]);
}
/**
* 信息返回
* @param int $code 错误码 0成功,其他失败
* @param string $msg 返回消息
* @param array $data 返回数据
* @return array
*/
function alert_info($code, $msg = '', $data = array())
{
$code = (int)$code;
$msg = trim($msg);
return ['code' => $code, 'msg' => $msg, 'data' => $data];
}
测试阶段
可以使用微信开发工具中的 《通过二维码编译》去进行调试
最后展示效果
将此图片保存到手机当中
注:此图片是本小程序二维码图片,中间二维码带有商品id、appid、unique_code随机字符参数