小程序生成分享海报,带小程序码,并保存图片

小程序生成分享海报,带小程序码

话不多说,先上效果图
在这里插入图片描述

效果图不是重点,海报基本都是大同小异,都是一些信息,加上一个小程序码,重点是如何将所有要素整合成跟效果图一样的图片。我们知道如果是H5的话,有html2canvas可以使用,非常方便,感兴趣的同志可以参考我的另一篇H5生成分享海报
但是,小程序没有提供类似的东西啊,怎么办呢,只能自己动手画了,这就用到了canvas了。
先上我自己项目里的代码吧,结合上图:

<!-- 画布canvas -->
  <canvas  style="width: 343px;height: {{canvasHeight+'px'}};position:fixed;top:-9999rpx;left:-9999rpx;z-index:999" canvas-id="mycanvas"/>
 <!-- 分享海报弹出层 -->
  <van-popup show="{{ true }}" position="center" safe-area-inset-top="{{true}}" overlay="{{false}}" bind:close="onClose" round>
    <view class="share_bill" style="height:{{innerViewH}}">
      <view class="inner_view">
        <view class="top_name">
          <image src="{{activityDetail.activity_imgs[0]}}?x-oss-process=image/resize,m_fill,h_302,w_686"></image>
          <view class="bg_view"></view>
          <view class="title">
            <image src="{{memberDetail.headimgurl?(memberDetail.headimgurl+'?x-oss-process=image/resize,m_fill,h_96,w_96'):'../../images/avatar.png'}}"></image>
            <text>{{nickname}}</text>
          </view>
          
        </view>
        <view class="bottom_msg">
          <view class="msg_info">
            <view class="title">
            <text class="pub_tip">{{activityDetail.is_public==1?'公开':'非公开'}}</text>
            {{activityDetail.activity_name}}
            </view>
            <view class="msg_list">
              <image class="msg_icon" src="../../images/hd_adress@2x.png"></image>
              <text class="msg_text">{{activityDetail.wx_address}} | {{activityDetail.distance}}</text>
            </view>
            <view class="msg_list">
              <image class="msg_icon" src="../../images/hd_time@2x.png"></image>
              <text class="msg_text">{{activityDetail.time}}</text>
            </view>
            <view class="msg_list">
              <image class="msg_icon" src="../../images/hd_enroll@2x.png"></image>
              <text class="msg_text">总人数 {{activityDetail.can_sign_num}}</text>
              <view class="fu_price">
                <text class="fu" wx:if="{{activityDetail.fee_type==1}}"></text>
                <text class="price">{{activityDetail.fee_type==0?'免费':(activityDetail.fee_type==2?'AA收款':(activityDetail.man_fee>activityDetail.woman_fee?activityDetail.woman_fee:activityDetail.man_fee))}}</text>
              </view>
            </view>
          </view>

          <view class="logo_code">
            <view class="logo_box">
              <image src="../../images/share_logo.png"></image>
              <view class="logo_msg">
                <text>羽乐papa报名</text>
                <view>长按识别二维码参与</view>
              </view>
            </view>
            <view class="code_box">
              <image src="{{codeImg}}"></image>
            </view>
          </view>
        </view>
      </view>
      
    </view>
  </van-popup>
waitCreate(){
   // 生成海报
   var that = this
   wx.showLoading({
     title: '海报生成中···',
     mask:'true'
   })
   setTimeout(() => {
     var obj=wx.createSelectorQuery();
     obj.selectAll('.inner_view').boundingClientRect();
     obj.exec(function (rect) {
       that.setData({
         canvasHeight:rect[0][0].height
       })
       that.createNewImg()
     })
     
   }, 1500);
 },
