因为网络图片不能直接使用ctx.drawImage()插入。得使用uni.getImageInfo()方法下载后插入。
但是当画布中存在多张网络图片时,必须等待uni.getImageInfo()下载完成后才行。这样得下载套下载。太过于繁琐。所以定义了一个递归下载方法。同时避免下载图片异步,画布不显示的结果。
一、引用函数
/**
* canvas文本自动换行,注意调用前要设置字体,例如: ctx.font = '12px Arial'
* @param {*} ctx CanvasRenderingContext2D
* @param {*} text 文本
* @param {*} x
* @param {*} y
* @param {*} lw 所占宽度
* @param {*} lh 行高
* return 绘制文本所需的高度
*/
const fillTextLineBreak = (ctx, text, x, y, lw, lh, color = '#000', font = '14') => {
var i = 0;
var n = 0;
var r = -1;
var initHeight = 0;
ctx.font = "" + font + "px Arial"; //字体大小
ctx.fillStyle = color; //字体颜色
while (i < text.length) {
while (ctx.measureText(text.substring(n, i)).width < lw && i < text.length) {
i++
}
r++
ctx.fillText(text.substring(n, i), x, y + lh * r)
n = i
}
initHeight = lh * r;
wx.setStorageSync('initHeight', initHeight);
}
/**
* 自定义函数roundRect
* 画圆弧
* ctx >> 画布
*bg_x 图片的x坐标
*bg_y 图片的y坐标
*bg_w 图片宽度
*bg_h 图片高度
*bg_r 图片圆角
*
*/
function roundRect(ctx, img, bg_x, bg_y, bg_w, bg_h, bg_r) {
// 开始绘制
ctx.save();
ctx.beginPath();
ctx.arc(bg_x + bg_r, bg_y + bg_r, bg_r, Math.PI, Math.PI * 1.5);
ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_r, bg_r, Math.PI * 1.5, Math.PI * 2);
ctx.arc(bg_x + bg_w - bg_r, bg_y + bg_h - bg_r, bg_r, 0, Math.PI * 0.5);
ctx.arc(bg_x + bg_r, bg_y + bg_h - bg_r, bg_r, Math.PI * 0.5, Math.PI);
ctx.clip();
ctx.drawImage(img, bg_x, bg_y, bg_w, bg_h);
// 恢复之前保存的绘图上下文
ctx.restore();
}
/*
* 画布网络图片预处理
*/
function canvasImage(that, Datas = {}, callback = null) {
var i = Datas.i ? Datas.i : 0;
var callback_Datas = Datas.callback_Datas ? Datas.callback_Datas : [];
var data = Datas.data ? Datas.data : [];
uni.getImageInfo({
src: data[i],
success(res) {
i++;
callback_Datas.push(res.path)
if (i == data.length) {
//回调函数
if (callback) {
callback(that, callback_Datas);
return;
}
} else {
//递归执行下一个上传
canvasImage(that, {
i,
callback_Datas,
data,
}, callback);
}
},
fail(e) {
wx.hideLoading();
//关闭画布弹窗
that.posterHideCanvas();
setTimeout(function() {
wx.showToast({
title: '生成失败,稍后再试',
icon: 'none',
})
}, 0)
}
});
}
二、index.vue
<view v-show="posterDatas.hidden" @touchmove="posterTouchMove" class="canvasMain flex center">
<view class="box flex center column">
<canvas :canvas-id="canvasId" :style="canvasStyle" class="canvas"></canvas>
<button v-if="posterDatas.buttonType==1" class="btn" @click="posterDownImage">点击保存图片到相册</button>
<button v-else-if="posterDatas.buttonType==2" class="btn">已保存到相册,快去分享吧</button>
<button v-else-if="posterDatas.buttonType==3" class="btn" open-type="openSetting"
bindopensetting="posterOpenSetting">进入设置页,开启“保存到相册”</button>
<text class="iconfont icon-x" @click="posterHideCanvas"></text>
</view>
</view>
<style>
/* 海报 */
.canvasMain {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
z-index: 9999;
}
.canvasMain.hidden {
top: 100%;
left: 100%;
}
.canvasMain .box {
position: relative;
}
.canvasMain .box .canvas {
margin: 0;
}
.canvasMain .box .btn {
height: 40px;
width: 280px;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 40px;
color: #fff;
font-size: 15px;
}
.canvasMain .box .btn {
margin: 10px 0;
padding-left: 0;
padding-right: 0;
}
.canvasMain .box .iconfont {
font-size: 46rpx;
color: #fff;
}
</style>
三、index.js
data() {
return {
posterDatas: {
width: 285, //画布宽度
height: 406, //画布高度
buttonType: 1,
hidden: false, // 是否隐藏海报弹窗
success: false, // 是否成功生成过海报
},
canvasId: 'firstCanvas',
canvasStyle: 'width: 285px; height: 406px;', // 根据实际需要设置 canvas 的宽高
};
},
//海报生成配置
posterSetCanvas() {
var that = this
var posterDatas = that.posterDatas;
var tempConfig = that.tempConfig;
//展示海报弹窗
that.posterDatas.hidden = true;
util.showLoading('生成中');
var userInfo = wx.getStorageSync('userInfo') || {};
var Datas = {};
Datas.i = 0;
Datas.callback_Datas = [];
Datas.data = [];
Datas.data[0] = userInfo.touimg; //头像
Datas.data[1] = app.globalData.swtCircle + "/mp_ewm_v2.php?act=getwxacode&tjrhao=" + (userInfo
.tjrhao || '') + "&url=pages/home/home"; //小程序码
util.canvasImage(that, Datas, function(that, canvasImage_arr) {
//要延时执行的代码
setTimeout(function() {
//预处理画布网络地址图片
var ctx = uni.createCanvasContext(that.canvasId, that);
// 清除背景
//ctx.clearRect(0, 0, posterDatas.width, posterDatas.height);
// 绘制背景
// ctx.fillStyle = "#fff";
// ctx.fillRect(0, 0, posterDatas.width, posterDatas.height);
var bgImg = '/static/images/yao_bj.png';
// 绘制背景图
ctx.drawImage(bgImg, 0, 0, posterDatas.width, posterDatas.height);
//简介
util.fillTextLineBreak(ctx, "邀请您使用" + tempConfig.appMain.name + "小程序", 100, 81, 150,
20, '#fff',
13);
//标题文字
ctx.font = "bold 18px PingFang SC";
ctx.fillStyle = "#fff";
ctx.textAlign = "start";
ctx.fillText(userInfo.nickName_hai, 100, 60);
util.roundRect(ctx, canvasImage_arr[0], 30, 35, 56, 56, 28);
ctx.font = "bold 18px PingFang SC";
ctx.fillStyle = "#0D7239";
ctx.textAlign = "start";
ctx.fillText('分享有礼 荐者有份', 70, 170);
ctx.drawImage(canvasImage_arr[1], 65, 200, 160, 160);
ctx.draw();
wx.hideLoading();
}, 200)
});
},
//海报下载图片
posterDownImage: function() {
var that = this
util.showLoading('保存中');
var posterDatas = that.posterDatas
uni.canvasToTempFilePath({
canvasId: "firstCanvas",
success(res) {
console.log(res)
wx.showToast({
icon: 'none',
title: '已保存到相册,快去分享吧',
})
posterDatas["buttonType"] = 2;
that.posterDatas = posterDatas;
// 在 h5 中,res.tempFilePath 返回的是 base64 类型要处理,通过 a 标签的形式下载
var arr = res.tempFilePath.split(',');
var bytes = atob(arr[1]);
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
var blob = new Blob([ab], {
type: 'application/octet-stream'
});
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = new Date().valueOf() + ".png";
var e = document.createEvent('MouseEvents');
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false,
false, 0, null);
a.dispatchEvent(e);
URL.revokeObjectURL(url);
},
fail(e) {
console.log(e, "fail");
util.showToast('保存失败,稍后再试”', 'none');
that.posterHideCanvas();
}
})
},
//海报隐藏弹窗
posterHideCanvas: function() {
var that = this;
that.posterDatas.buttonType = 1;
that.posterDatas.hidden = false;
},
//海报弹窗禁止屏幕滚动
posterTouchMove: function() {
//在蒙层加上 catchtouchmove 事件
//这里什么都不要放
},