生成海报,涉及小程序二维码生成,多图片合成,图片定位居中,多行文字,文字居中,禁止滚动等问题
1、首先base64图片不能和canvas合成
2、url图片必须用wx.downloadFile()先下载,返回结果为http://tmp/wx0b6251f....是正确结果
3、小程序二维码是在API服务器端生成的,点击
//解决禁止滚动问题
<!-- catchtouchmove="preventTouchMove" -->
<!-- disable-scroll="true" bindtouchstart="touchStart" -->
<!-- disable-scroll="true" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" -->
<view class='haibao' wx-if="{{haib}}" catchtouchmove="preventTouchMove">
<view class='hb'>
<view class='hb_01' bindtap='close_hb' hidden="{{btn_save}}">
<image src='/imgs/xc.png'></image>
</view>
<view class='hb_02 poste_box' id='canvas-container'>
<canvas canvas-id="myCanvas" style="width:100%;height:420px;" disable-scroll="true" bindtouchstart="touchStart" />
<!-- bindtouchmove="touchMove" bindtouchend="touchEnd" -->
</view>
<view class='hb_03' hidden="{{btn_save}}">
<van-button round type="danger" bindtap="save"> 保存到本地</van-button>
</view>
</view>
<v-black></v-black>
</view>
import { HB } from '../../util/hb.js';
//显示海报框,并开始生成海报
show_hb() {
const that = this
this.setData({
haib: true,
share_t: false,
btn_save: false,
})
const obj = {
id: 2,
imgs: https://www.phps.shop/1.jpg,
name: 'aaa',
price: 1,
market_price: 2,
}
HaoBao.create_hb(obj);
},
//保存海报
save() {
const that=this
HaoBao.save(res=>{
if(res==1){
that.setData({
haib: false,
share_t: false,
btn_save: true,
})
}else{
wx.showToast({
title: '保存失败'
})
}
})
}
封装好的海报类
import {
HTTP
} from 'http.js';
const http = new HTTP
class HB {
cardInfo = {
Company: '-- 如花商城 --',
bgimg: 'https://wy.phps.shop/hb_bg.png', //背景图
wximg: 'https://wy.phps.shop/wx.png', //固定二维码
goodsimg: '',
Name: '',
Price: '',
market_price: '',
id:''
}
create_hb(obj){
obj.imgs = obj.imgs.replace('http://', 'https://');
this.cardInfo.Name=obj.name
this.cardInfo.Price = '¥ '+obj.price
this.cardInfo.market_price = '原价 '+obj.market_price
this.cardInfo.goodsimg = obj.imgs
this.cardInfo.goods_id = obj.id
this.get_codeimg()
}
//获取小程序码
get_codeimg() {
const that = this
const id = that.cardInfo.id
//that.getQrCode(that.cardInfo.wximg) //使用固定二维码
/* 动态获取二维码 */
http.request({
url: '/get_codeimg',
data: {
path: 'pages/goods/index?id=' + id
}
}).then(res => {
that.getQrCode(res)
})
}
//下载二维码图片,虽然已经有二维码图片了,但必须wx.downloadFile后才能绘制
getQrCode(urls) {
var that = this;
console.log('二维码0', urls)
wx.showLoading({
title: '生成中...',
mask: true,
})
wx.downloadFile({
url: urls, //二维码路径
success: function (res) {
wx.hideLoading();
if (res.statusCode === 200) {
var codeSrc = res.tempFilePath;
console.log('二维码1', res)
that.down_bg(codeSrc);
} else {
wx.showToast({
title: '二维码下载失败!',
icon: 'none',
duration: 2000,
success: function () {
var codeSrc = "";
that.down_bg(codeSrc);
}
})
}
}
})
}
//下载背景图
down_bg(urls) {
var that = this;
wx.showLoading({
title: '生成中...',
mask: true,
})
console.log('bgimg:', that.cardInfo.bgimg)
wx.downloadFile({
url: that.cardInfo.bgimg,
success: function(res) {
console.log('bgcode:', res.tempFilePath)
wx.hideLoading();
if (res.statusCode === 200) {
var bg = res.tempFilePath;
that.getAvaterInfo(bg, urls);
} else {
wx.showToast({
title: '商品图片下载失败!',
icon: 'none',
duration: 2000,
success: function() {
var bg = "";
that.getAvaterInfo(bg, urls);
}
})
}
}
})
}
//下载商品图片
getAvaterInfo(bg, urls) {
var that = this;
wx.showLoading({
title: '生成中...',
mask: true,
})
wx.downloadFile({
url: that.cardInfo.goodsimg, //图片路径
success: function(res) {
console.log('goodsimg:', res.tempFilePath)
wx.hideLoading();
if (res.statusCode === 200) {
var avaterSrc = res.tempFilePath; //下载成功返回结果
that.sharePosteCanvas(bg, urls, avaterSrc); //生成海报
} else {
wx.showToast({
title: '商品图片下载失败!',
icon: 'none',
duration: 2000,
success: function() {
var avaterSrc = "";
that.sharePosteCanvas(bg, urls, avaterSrc);
}
})
}
}
})
}
//绘制海报
sharePosteCanvas(bj, codeSrc, avaterSrc) {
wx.showLoading({
title: '生成中...',
mask: true,
})
let {
windowWidth
} = wx.getSystemInfoSync();
windowWidth = windowWidth * 0.72 //css中设置的是72%宽
console.log('w:', windowWidth)
var that = this;
var cardInfo = that.cardInfo; //需要绘制的数据集合
const ctx = wx.createCanvasContext('myCanvas'); //创建画布
var width = "";
wx.createSelectorQuery().select('#canvas-container').boundingClientRect(function(rect) {
var height = rect.height;
var right = rect.right;
width = rect.width * 0.8;
var left = 15;
ctx.setFillStyle('#fff');
ctx.fillRect(0, 0, rect.width, height);
//背景图片
if (bj) {
ctx.drawImage(bj, 0, 0, windowWidth, 420);
ctx.setFontSize(14);
ctx.setFillStyle('#fff');
ctx.setTextAlign('left');
}
//公司名称
if (cardInfo.Company) {
ctx.setFontSize(14);
ctx.setFillStyle('#333');
ctx.setTextAlign('center');
let fw = ctx.measureText(cardInfo.Company) //计算文字总宽度,ctx.setTextAlign('leftr')时有用
ctx.fillText(cardInfo.Company, windowWidth / 2, 25); /*参数为:图片,左,上*/
}
// //商品图片
if (avaterSrc) {
ctx.drawImage(avaterSrc, left, 35, width + 25, 180); //参数为:图片,左,上,宽,高
ctx.setFontSize(14);
ctx.setFillStyle('#fff');
ctx.setTextAlign('left');
}
//商品名称
if (cardInfo.Name) {
const CONTENT_ROW_LENGTH = 32; // 正文 单行显示字符长度
let [contentLeng, contentArray, contentRows] = that.textByteLength(cardInfo.Name, CONTENT_ROW_LENGTH);
ctx.setFontSize(14);
ctx.setFillStyle('#000');
if (contentLeng < 33) {
ctx.setTextAlign('center');
ctx.fillText(cardInfo.Name, windowWidth / 2, width + 33);
} else {
ctx.setTextAlign('left');
let contentHh = 22 * 1;
for (let m = 0; m < contentArray.length; m++) {
ctx.fillText(contentArray[m], left + 10, width + 33 + contentHh * m);
}
}
}
//商品价格
if (cardInfo.Price) {
ctx.setFontSize(16);
ctx.setTextAlign('center');
ctx.setFillStyle("#FF4444")
var price_width = ctx.measureText(cardInfo.Price)
ctx.fillText(cardInfo.Price, (windowWidth - price_width.width) / 2, width + 82);
}
//商品原价
if (cardInfo.market_price) {
ctx.setFontSize(12);
ctx.setTextAlign('center');
ctx.setFillStyle("#666")
ctx.fillText(cardInfo.market_price, (windowWidth + price_width.width) / 2 + 10, width + 82);
}
ctx.setFontSize(14);
ctx.setFillStyle('#000');
ctx.setTextAlign('left');
ctx.fillText('长按识别小程序二维码', left + 90, width + 150);
ctx.setFontSize(12);
ctx.setFillStyle('#666');
ctx.setTextAlign('left');
ctx.fillText('好货要和朋友一起分享', left + 90, width + 180);
// 绘制二维码
if (codeSrc) {
ctx.drawImage(codeSrc, left, width + 125, width / 3, width / 3)
}
}).exec()
setTimeout(function() {
ctx.draw();
wx.hideLoading();
}, 1000)
}
//多行文字换行
textByteLength(text, num) {
let strLength = 0; // text byte length
let rows = 1;
let str = 0;
let arr = [];
for (let j = 0; j < text.length; j++) {
if (text.charCodeAt(j) > 255) {
strLength += 2;
if (strLength > rows * num) {
strLength++;
arr.push(text.slice(str, j));
str = j;
rows++;
}
} else {
strLength++;
if (strLength > rows * num) {
arr.push(text.slice(str, j));
str = j;
rows++;
}
}
}
arr.push(text.slice(str, text.length));
return [strLength, arr, rows] // [处理文字的总字节长度,每行显示内容的数组,行数]
}
//点击保存到相册
save(callback) {
var that = this;
wx.showLoading({
title: '正在保存',
mask: true,
})
wx.canvasToTempFilePath({
canvasId: 'myCanvas',
success: function(res) {
wx.hideLoading();
var tempFilePath = res.tempFilePath;
wx.getSetting({
success(res) {
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() {
that.save_phones(tempFilePath, callback)
}
})
} else {
that.save_phones(tempFilePath, callback)
}
}
})
}
});
}
save_phones(tempFilePath, callback) {
wx.saveImageToPhotosAlbum({
filePath: tempFilePath,
success(res) {
wx.showModal({
content: '图片已保存到相册,赶紧晒一下吧',
showCancel: false,
confirmText: '好的',
confirmColor: '#333',
success: function(res) {
callback(1)
},
fail: function(res) {
callback(0)
}
})
},
fail: function(res) {
wx.showToast({
title: res.errMsg,
icon: 'none',
duration: 2000
})
callback(0)
}
})
}
}
export { HB};
遇到的错误提示
1、错误码:48001 api unauthorized hint:
AccessToken错误或过期
2、
网友评论