// 生成海报方法
 createNewImg: function () {
   
   var that = this;
   //绘制海报底板,宽343,高海报的高度that.data.canvasHeight,圆角8;具体尺寸可根据实际设计图自己修改
   var context = wx.createCanvasContext('mycanvas');
   context.arc(8, 8, 8, Math.PI, Math.PI*1.5)
   context.arc(343 - 8, 8, 8, Math.PI * 1.5, Math.PI * 2)
   context.arc(343-8, that.data.canvasHeight - 8, 8, 0, Math.PI * 0.5)
   context.arc(8, that.data.canvasHeight - 8, 8, Math.PI * 0.5, Math.PI)
   context.clip()
   context.strokeStyle = "#ffffff";
   context.fillStyle="#ffffff"
   context.fill()
   // context.stroke();
   context.drawImage(that.data.activityCanvasPath, 0, 0, 343, 151);//绘制活动图片
   context.save()
   // 绘制昵称蒙层
   
   const grd = context.createLinearGradient(0, 0, 0, 343)
   grd.addColorStop(0, 'rgba(0, 0, 0, 0)')
   grd.addColorStop(1, 'rgba(0, 0, 0, 0.3)')
   context.setFillStyle(grd)
   context.fillRect(0,103,343,48);
   // 绘制昵称
   context.setFontSize(15);
   context.setFillStyle('#ffffff');
   context.setTextAlign('left');
   context.fillText(utils.StringExchangeEmoji(that.data.memberDetail.nickname),60,113+15)
   

   context.restore()
   // 绘制公开非公开
   context.setFontSize(13);
   context.setFillStyle('#ffffff');
   context.setTextAlign('left');
   var pub_w = 0;
   if(that.data.activityDetail.is_public==1){
     pub_w = context.measureText('公开').width+8
   }else{
     pub_w = context.measureText('非公开').width+8
   }
   console.log(pub_w)
   context.save()
   context.beginPath();
   const pub_bg = context.createLinearGradient(0, 0, pub_w,0)
   pub_bg.addColorStop(0, 'rgba(0, 84, 220, 0.4)')
   pub_bg.addColorStop(1, 'rgba(0, 209, 162, 0.4)')
   context.arc(16+2, 170+2, 2, Math.PI, Math.PI*1.5)
   context.arc(16+pub_w - 2, 170+2, 2, Math.PI * 1.5, Math.PI * 2)
   context.arc(16+pub_w-2, 170+18 - 2, 2, 0, Math.PI * 0.5)
   context.arc(16+2, 170+18 - 2, 2, Math.PI * 0.5, Math.PI)
   context.setFillStyle(pub_bg)
   context.fill()
   context.closePath();
   context.restore()
   context.fillText(that.data.activityDetail.is_public==1?'公开':'非公开',20,170+13);
   
   
   
   // 绘制活动名称
   context.setFontSize(17);
   context.setFillStyle('#333333');
   context.setTextAlign('left');
   let s = "我是a_dddhuo互动名称11111111受到冲击难道就省的111吨吨吨吨"
   let str = that.data.activityDetail.activity_name.split("")
   var temp = '';
   var row = [];
   for(var i = 0;i<str.length;i++){
     console.log(row,'row???')
     console.log(context.measureText(temp),'measureTextwidth???')
     if(row.length<1){
       if(that.data.activityDetail.is_public==1){
         if(context.measureText(temp).width < 269){

         }else{
           row.push(temp);
           temp = "";
         }
       }else{
         if(context.measureText(temp).width < 256){

         }else{
           row.push(temp);
           temp = "";
         }
       }
       
     }else{
       if(context.measureText(temp).width < 311){

       }else{
         row.push(temp);
         temp = "";
       }
     }
     
     temp += str[i];
   }
   row.push(temp)
   console.log(row,'row')
   for(var b = 0; b < row.length; b++){
     if(b==0){
       context.fillText(row[b],that.data.activityDetail.is_public==1?58:71,167+(b+1)*17);
       context.fillText(row[b],that.data.activityDetail.is_public==1?(58+0.5):(71+0.5),167+(b+1)*17+0.5);
     }else{
       context.fillText(row[b],16,167+(b+1)*17);
       context.fillText(row[b],16+0.5,167+(b+1)*17+0.5);
     }
     
   }

   context.save()
   
   // 绘制地址人数时间信息
   context.drawImage('/images/hd_adress@2x.png', 16, 201+row.length*17-17, 14, 14);
   context.setFontSize(13);
   context.setFillStyle('#999999');
   context.setTextAlign('left');

   let name_str = that.data.activityDetail.wx_address.split("")
   var name_temp = '';
   var name_row = [];
   for(var i = 0;i<name_str.length;i++){
     if(context.measureText(name_temp).width < 277){

     }else{
       name_row.push(name_temp);
       name_temp = "";
     }
     name_temp += name_str[i];
   }
   name_row.push(name_temp)
   for(var b = 0; b < name_row.length; b++){
     context.fillText(name_row[b], 34, 199+(b+1)*13+row.length*17-17);
     
   }

   context.drawImage('/images/hd_time@2x.png', 16, 223+(name_row.length*13)-13+row.length*17-17, 14, 14);
   context.fillText(that.data.activityDetail.time, 34, 221+(name_row.length*13)+row.length*17-17);
   context.drawImage('/images/hd_enroll@2x.png', 16, 245+(name_row.length*13)-13+row.length*17-17, 14, 14);
   context.fillText('总人数'+that.data.activityDetail.can_sign_num, 34, 243+(name_row.length*13)+row.length*17-17);
   
   // 价格
   context.restore()
   context.setFontSize(15);
   context.setFillStyle('#F13138');
   context.setTextAlign('left');
   if(that.data.activityDetail.fee_type==0){
     let w = context.measureText('免费').width
     context.fillText('免费', 343-16-w, 237+15+(name_row.length*13)-10+row.length*17-17);
   }
   if(that.data.activityDetail.fee_type==2){
     let w = context.measureText('AA收款').width
     context.fillText('AA收款', 343-16-w, 237+15+(name_row.length*13)-10+row.length*17-17);
   }
   if(that.data.activityDetail.fee_type==1){
     if(that.data.activityDetail.man_fee>that.data.activityDetail.woman_fee){
       let w = context.measureText('¥'+that.data.activityDetail.woman_fee).width
       context.fillText('¥'+that.data.activityDetail.woman_fee, 343-16-w, 237+15+(name_row.length*13)-10+row.length*17-17);
     }else{
       let w = context.measureText('¥'+that.data.activityDetail.man_fee).width
       context.fillText('¥'+that.data.activityDetail.man_fee, 343-16-w, 237+15+(name_row.length*13)-10+row.length*17-17);
     }
     
   }
   // context.fillText('¥'+that.data.activityDetail.man_fee, 297, 278+13+row.length*17-17);
   // 画线
   context.save()
   context.moveTo(16,277+(name_row.length*13)-13+row.length*17-17)
   context.lineTo(327,277+(name_row.length*13)-13+row.length*17-17)
   context.strokeStyle="#E5E5E5"
   context.stroke()
   // 绘制logo
   context.restore()
   context.drawImage('/images/share_logo.png', 16, 319+(name_row.length*13)-13+row.length*17-17-30, 48, 48);
   // 绘制小程序码
   context.drawImage(that.data.codeCanvasPath, 227, 293+(name_row.length*13)-13+row.length*17-17-15, 100, 100);

   context.setFontSize(15);
   context.setFillStyle('#333333');
   context.setTextAlign('left');
   context.fillText("羽乐papa报名", 76, 319+15+(name_row.length*13)-13+row.length*17-17-30);

   context.setFontSize(13);
   context.setFillStyle('#999999');
   context.setTextAlign('left');
   context.fillText("长按识别二维码参与", 76, 346+19+(name_row.length*13)-13+row.length*17-17-30);
   context.save()
   // 画头像
   context.beginPath();
   context.arc(32, 123, 16, 0, 2 * Math.PI) //画出圆
   context.strokeStyle = "#ffffff";
   context.fill()
   context.clip(); //裁剪上面的圆形
   context.drawImage(that.data.avatarCanvasPath, 16, 107, 32, 32); // 在刚刚裁剪的园上画图
   context.restore();
   context.closePath();
     
   context.draw();
    
     
   
   setTimeout(function () {
     wx.hideLoading()
     wx.canvasToTempFilePath({//画出的海报转成图片链接,页面展示用,或保存图片时用
       canvasId: 'mycanvas',
       success: function (res) {
         var tempFilePath = res.tempFilePath;
         console.log("海报的图片链接",tempFilePath)
         that.setData({
           imagePath:tempFilePath
         })
         
       },
       fail: function (res) {
         console.log(res);
       }
     });
     
   }, 1000);
   
 },

