微信小程序开发

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

2020-04-15  本文已影响0人  Ever月亮
背景

在一个完整带用户交互的小程序项目开发中,总会遇到分享这个功能,转发给好友用通用的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为最终生成效果图,微信识别二维码就可定位到具体业务页~

有问题可留言交流~

完...

上一篇下一篇

猜你喜欢

热点阅读