UniApp:方法篇:微信小程序海报生成和保存

UniApp在微信小程序中的Canvas画布操作

  • 画布建立

    • 建立画布
    <!-- 禁止画布被拖动 -->
    <canvas 
            canvas-id="posterid" 
            style="width:300rpx;height:400rpx;" 
            disable-scroll="true" 
            @touchstart="" 
            @touchmove="" 
            @touchend=""
    >
    </canvas>
    
    
    • js声明画布对象
    var ctx = uni.createCanvasContext('posterid')
    //····绘画操作
    ctx.draw();	//绘制图像
    
  • 网络图片在画布中需要先下载到本地才可以绘入画布中,最后导出图片

    • 下载网络图片(这一步要先准备好)

      uni.downloadFile({
          url: imgUrl, 
          success: (res) => {
              if (res.statusCode === 200) {
                  uni.setStorageSync('headImg',res.tempFilePath);
              }
          },fail: (err) => {
              console.log(err);
          }
      })
      
    • 先确认是否可以获取到网络图片下载到本地后的信息才可以开始绘画

      • 获取图片信息

        getImageInfo(image) {
            //返回一个Promise对象,包含用户的信息
            return new Promise((req, rej) => {
                uni.getImageInfo({
                    src: image,
                    success: function(res) {
                        req(res)
                    },
                });
            })
        }
        
  • 绘制的几个方法

    • 文本换行

      /**
      	* @desc canvas文字过长换行脚本
      	* @param {Object} ctx canvas对象
      	* @param {String} text 文字
      	* @param {Number} x 距离左边的宽度
      	* @param {Number} y 距离右边的宽度
      	* @param {Number} w 文本区域的宽度
      	* @param {Object} fontStyle 文本的字体风格/位置,有默认值
      */
      ctb (ctx,text,x,y,w,fontStyle) {
          ctx.save();
          ctx.fillStyle = fontStyle.fillStyle;
          ctx.textAlign = fontStyle.textAlign;
          ctx.textBaseline = fontStyle.textBaseline;
          const chr = text.split('');
          const row = [];
          let temp = '';
          /*
      		判断如果末尾是,!。》 就不要换行
      		判断如果末尾是《 就要换行
      	*/
          for (let a = 0; a < chr.length; a++) {
              if (ctx.measureText(temp).width < w) { } else {
                  if (/[,。!》]/im.test(chr[a])) {
                      // console.log(`我是${chr[a]},我在末尾,我不换行`);
                      temp += ` ${chr[a]}`;
                      // 跳过这个字符
                      a++;
                  }
                  if (/[《]/im.test(chr[a - 1])) {
                      // console.log(`我是${chr[a-1]},我在末尾,我要换行`);
                      // 删除这个字符
                      temp = temp.substr(0, temp.length - 1);
                      a--;
                  }
      
                  row.push(temp);
                  temp = '';
              }
      
              temp += chr[a] ? chr[a] : '';
          }
          row.push(temp);
          for (let b = 0; b < row.length; b++) {
              ctx.fillText(row[b], x, y + b * fontStyle.lineHeight);
          }
      }
      
    • 矩形图片裁剪成原型

      /**
      	* @param {Object} ctx 创建的画布对象
      	* @param {Object} width 矩形的宽
      	* @param {Object} height 矩形的高
      	* @param {Object} x 矩形左上角x轴坐标点
      	* @param {Object} y 矩形左上角y轴坐标点
      	* @param {Object} url 绘制的图片的URL
      */
      drawCircular(ctx,width, height, x, y, url) {
          var avatarurl_width = width;
          var avatarurl_heigth = height;
          var avatarurl_x = x;
          var avatarurl_y = y;
          ctx.save();
          ctx.beginPath();
          ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2, false);
          ctx.clip();
          ctx.drawImage(url, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth);
          ctx.restore();
      }
      
  • 绘画需要异步执行

    //海报绘制
    async draw(){
        var ctx = uni.createCanvasContext('posterid')
        //获取图片信息
        let headImg = await this.getImageInfo(uni.getStorageSync("headImg"));
        //延迟绘制海报
        setTimeout(() =>{
            //绘画操作
            //·····················
            ctx.draw(false,() =>{
                //绘画成功的操作,可以存放保存图片
                setTimeout(() =>{
                    uni.canvasToTempFilePath({
                        canvasId: 'posterid',
                        destWidth: this.cropW * this.canvasScale * 5, //分享图片尺寸=画布尺寸1*缩放比0.5*像素比2
                        destHeight: this.cropH * this.canvasScale * 5,
                        quality:1,
                        fileType:'png',
                        success: (res) => {
                            if(res.errMsg == "canvasToTempFilePath:ok"){
                                uni.saveImageToPhotosAlbum({
                                    filePath: res.tempFilePath,
                                    success:(res) => {
                                        uni.showToast({
                                            icon: 'none',
                                            position: 'bottom',
                                            title: "图片已下载至【图库】,请打开【图库】查看",
                                        });
                                    },fail: (err) => {
                                        //重新提示用户打开保存图片的授权
                                        if(err.errMsg === "saveImageToPhotosAlbum:fail auth deny"){
                                            uni.showModal({
                                                title: '提示',
                                                content: '需要您授权保存相册',
                                                showCancel: false,
                                                success(res) {
                                                    if (res.confirm) {
                                                        uni.openSetting({
                                                            success(settingdata) {
                                                                if (settingdata.authSetting['scope.writePhotosAlbum']) {
                                                                    uni.showModal({
                                                                        title: '提示',
                                                                        content: '获取权限成功,再次保存图片即可成功',
                                                                        showCancel: false,
                                                                    })
                                                                } else {
                                                                    uni.showModal({
                                                                        title: '提示',
                                                                        content: '获取权限失败,无法保存到相册',
                                                                        showCancel: false
                                                                    })
                                                                }
                                                            }
                                                        })
                                                    }
                                                }
                                            })
                                        }else{
                                            uni.showToast({
                                                icon: 'none',
                                                title: '下载失败', 
                                            });
                                        }
                                    }
                                });
                            }
                        },
                    }, this);
                },500)
            })
        },1500)
    }
    