逐步解析:
1,首先是画海报的整个底板,也就是接下来的画板区域,它决定了你的有效绘画区域,就跟你拿张纸画画一样,你能画的地方就只限于这张纸上。context.arc(x,y,r,sAngle,eAngle,counterclockwise) 方法创建弧/曲线(用于创建圆或部分圆)。x,y圆心坐标,r圆半径,sAngle起始角,eAngle结束角,counterclockwise,可选,false顺时针,true逆时针。
2,context.clip(),剪切;
3,context.strokeStyle,笔触颜色,就是啥颜色的笔画;
4,context.fillStyle,填充色,可以理解为背景颜色;
5,context.fill(),填充当前的图像(路径);
6,context.drawImage(img,x,y,width,height),将现有图片绘制到canvas上,img图片地址,x,y坐标,width,height宽高大小;
7,context.save(),把当前状态的一份拷贝压入到一个保存图像状态的栈中。这就允许您临时地改变图像状态,然后,通过调用 restore() 来恢复以前的值。与context.restore()成对出现;
8,context.createLinearGradient(x0,y0,x1,y1),创建线性的渐变对象。渐变可用于填充矩形、圆形、线条、文本等等。x0渐变开始点的 x 坐标,y0渐变开始点的 y 坐标,x1渐变结束点的 x 坐标,y1渐变结束点的 y 坐标;
9,grd.addColorStop(),渐变对象中的颜色和位置;
10,context.setFillStyle,设置或返回用于填充绘画的颜色、渐变或模式;
11.context.fillRect(x,y,width,height),绘制"已填充"的矩形,x,y矩形左上角的坐标;
12,context.setFontSize(),设置字体大小;
13,context.setTextAlign(),设置字体对齐方式;
14,context.fillText(text,x,y,maxWidth),在画布上绘制填色的文本,text要绘制的文字,x,y起始坐标,相对于画布,maxWidth可选,允许的最大文本宽度,以像素计;
15,context.measureText(text).width,返回字体的宽度,text要测量的字体,可用于判断字体太多,进行换行绘制;
16,context.beginPath(),开始一条路径,或重置当前的路径;
17,context.closePath(),创建从当前点到开始点的路径;
18,context.moveTo(x,y),把路径移动到画布中的指定点,不创建线条,路径目标位置坐标;
19,context.clip(),从原始画布中剪切任意形状和尺寸,一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法);
20,context.draw(),整体绘制。

