微信小程序生成海报

214 阅读5分钟

1、前言

适用场景为:一些商品的海报宣传,个人名片宣传等场景。

源码地址为:gitee.com/acher_Saber…

效果为:

2.gif

2、核心代码

poster.wxml 文件

<view class="bg"></view>
<view class="PageCen">
    <view class="tit">海报分享</view>
    <view class="flex-box-center">
        <view class="yellowLine"></view>
    </view>
    <view class="main">
        <swiper class="gundong" circular bindchange="changeHB" previous-margin="100rpx" next-margin="100rpx">
            <block wx:for="{{ shareImgs }}" wx:for-item="item" wx:key="index">
                <swiper-item class="gundongItem">
                    <view class="Item {{currentIndex == index ? '' : 'smItem'}}">
                        <!-- 海报分享 -->
                        <view class="shareImg">
                            <image style="width:100%;height:100%" src="{{item.img_url}}" />
                        </view>
                        <!-- 二维码 -->
                        <view class="code">
                            <view class="img">
                                <image style="width: 100rpx;height:100rpx;" class="{{currentIndex == index ? '' : 'smCode'}}" src="{{item.code_url}}" />
                            </view>
                            <view class="code_txt">
                                <text>长按扫码</text>
                                <text style="font-weight:bold">J1ay ' blogs</text>
                                <text style="font-size: 14rpx;color: #999999;line-height: 20rpx;">每一個想要學習的念頭,那有可能是未來的你在向你求救。</text>
                            </view>
                        </view>
                    </view>
                </swiper-item>
            </block>
        </swiper>
    </view>

    <!-- 保存按钮 -->
    <button class="btn green" bindtap="saveHaiBao">
        <view class="btnTxt">保存海报</view>
    </button>
</view>

<!-- canvas绘制海报 -->
<canvas type="2d" class="hide" id="share"></canvas>

poster.js 文件