---------------------------------------------------------------------------------------------------------新项目修正------------------------------

 drawPageImg() {
				const ctx = uni.createCanvasContext('myCanvas');
				const ctxW = '750'; //canvas的宽度
				const ctxH = '1762'; //canvas的高度
				
				/**
				 * 文字绘制
				 * @param {Object} ctx
				 * @param {Object} text 内容
				 * @param {Object} x x轴坐标
				 * @param {Object} y y轴坐标
				 * @param {Object} w 文字占用的宽度面基
				 * @param {Object} size 字号
				 */
				const ctxText = (ctx, text, x, y, w, size, color) => {
					var temp = '';
					var row = [];
					let textAll = text.split('');
					for (var a = 0; a < textAll.length; a++) {
						if (ctx.measureText(temp).width < w) {
						} else {
							row.push(temp);
							temp = '';
						}
						temp += textAll[a];
					}
					row.push(temp);
					ctx.font = `${size}px arail`;
					ctx.textAlign = 'left';
					ctx.lineHeight = '30px';
					if(color) {
						ctx.fillStyle = color;
					} else {
						ctx.fillStyle = '#333';
					}
					ctx.globalCompositeOperation = 'source-over';
					for (var b = 0; b < row.length; b++) {
						ctx.fillText(row[b] || '', x, y + (b + 1) * 20);
					}
				};
				
				const ctxTextB = (ctx, text, x, y, w, size, color) => {
					var temp = '';
					var row = [];
					let textAll = text.split('');
					for (var a = 0; a < textAll.length; a++) {
						if (ctx.measureText(temp).width < w) {
						} else {
							row.push(temp);
							temp = '';
						}
						temp += textAll[a];
					}
					row.push(temp);
					ctx.font = `${size}px arail`;
					ctx.textAlign = 'left';
					ctx.lineHeight = '30px';
					if(color) {
						ctx.fillStyle = color;
					} else {
						ctx.fillStyle = '#333';
					}
					ctx.globalCompositeOperation = 'source-over';
					for (var b = 0; b < row.length; b++) {
						ctx.fillText(row[b] || '', x, y + (b + 1) * 30);
					}
				};
				
				/**
				 * 画一个带圆角矩形
				 * @param {Object} ctx
				 * @param {Object} img 图片
				 * @param {Object} x,
				 * @param {Object} y
				 * @param {Object} width 宽
				 * @param {Object} height 高
				 * @param {Object} r 圆角
				 * @param {Object} shadow 阴影
				 */
				const ctxCircular = (ctx, img, color,  x, y, width, height, r, shadow) => {
					ctx.beginPath(); //开始绘制
					ctx.save(); //保存(canvas)状态
					ctx.moveTo(x + r, y);
					ctx.lineTo(x + width - r, y);
					ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
					ctx.lineTo(x + width, y + height - r);
					ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5);
					ctx.lineTo(x + r, y + height);
					ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI);
					ctx.lineTo(x, y + r);
					ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
					if (shadow == 1) {
						ctx.shadowBlur = 20; // 模糊效果程度的
						ctx.shadowColor = 'red'; // 阴影颜色
					}
					if (color) {
						ctx.fillStyle = color;
					} else {
						ctx.fillStyle = 'rgba(0,0,0,0)'
					}
					ctx.fill(); //对当前路径中的内容进行填充
					ctx.clip(); //从原始画布中剪切任意形状和尺寸
					ctx.closePath(); //关闭一个路径
					if(img) {
						ctx.drawImage(img, x, y, width, height);
					}
					ctx.restore(); //恢复(canvas)状态
					ctx.globalCompositeOperation = 'source-over';
				};
				
				//  开始绘制
				ctx.drawImage(this.shareBg, 0, 0, ctxW, ctxW); // 绘制背景
				ctxText(ctx, this.shopName, 30, 76, 528, 34, 0); // 绘制门店名称
				ctxText(ctx, '减重排行榜', 30, 134, 150, 30, 0); // 绘制门店名称
				ctxCircular(ctx, this.shareLogo, 0, 564, 76, 156, 156, 20, 0) // 绘制门店logo
				
				ctxCircular(ctx, 0, '#fff', 30, 186, 380, 40, 10, 0) // 绘制日期背景颜色
				ctxText(ctx, this.list.startDate + ' - ' + this.list.endDate, 60, 196, 360, 26); // 绘制日期
				// 绘制排名背景
				ctxCircular(ctx, this.oneZhu, 0, 265, 351, 220, 249, 0, 0);
				ctxCircular(ctx, this.twoZhu, 0, 45, 381, 220, 249, 0, 0);
				ctxCircular(ctx, this.threeZhu, 0, 485, 401, 220, 249, 0, 0);
				 // 绘制排名底座
				ctxCircular(ctx, this.oneBg, 0, 328, 270, 113, 113, 0, 0);
				ctxCircular(ctx, this.twoBg, 0, 108, 300, 113, 113, 0, 0);
				ctxCircular(ctx, this.threeBg, 0, 548, 320, 113, 113, 0, 0);
				// 绘制头像
				console.log("this.rankList[0].txImg", this.rankList[0].txImg);
				ctxCircular(ctx, this.rankList[0].txImg, 0, 330, 291, 90, 90, 45, 0); 
				ctxCircular(ctx, this.rankList[1].txImg, 0, 110, 321, 90, 90, 45, 0);
				ctxCircular(ctx, this.rankList[2].txImg, 0, 550, 341, 90, 90, 45, 0);
				// 绘制内容 
				// 第一
				ctxText(ctx, this.rankList[0].studentName + '(' + this.rankList[0].trainer + ')', 315, 421, 164, 20, "#1C9098"); 
				
				ctxText(ctx, '减重重量', 286, 469, 80, 20, "#1C9098"); 
				ctxText(ctx, '减重比率', 390, 469, 80, 20, "#1C9098"); 
				
				ctxText(ctx, this.rankList[0].weightLoss + '斤', 294, 502, 60, 20, "#1C9098"); 
				ctxText(ctx, this.rankList[0].ration + '%', 403, 502, 60, 20, "#1C9098"); 
				// 第二
				ctxText(ctx, this.rankList[1].studentName + '(' + this.rankList[1].trainer + ')', 95, 451, 164, 20, "#097159"); 
				
				ctxText(ctx, '减重重量', 60, 499, 80, 20, "#097159");
				ctxText(ctx, '减重比率', 170, 499, 80, 20, "#097159"); 
				
				ctxText(ctx, this.rankList[1].weightLoss + '斤', 74, 532, 60, 20, "#097159"); 
				ctxText(ctx, this.rankList[1].ration + '%', 183, 532, 60, 20, "#097159"); 
				// 第三
				ctxText(ctx, this.rankList[2].studentName + '(' + this.rankList[2].trainer + ')', 535, 471, 164, 20, "#097159"); 
				
				ctxText(ctx, '减重重量', 500 ,519, 80, 20, "#097159");
				ctxText(ctx, '减重比率', 610, 519, 80, 20, "#097159"); 
				
				ctxText(ctx, this.rankList[2].weightLoss + '斤', 514, 552, 60, 20, "#097159"); 
				ctxText(ctx, this.rankList[2].ration + '%', 623, 552, 60, 20, "#097159"); 
				// 表头
				ctxCircular(ctx, 0, "#fff", 20, 598, 710, 93, 20, 0);
				ctxText(ctx, '排名', 50, 632, 48, 24, "#8A8697"); // 绘制门店名称
				ctxText(ctx, '学员(教练)', 185, 632, 144, 24, "#8A8697"); // 绘制门店名称
				ctxText(ctx, '减重(斤)', 425, 632, 88, 24, "#8A8697"); // 绘制门店名称
				ctxText(ctx, '减重比率', 604, 632, 96, 24, "#8A8697"); // 绘制门店名称
				// 排名
				
				for(let i = 0; i < 7;i++) {
					ctxCircular(ctx, 0, "#fff", 20, 701 + (112 * i), 710, 102, 20, 0);
					ctxText(ctx, JSON.stringify(4 + i), 65, 746 + (112 * i), 38, 32, "#151B2A"); 
					ctxCircular(ctx, this.rankList[3 + i].txImg, 0, 144, 721 + (112 * i), 62, 62, 31, 0);
					ctxText(ctx, this.rankList[3 + i].studentName + '(' + this.rankList[3 + i].trainer + ')', 226, 742 + (112 * i), 210, 24, "#151B2A"); 
					ctxText(ctx, JSON.stringify(this.rankList[3 + i].weightLoss), 455, 742 + (112 * i), 164, 24, "#FF4763"); 
					ctxText(ctx, this.rankList[3 + i].ration + '%', 615, 742 + (112 * i), 60, 24, "#10D63B"); 
				}
				
				// 底部
				ctxCircular(ctx, 0, "#fff", 20, 1495, 710, 174, 20, 0);
				ctxCircular(ctx, this.shareMa, 0, 60, 1515, 134, 134, 0, 0);
				ctxText(ctx, '183-5833-2800', 228, 1539, 300, 44, "#151B2A"); 
				ctxTextB(ctx, '地址:浙江省嘉兴市海宁市皮都锦江酒店158号', 228, 1569, 850, 24, "#151B2A"); 
				
				ctxText(ctx, '中健keep提供数据支持', 252, 1699, 246, 24, "#4D8F4D"); 
				
				ctx.draw(
					false,
					(() => {
						setTimeout(() => {
							// 将canvas 变成图片方便发送给好友或者保存
							uni.canvasToTempFilePath({
								canvasId: 'myCanvas',
								destWidth: ctxW * 5, //展示图片尺寸=画布尺寸1*像素比2
								destHeight: ctxH * 5,
								quality: 1,
								fileType: 'png',
								success: (res) => {
									uni.hideLoading();
									uni.hideLoading();
									console.log(res.tempFilePath);
									this.canvasImg = res.tempFilePath;
									
									this.isImgBox = true;
									
								},
								fail: function (error) {
									uni.hideLoading();
									uni.showToast({
										icon: 'none',
										position: 'bottom',
										title: '绘制图片失败'
									});
								}
							});
						}, 100);
					})()
				);
			},
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值