绘制完成后,wx.canvasToTempFilePath(),微信小程序的这个方法,可以生成canvas图片链接。接下来就是保存图片,同样微信小程序也提供了对应的方法,

// 点击保存图片
  saveImg(){
    var that = this
    // console.log(mycanvas,'mycanvas')
    // 保存图片
    wx.canvasToTempFilePath({
      canvasId: 'mycanvas',
      success: function (res) {
        var tempFilePath = res.tempFilePath;
        console.log("海报的链接",tempFilePath)
        that.setData({
          imagePath:tempFilePath
        })
        wx.saveFile({
          tempFilePath: tempFilePath,
          success (res) {
            console.log("resresrese",res)
            const savedFilePath = res.savedFilePath
            wx.saveImageToPhotosAlbum({
              filePath: savedFilePath,
              success: function (res) {
                wx.showToast({
                  title: '已保存至手机相册',
                  icon:'none',
                  duration:2000
                })
                
              },
              fail: function (res) {
                wx.showToast({
                  title: '请在左上角设置中开启保存相册权限!',
                  icon:'none',
                  duration:5000
                })
              },
            })
    
          }
        })
      },
      fail: function (res) {
        console.log(res);
      }
    });
    
    
  },

备注:值得一提的是,小程序在context.drawImage()绘制图片的时候,是不支持网络路径的图片的,支、只支持本地路径的图片!!!!解决办法,将线上路径的图片转化为本地路径的图片:

this.setImgPath(res.data.param.url).then(function(data){
 that.setData({
   codeCanvasPath:data
 })
})
// 链接图片转画图路径
  setImgPath(path){//传入图片线上路径
    return new Promise((resolve,reject)=>{
      wx.getImageInfo({
        src:path,
        success (res) {
          resolve(res.path)
        }
      });
    })
    
  },
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值