Page({
  data: {
    // 网图
    // shareImgs:[
    //   {id: 0, img_url: "https://z3.ax1x.com/2021/09/04/h2JPgO.jpg",  code_url: "https://z3.ax1x.com/2021/09/04/h2tH8P.png"},
    //   {id: 1, img_url: "https://z3.ax1x.com/2021/09/04/h282wR.jpg",  code_url: "https://z3.ax1x.com/2021/09/04/h2tXDg.png"},
    //   {id: 2, img_url: "https://z3.ax1x.com/2021/09/04/h2JcI1.jpg",  code_url: "https://z3.ax1x.com/2021/09/04/h2NQxK.png"},
    //   {id: 3, img_url: "https://z3.ax1x.com/2021/09/04/h2JzLQ.jpg",  code_url: "https://z3.ax1x.com/2021/09/04/h2tXDg.png"},
    //   {id: 4, img_url: "https://z3.ax1x.com/2021/09/04/h2tNcT.jpg", code_url: "https://z3.ax1x.com/2021/09/04/h2tH8P.png"}
    // ],
    // 本地图片
    shareImgs: [{
        id: 0,
        img_url: "../../../../static/posterImg/1.jpg",
        code_url: "../../../../static/posterImg/code1.png"
      },
      {
        id: 1,
        img_url: "../../../../static/posterImg/1.jpg",
        code_url: "../../../../static/posterImg/code1.png"
      },
      {
        id: 2,
        img_url: "../../../../static/posterImg/1.jpg",
        code_url: "../../../../static/posterImg/code1.png"
      },
      {
        id: 3,
        img_url: "../../../../static/posterImg/1.jpg",
        code_url: "../../../../static/posterImg/code1.png"
      }
    ],
    // 当前图片地址
    img_url: 'img/1.jpg',
    // 当前二维码地址
    code_url: 'img/code1.png',
    // 网络地址
    img_urls: 'https://z3.ax1x.com/2021/09/04/h2JPgO.jpg',
    code_urls: 'https://z3.ax1x.com/2021/09/04/h2tH8P.png',
    // 临时地址
    hbpath: '', // 海报
    codepath: '', // 二维码
    widths: '',
    heights: '',
    currentIndex: 0,
  },
  onLoad: function (options) {
    this.setData({
      widths: 480,
      heights: 854,
      img_url: this.data.shareImgs[0].img_url,
      code_url: this.data.shareImgs[0].code_url,
    })
  },

  // 切换海报的选择
  changeHB(e) {
    let index = e.detail.current ? e.detail.current : 0
    this.setData({
      img_url: this.data.shareImgs[index].img_url,
      code_url: this.data.shareImgs[index].code_url,
      currentIndex: index
    })
  },
  // 保存海报
  saveHaiBao() {
    // 若是图片是网络地址,则直接执行ImgUrlToTmp方法
    // this.ImgUrlToTmp(); 

    // 以下方法为本地图片
    this.setData({
      hbpath: this.data.img_url,
      codepath: this.data.code_url
    }, () => {
      let that = this
      that.sharePage()
    })
  },

  // 获取海报图片和二维码临时地址
  ImgUrlToTmp() {
    var that = this;
    wx.showLoading({
      title: '拼命生成中...',
    });
    wx.getImageInfo({
      src: that.data.img_url,
      success(res) {
        console.log("模板图片临时路径:" + res.path);
        that.setData({
          hbpath: res.path
        }, () => {
          console.log(that.data.code_url)
          wx.getImageInfo({
            src: that.data.code_url,
            success(res) {
              console.log("二维码临时路径:" + res.path);
              that.setData({
                codepath: res.path
              }, () => {
                that.sharePage()
              })
            },
            fail(e) {
              console.log(e)
            }
          })
        })
      },
      fail(e) {
        console.log(e)
      }
    })
  },

  // 生成分享海报
  sharePage() {
    var that = this;
    that.createNewImg();
  },

  //将canvas转换为图片保存到本地,然后将图片路径传给image图片的src
  createNewImg: function () {
    var that = this;
    // 获取画布节点
    wx.createSelectorQuery()
      .select('#share')
      .fields({
        node: true,
        size: true,
      })
      .exec(function (res) {
        console.log(res)
        // 获取画布canvas节点
        const canvas = res[0].node
        // 获取画布2d的context对象
        const context = canvas.getContext('2d')

        // 获取画布canvas节点设定的宽高
        const width = res[0].width
        const height = res[0].height

        // restore()恢复之前保存的绘图上下文。
        context.restore();

        // 取倍率, 1px = 2rpx
        const dpr = wx.getSystemInfoSync().pixelRatio

        // 设置画布宽度/高度
        canvas.width = width * dpr
        canvas.height = height * dpr

        // x,y轴缩放,这样做的目的是不同宽度的移动设备保证是1:1的图形比例
        context.scale(dpr, dpr)

        // clearRect清除画布上在该矩形区域内的内容
        context.clearRect(0, 0, width, height);

        // 画布填充得底色
        context.fillStyle = 'white'

        // 绘制矩形 fillRect(number x, number y, number width, number height)
        context.fillRect(0, 0, width, height)

        // save() 保存绘图上下文
        context.save();

        // 画海报
        var path = that.data.hbpath;
        const hbPromise = new Promise((resolve, reject) => {
          // 创建一个图片对象。 支持在 2D Canvas 和 WebGL Canvas 下使用, 但不支持混用 2D 和 WebGL 的方法。创建的hb是一个image对象
          const hb = canvas.createImage()

          // image对象加载成功调用
          hb.onload = () => {
            resolve(hb)
          }

          // image对象加载失败调用
          hb.onerror = () => {
            reject(new Error(`fail to fetch image form: ${path}`))
          }

          // image对象的src属性赋值为path
          hb.src = path
        })
        hbPromise.then(img => {
          // drawImage(string imageResource, number sx, number sy, number sWidth, number sHeight, number dx, number dy, number dWidth, number dHeight) 绘制图像到画布  , imageResource必须下载到本地
          context.drawImage(img, 0, 0, width, height * 0.8)
        })

        // 画二维码
        var codepath = that.data.codepath;
        const codePromise = new Promise((resolve, reject) => {
          const code = canvas.createImage()
          code.onload = () => {
            resolve(code)
          }
          code.onerror = () => {
            reject(new Error(`fail to fetch image form: ${codepath}`))
          }
          code.src = codepath
        })
        codePromise.then(img => {
          context.drawImage(img, 15, height * 0.83, 100, 100)
        })

        // 画话,绘制文本
        var t1 = "长按扫码1";
        var title = "J1ay ' blogs";
        var tishi = "每一個想要學習的念頭,那有可能是未來的你在向你求救。";

        // 绘制第一句文本
        context.fillStyle = '#333';
        context.fillText(t1, 130, height * 0.872);

        // 绘制第二句文本
        context.font = 'normal bold 13px sans-serif';
        context.fillText(title, 130, height * 0.9);

        // 绘制第三句文本
        context.fillStyle = '#999';
        context.font = 'normal 10px sans-serif';
        context.fillText(tishi, 130, height * 0.93);

        // 绘制
        context.stroke();

        // 保存
        context.save();

        setTimeout(() => {
          // 保存到本地
          that.toSave(canvas);
        }, 1000);
      });


  },
  // 打包海报
  toSave(canvas) {
    console.log(canvas)
    let that = this
    // wx.canvasToTempFilePath() 把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功。
    wx.canvasToTempFilePath({
      x: 0,
      y: 0,
      canvasId: 'share',
      canvas: canvas,
      width: that.data.widths,
      height: that.data.heights,
      destWidth: that.data.widths * wx.getSystemInfoSync().pixelRatio,
      destHeight: that.data.heights * wx.getSystemInfoSync().pixelRatio,
      success: function (res) {
        let canvasToTempFilePath = res.tempFilePath // 返回的图片地址保存到一个全局变量里
        // console.log(res)
        that.saveShareImg(canvasToTempFilePath)
      },
      fail: function (error) {
        console.log(error)
      }
    })
  },
  // 保存到系统相册
  saveShareImg: function (canvasToTempFilePath) {
    // 判断是否授权
    wx.getSetting({
      success(res) {
        wx.hideLoading()
        // 无打开相册权限
        if (!res.authSetting['scope.writePhotosAlbum']) {
          wx.authorize({
            scope: 'scope.writePhotosAlbum',
            success() {
              // 保存图片到相册
              wx.saveImageToPhotosAlbum({
                filePath: canvasToTempFilePath,
                success() {
                  wx.showToast({
                    title: '保存成功',
                    icon: 'success',
                    duration: 2000
                  })
                },
                fail() {
                  wx.showToast({
                    title: '保存失败',
                    icon: 'none'
                  })
                }
              })
            },
            fail() {
              wx.showModal({
                title: '提示',
                content: '请设置允许访问相册,否则将无法使用该功能',
                showCancel: false,
                success(res) {
                  if (res.confirm) {
                    console.log('用户点击确定')
                    // 重新设置权限
                    wx.openSetting({
                      success(res) {
                        // 重新设置相册权限成功,就保存当前图片到相册
                        wx.saveImageToPhotosAlbum({
                          filePath: canvasToTempFilePath,
                          success() {
                            wx.showToast({
                              title: '保存成功',
                              icon: 'success',
                              duration: 2000
                            })
                          },
                          fail() {
                            wx.showToast({
                              title: '保存失败',
                              icon: 'error'
                            })
                          }
                        })
                      },
                      fail(err) {
                        console.log(err)
                      }
                    })

                  } else if (res.cancel) {
                    console.log('用户点击取消')
                  }
                }
              })
            }
          })
        } else {
          wx.saveImageToPhotosAlbum({
            filePath: canvasToTempFilePath,
            success() {
              wx.showToast({
                title: '保存成功',
                icon: 'success',
                duration: 2000
              })
            },
            fail() {
              wx.showToast({
                title: '保存失败',
                icon: 'error'
              })
            }
          })
        }
      },
      fail() {}
    });
  },

  onShareAppMessage: function (res) {
    let conf_ = getApp().globalData.cofData;
    let openid = wx.getStorageSync('openid') ? wx.getStorageSync('openid') : getApp().globalData.openid;
    return {
      title: conf_.inviteTxt,
      path: "/pages/index/index?oid=" + openid,
      imageUrl: conf_.inviteImg,
    }
  }
})

