美文网首页微信小程序开发
【微信小程序】分享朋友圈组件开发实践

【微信小程序】分享朋友圈组件开发实践

作者: Ever月亮 | 来源:发表于2020-04-15 10:21 被阅读0次
    背景

    在一个完整带用户交互的小程序项目开发中,总会遇到分享这个功能,转发给好友用通用的api方法,分享朋友圈总是有各种各样的招式,一般的交互方案是生成一个带二维码的图,二维码有时候也分带参数和默认的。分享一个前段时间开发的生成分享图的功能,我当时的业务二维码是带参数的,为了识别能定位到固定产品页(生成带参数二维码是用微信云调用提供的方法处理,点此看帖

    分享图用canvas画布开发,所以对wx封装的canvas相关api要有一定了解

    开发步骤
    • 新建一个share组件文件包,并开发业务逻辑和样式编写含小程序规范的几个文件js/wxml/wxss/json
    • 开发代码,在需要引用页面的对应配置文件中加入组件配置
    • 在引用的wxml中加入组件代码和传参,js文件写参数的交互
    效果图
    分享步骤效果图.jpg
    代码概览

    1.share / share.wxml 【参考效果图 步骤2/3

    备注:
    1.第一模块 步骤2的分享引导层,点击分享票圈,出来第二模块弹框
    2.第二模块 步骤3效果图,share-load.gif是一个加载中动画的gif;close.png是关闭图标

    <!--分享弹窗-->
    <view class="share-wrap" bindtap="toClose">
      <!-- 分享的引导层 分 转发票圈或好友 -->
      <view class="share-mod" catchtap="doNothing">
        <view class="share-hd">
          <text class="fl">分享</text>
          <image class="share-close" catchtap="toClose" src="../../images/icons/close.png"></image>
        </view>
        <view class="share-guide">
          <view class="share-wx">
              <button class="share-btn" open-type="share"></button>
              <image src="../../images/icons/wx_friend.png"></image>
              <text>分享给好友</text>
          </view>
          <view class="share-line"></view>
          <view class="share-wx" catchtap="toShowShareImg">
            <image src="../../images/icons/wx_quan.png"></image>
            <text>生成分享海报</text>
          </view>
        </view>
      </view>
    
      <!-- 点击引导层的 转发票圈 触发的生成画布图弹层 -->
      <view class="share-mod" hidden="{{!showShareImg}}" catchtap="doNothing">
        <view class="share-hd">
          <text class="fl">保存到相册</text>
          <image class="share-close" catchtap="toCloseShareImg" src="../../images/icons/close.png"></image>
        </view>
        <view class="share-quan">
          <image wx:if="{{imgSrc!=''}}" class="share-img" src="{{imgSrc}}"></image>
          <view wx:else>
            <image class="share-img-load" src="../../images/icons/share-load.gif"></image>
            <text class="share-load-text">{{loadText}}</text>
          </view>
          <view class="save-btn" catchtap="saveImg">保存图片</view>
          <view class="save-tip">保存图片到手机相册后,就可以分享至您的圈子啦</view>
        </view>
      </view>
    </view>
    

    2.share/ share.json

    {
      "component": true
    }
    

    3.share/ share.js

    /*  使用说明↓↓↓
    1.需要引用的页面json配置文件新增如下配置项
    "usingComponents": {
      "share-win": "/component/share/share"
    }
    
    2.页面wxml文件使用如下 shareInfo格式说明见下文 该组件是否渲染根据该对象是否有具体数据
    <canvas canvas-id="shareCanvas" style="position:fixed;top:0;left:999rpx;width:1000px;height:750px;"></canvas>
    <share-win share-info="{{shareInfo}}" bindcloseshare="closeShareWin"></share-win>
    
    3.页面对应js需要定义一个closeshare事件[由组件里的toClose触发] 内部主要是将shareInfo参数置空
    
    4.shareInfo格式
    
    */
    
    Component({
      properties: {
        shareInfo: {
          type: Object,
          value: {},
        }
      },
      data: {
        imgSrc:'',
        showShareImg:false,
        hasDownload:false,
        loadText:'分享图绘制准备中...'
      },
      ready:function(){
        
      },
      methods: {
        //点击浮层区域关闭弹窗
        toClose:function(){
          this.toCloseShareImg();
          this.triggerEvent("closeshare")
        },
        toCloseShareImg:function(){
          this.setData({
            showShareImg:false
          })
          if(this.data.hasDownload){
            this.triggerEvent("closeshare")
          }
        },
        toShowShareImg:function(){
          this.setData({
            showShareImg:true
          })
          this.renderShareImg();
        },
        doNothing:function(){
          return false;
        },
        setLoadText:function(txt){
          this.setData({
            loadText:txt
          })
        },
        //渲染分享图
        renderShareImg:function(){
          //1000x750
          const _this = this;
          const _obj = _this.data.shareInfo;
    
          //默认题图
          let promise1 = new Promise(function (resolve, reject) {
            if(_obj.cover==undefined || _obj.cover==''){
              _obj.cover = '../../images/share_default.jpg'
              resolve({path:_obj.cover});
            }else{
              _obj.cover = "https://"+_obj.cover.split('\/\/')[1]
              wx.getImageInfo({
                src: _obj.cover,
                success: function (res) {
                  resolve(res);
                }, fail: function (error) {
                  console.log(error);
    
                  _obj.cover = '../../images/share_default.jpg'
                  resolve({path:_obj.cover});
                }
              })
            }
          });
    
          //小程序码
          let promise2 = new Promise(function (resolve, reject) {
            wx.cloud.callFunction({
              name: 'openapi',
              data: {
                action:'getWXACodeUnlimit',
                page: 'pages/detail/detail',
                width: 220,
                scene: _obj.id+"_"+_obj.goodsId,
              },
              success: res => {
                wx.getImageInfo({
                  src: res.result[0].tempFileURL,
                  success: function (suc) {
                    resolve(suc);
                  }, fail: function (error) {
                    resolve({path:"../../images/qrcode.jpg"})
                    console.log(error)
                  }
                })    
              },
              fail: error => {
                console.log(JSON.stringify(error))
                resolve({path:"../../images/qrcode.jpg"})
              }
            });
          });
          
    
          //加载所有完图片后绘制画布
          Promise.all(
            [promise1,promise2]
          ).then(res => {
            //绘制头图的圆角效果
            const ctx = wx.createCanvasContext('shareCanvas')
            ctx.setFillStyle('#ffffff');
            ctx.fillRect(0, 0, 750, 1125);
    
            //绘制题图
            _this.setLoadText("绘制商品图...")
            ctx.drawImage(res[0].path, 25, 25, 700, 700)
    
           // ...删除了部分 绘制逻辑代码...
            
            //绘制小程序码
            _this.setLoadText("绘制小程序码...")
            ctx.drawImage(res[1].path, 35, 874, 228, 228);
    
    
            //画布绘制完成转图片,将地址赋值给图片
            _this.setLoadText("分享图生成中...")
            ctx.draw();
    
            setTimeout(function () {
              wx.canvasToTempFilePath({
                width: 750,
                height: 1125,
                destWidth: 750,
                destHeight: 1125,
                quality: 1,
                canvasId: 'shareCanvas',
                success: function (res) {
                  // console.log("canvasToTempFilePath success:"+res.tempFilePath);
                  wx.hideLoading({})
                  _this.setData({
                    imgSrc: res.tempFilePath,
                    shareShow: true
                  })
                },
                fail: function (res) {
                }
              })
            }, 200)
          })
        },
        //保存图片
        saveImg:function(){
          //下载文件
          const _this = this;
          if(_this.data.imgSrc==''){
            wx.showToast({
              title:"分享图还在生成中...",
              icon: 'none',
              duration:3000
            })
            return false;
          }
          wx.saveImageToPhotosAlbum({
            filePath: _this.data.imgSrc,
            success(res) {
              wx.showToast({
                title:"已保存至相册,可以分享啦",
                icon: 'none',
                duration:3000
              })
              _this.setData({
                hasDownload:true
              })
            }
          })
        },
        //图片按比例居中裁剪
        calClipImg(oW,oH,mW,mH){
          var oR = parseFloat(oW/oH).toFixed(5);
          var mR = parseFloat(mW/mH).toFixed(5);
          if(oR == mR){
            return [0,0,oW,oH]
          }else if(oR > mR){
            var ratio = parseFloat(mH/oH).toFixed(5);
            return [((oW*ratio-mW)/2)/ratio,0,mW/ratio,mH/ratio];
          }else{
            var ratio = mW/oW;
            return [0,((oH*ratio-mH)/2)/ratio,mW/ratio,mH/ratio];
          }
        },
        //绘制圆角
        roundRect(x, y, w, h, r,ctx){
          var min_size = Math.min(w, h);
          if (r > min_size / 2) r = min_size / 2;
          // 开始绘制
          ctx.beginPath();
          ctx.moveTo(x + r, y);
          ctx.arcTo(x + w, y, x + w, y + h, r);
          ctx.arcTo(x + w, y + h, x, y + h, r);
          ctx.arcTo(x, y + h, x, y, r);
          ctx.arcTo(x, y, x + w, y, r);
          ctx.closePath();
        },
        //绘制文本方法
        drawText(str,ctx,initX,initY,lineHeight,minusW,maxLine){
          var curLine = 1;
          var lineWidth = 0;
          var canvasWidth = 750;
          var lastSubStrIndex= 0;
          var d = 0;
          for(var i=0;i<str.length;i++){
            lineWidth += ctx.measureText(str[i]).width;
            //判断当前文字行是否超过一行 [减minusW,防止边界溢出]
            if((d==0 && lineWidth>canvasWidth-minusW)||(d>0 && ((lineWidth>=canvasWidth-minusW) || ((lineWidth+ctx.measureText(str[i+1]).width)>canvasWidth-minusW)))){
              d++;
              ctx.fillText(str.substring(lastSubStrIndex,i),initX,initY);
              initY+=lineHeight;
              lineWidth=0;
              lastSubStrIndex=i;
              curLine = curLine+1;
              if(maxLine!=-1 && curLine>maxLine)break; //最多绘制六行
            }
            //最后一个字的时候 绘制一行
            if(i==str.length-1){
              ctx.fillText(str.substring(lastSubStrIndex,i+1),initX,initY);
            }
          }
        }
      }
    })
    
    
    

    4.share.wxss

    .share-wrap{
      position:fixed;
      top:0;
      width:750rpx;
      height:100%;
      background:rgba(0,0,0,.4);
      overflow: hidden;
      z-index:1001;
    }
    
    .share-mod{
      position:fixed;
      bottom:0;
      width:100%;
      background:#fff;
      z-index:1001;
      overflow: hidden;
    }
    
    .share-mod .share-hd{
      padding-left:20rpx;
      height:80rpx;
      line-height:80rpx;
      background:#efefef;
      color:#666;
      font-size:32rpx;
    }
    
    .share-mod .share-close{
      float:right;
      margin:15rpx 20rpx;
      height:50rpx;
      width:50rpx;
    }
    
    .share-guide{
      padding:35rpx;
      width:680rpx;
      height:180rpx;
    }
    
    .share-guide .share-wx,
    .share-guide .share-line{
      float:left;
    }
    .share-guide .share-line{
      margin-top:60rpx;
      height:160rpx;
      width:1rpx;
      color:#cdcdcd;
    }
    .share-guide .share-wx{
      width:339rpx;
      height:180rpx;
      text-align:center;
      font-size:24rpx;
    }
    .share-guide .share-wx image{
      display: block;
      margin:20rpx auto;
      padding:10rpx;
      width:64rpx;
      height:64rpx;
      border-radius:43rpx;
      border:1rpx solid #dedede;
    }
    .share-guide .share-btn{
      position: absolute;
      margin:0;
      padding:0;
      bottom:40rpx;
      left:35rpx;
      width:340rpx;
      height:180rpx;
      background:none;
    }
    .share-guide .share-btn:after{
      border:none;
    }
    .share-quan{
      margin:20rpx;
      overflow: hidden;
    }
    .share-quan .share-img{
      display:block;
      margin:10rpx auto 28rpx;
      height:600rpx;
      width:400rpx;
      border-radius:8rpx;
      box-shadow:0 0 10rpx #cdcdcd;
    }
    .share-quan .share-img-load{
      display:block;
      margin:260rpx auto 20rpx;
      height:80rpx;
      width:80rpx;
    }
    .share-quan .share-load-text{
      margin:0 auto 220rpx;
      display:block;
      widows:100%;
      text-align:center;
      font-size:28rpx;
      color:#b7b7b7;
    }
    .share-quan .save-btn{
      width:710rpx;
      height:80rpx;
      line-height:80rpx;
      color:#fff;
      text-align:center;
      letter-spacing:4rpx;
      background-color:#e2633f;
      border-radius:6rpx;
      font-size:34rpx;
    }
    .share-quan .save-tip{
      margin:18rpx;
      text-align:center;
      font-size:24rpx;
    }
    

    ↓组件开发已经完成,接下去是组件的使用↓

    1.demo.json
    备注:usingComponents加入对应组件配置即可

    {
      "navigationBarBackgroundColor": "#ffffff",
      "navigationBarTextStyle": "black",
      "navigationBarTitleText": "xxx",
      "usingComponents": {
        "share-win": "/component/share/share"
      }
    }
    

    2.demo.wxml
    备注:点击分享按钮的时候 showShareWin值改变,shareInfo根据渲染需求赋值

    <view>
        <!-- S 其他业务代码 -->
        <!-- E 其他业务代码 -->
    
        <!-- 分享 -->
        <canvas canvas-id="shareCanvas" style="position:fixed;top:0;left:999rpx;width:750px;height:1125px;"></canvas>
        <share-win wx:if="{{showShareWin}}" share-info="{{shareInfo}}" bindcloseshare="closeShareWin"></share-win>
    
    </view>
    

    3.demo.js
    备注:删除了其他业务代码,仅剩和分享的交互,便于阅读。shareInfo数据在load时就塞进去了,下面没有放出来~

    Page({
      data: {
        showTop:false
      },
      //点击右侧悬浮的分享按钮
      doShare:function(){
        this.setData({
          showShareWin:true
        })
      },
     //触发关闭分享弹框
      closeShareWin:function(){
        this.setData({
          showShareWin:false
        })
      },
    })
    
    

    其他说明:
    步骤4为最终生成效果图,微信识别二维码就可定位到具体业务页~

    有问题可留言交流~

    完...

    相关文章

      网友评论

        本文标题:【微信小程序】分享朋友圈组件开发实践

        本文链接:https://www.haomeiwen.com/subject/jcksnctx.html