因为网络图片不能直接使用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 = thisvar 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 = thisutil.showLoading('保存中');var posterDatas = that.posterDatasuni.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 事件//这里什么都不要放},