poster.json 文件

{
  "usingComponents": {},
  "navigationBarTitleText": "海报"
}

poster.wxss 文件

page {
    height: 100vh;
    display: flex;
    flex-direction: column;
    background: #fcf8f5;
}

.bg {
    position: absolute;
    width: 100%;
    height: 42%;
    background: linear-gradient(to bottom, #0b6d08 0%, #fcf4d4 100%);
    border-bottom-left-radius: 17.8%;
    border-bottom-right-radius: 17.8%;
}

.PageCen {
    position: relative;
    width: 100%;
    flex: 1;
}

.tit {
    margin-top: 10vh;
    font-size: 36rpx;
    color: white;
    line-height: 36rpx;
    text-align: center;
    position: relative;
    top: 0;
    z-index: 9;
}

.yellowLine {
    width: 50rpx;
    height: 10rpx;
    background: #FFD500;
    border-radius: 7rpx;
    position: relative;
    z-index: 9;
    top: 26rpx;
}

.yellow {
    width: 95rpx;
    height: 41.78%;
    background: #FFD552;
    position: absolute;
    top: 20.18%;
    z-index: 9;
}

.right {
    right: 0;
}

.main {
    position: relative;
    z-index: 9;
    top: 40rpx;
    width: 100vw;
    height: 63vh;
}

.haibao {
    display: flex;
    justify-content: center;
    margin-top: 30rpx;
}

.gundong {
    height: 65vh;
    margin-top: 30rpx;
}

.gundongItem {
    width: 66vw;
    height: 63vh;
    display: flex;
    justify-content: center;
}

.Item {
    width: 66vw;
    height: 63vh;
    background: #ffffff;
    box-shadow: 0px 0px 30rpx 0px rgba(250, 60, 50, 0.3);
}

.smItem {
    height: 51vh;
    margin-top: 5.5vh;
}

.smCode {
    width: 86rpx !important;
    height: 86rpx !important;
}

.shareImg {
    height: 80%;
    width: 100%;
    background: rgb(250, 237, 196);
}

.img {
    height: 100%;
    width: 150rpx;
    display: flex;
    justify-content: center;
    align-items: center;
}

.code {
    height: 20%;
    width: 100%;
    display: flex;
}

.code_txt {
    height: 100%;
    /* width: 150rpx; */
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* align-items:center; */
    font-family: PingFangSC-Regular, PingFang SC;
    font-weight: 400;
    color: #333333;
    line-height: 36rpx;
    font-size: 22rpx
}

.btn {
    width: 66vw;
    height: 80rpx;
    border-radius: 50rpx;
    position: relative;
    z-index: 9;
    top: 110rpx;
}

.red {
    background: linear-gradient(225deg, #FF9F67 0%, #FA312C 100%);
}

.blue {
    background: linear-gradient(to right, #6ebefc, #6149f6);
}

.green {
    background: linear-gradient(to left, #04cfa3, #209e87);
}

.btn .btnTxt {
    font-size: 34rpx;
    font-family: PingFangSC-Medium, PingFang SC;
    font-weight: 500;
    color: #FFFFFF;
    display: flex;
    justify-content: center;
    align-items: center;
}

.hide {
    position: fixed;
    left: 9999px;
    width: 480px;
    height: 854px;
}

.bimg {
    position: fixed;
    width: 100%;
    top: 0;
    left: 0;
    z-index: 1;
    height: 620rpx;
}