使用微信小程序生成海报

如何自适应实现微信小程序生成海报

前言

说实话,刚收到这个需求时,领导给我演示了一下,以为是个很高大上的功能。找了领导给的小程序生成海报功能的图片做测试,经过我不懈的 努力后 百度后,发现这原来只不过是canvas生成一张图片罢了。,那么问题来了,什么是canvas,呃呃,canvas就是。。。
读者:废话少说,先给看看效果吧,我:好嘞,客官

补充:手机上图片需要网络图片,且先下载准备临时图片地址,最下面有代码演示

(https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html).

效果

原图
在这里插入图片描述
点击生成海报
在这里插入图片描述
啪,灯神出。。。
在这里插入图片描述
好了,效果图也看完了,是不是你想要的效果?如果不是你也不用看下去了。好,接下来咱进入下一个阶段,代码实现

还想多说几句

因为我百度了其他大佬们的代码,发现他们居然没怎么考虑自适应问题。小程序本身就自带了rpx这样的玩意,那我们也就更方便了。先把设计稿放到ps里,Alt+i+i将设计稿缩小为750px的宽,然后直接测量每一个元素的坐标,如下图(相信大家都是一个成熟的前端了,怎么用测量工具就不使用用我来过多说了)
在这里插入图片描述
这次真的不多说了,下面上代码

wxml

<!--pages/detail/index.wxml-->
<view class="container">
  <view class="btn">
    <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">
      <view class="text">生成海报</view>
    </button>
  </view>

  <!-- 生成海报 -->
  <view 
    class="poster-box"
    wx:if='{{isPhotoModel}}' 
    bindtap="setPhotoModel"
  >
    <canvas canvas-id="mycanvas" style="width:{{posterW}}px;height:{{posterW}}px; "/>
    <image  mode="widthFix" src='{{shareImagePath}}' wx:if='{{shareImagePath}}'></image>
    <view class="close-save">关闭并保存海报</view>
  </view>
</view>

CSS

/* pages/poster/index.wxss */
.container{
  padding-top: 400rpx;
}
/* 海报弹框 */
.poster-box{
  position: fixed;
  top:0;
  left:0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,.4);
  z-index: 100; 
}
.poster-box canvas{
  position: absolute;
  top:0;
  left:10000px;
  bottom:0;
  right:0;
  margin:auto;
  background: #fff;
}
.poster-box image{
  width: 90%;
  position: absolute;
  top:0;
  left:0;
  bottom:0;
  right:0;
  margin:auto;
}
.poster-box .close-save{
  width: 300rpx;
  position: absolute;
  left:0;
  bottom: 10%;
  right:0;
  margin:auto;
  border-radius: 10rpx;
  line-height: 80rpx;
  font-size: 30rpx;
  text-align: center;
  background: #fff;
  color: #aaa;
  }

JS

// pages/detail/index.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    // 分享海报图片
    shareImagePath: "",
    // 分享的小程序码
    qtsheXcxCode: "/images/image/wx-code.png",
    // 分享的用户姓名
    userName: "你的名字",
    // 分享的用户头像
    userHeadUrl: "/images/image/head-img.png",
    // 分享的海报背景图片
    qtsheBackground: "/images/image/poster-img.png",
    // 海报弹框
    isPhotoModel: false,
    // 海报宽高
    posterW: 0,
    posterH: 0,
    Rpx: 0,

  },

  /**
   * 功能函数 *
   */
  // 获取容器高度 提前设置好canvas宽高
  getContentHeight() {
    wx.getSystemInfo({
      success: (res) => {
        // console.log(res)
        this.setData({
          Rpx: res.windowWidth / 750,
          posterW: res.windowWidth,
          posterH: res.windowWidth,
        })
      }
    })
  },
  // 获取用户信息
  getUserInfo(res) {
    console.log(res)
    // 获取用户信息失败
    if (res.detail.errMsg !== "getUserInfo:ok") {
      return false;
    }
    this.setData({
      userName: res.detail.userInfo.nickName,
      // userHeadUrl: res.detail.userInfo.avatarUrl,
      // qtsheBackground: "",
    });
    this.createNewImg();
  },
  // 海报弹框隐藏显示
  setPhotoModel() {
    this.setData({
      isPhotoModel: !this.data.isPhotoModel
    })
  },
  /**
   * 海报绘制函数 *
   */
  //将canvas转换为图片保存到本地,然后将图片路径传给image图片的src
  createNewImg() {
    wx.showLoading({
      title: '正在生成海报...',
      mask: true
    })
    let that = this
    let ctx = wx.createCanvasContext('mycanvas')
    // 绘制背景
    this.setBg(ctx);
    //绘制背景图片
    this.setBgImage(ctx);
    // 绘制头像
    this.setAvatar(ctx);
    // 名字
    this.setName(ctx);
    // 名字下的标题1
    this.setText({ ctx: ctx, text: "邀请你免费学习", size: 30, x: 150, y: 412, color: "#A2A2A2" });
    // 名字下的标题2
    this.setText({ ctx: ctx, text: "混沌学院", size: 30, x: 393, y: 412, color: "#c1bba8" });
    // 名字下的标题3
    this.setText({ ctx: ctx, text: "重磅好课", size: 30, x: 536, y: 412, color: "#A2A2A2" });
    // 绘制线条
    this.setLine(ctx);
    // 绘制小程序码
    this.setQrcode(ctx);
    // 小程序码旁边的文字
    this.setText({ ctx: ctx, text: "扫码升级你的思维", size: 30, x: 257, y: 577, color: "#d3d3d3" });
    this.setText({ ctx: ctx, text: "开课后72小时内免费学习此课程", size: 30, x: 257, y: 634, color: "#d3d3d3" });
    // 结束绘制
    ctx.draw()
    // 保存
    ctx.save()
    // 展示海报弹框
    this.setPhotoModel();
    //将生成好的图片保存到本地,需要延迟一会,绘制期间耗时
    setTimeout(() => {
      wx.canvasToTempFilePath({
        canvasId: 'mycanvas',
        success: function (res) {
          console.log(res)
          wx.hideLoading()
          that.setData({
            shareImagePath: res.tempFilePath
          });
        },
        fail: function (res) {
          console.log(res.errMsg)
          wx.showToast({
            title: '保存失败,请刷新页面重试',
            icon: 'none'
          })
        }
      }, this)
    }, 2000)
  },
  // 绘制背景
  setBg(ctx) {
    let { Rpx } = this.data
    let W = 750 * Rpx
    let H = 750 * Rpx
    ctx.setFillStyle('#FFFFFF')
    ctx.fillRect(0, 0, W, H)
  },
  // 绘制背景海报
  setBgImage(ctx) {
    let { qtsheBackground } = this.data
    this.setImage({ ctx: ctx, imageUrl: qtsheBackground, x: 0, y: 0, W: 750, H: 305 })
  },
  /*
  * 绘制用户头像
  * cx cy 是圆心到画布原点坐标
  * r*2是上面圆的直径,也是头像的宽高
  */
  setAvatar(ctx) {
    let { Rpx } = this.data
    // 先画个圆形区域
    let cx = 83 * Rpx;
    let cy = 400 * Rpx;
    let r = 50 * Rpx;
    ctx.arc(cx, cy, r, 0, 2 * Math.PI);
    ctx.clip();
    // 将头像绘制在圆里边
    let path = this.data.userHeadUrl;
    this.setImage({ ctx: ctx, imageUrl: path, x: 35, y: 353, W: 100, H: 100 })
  },
  // 昵称绘制
  setName(ctx) {
    let text = this.data.userName + "我是测试昵称";
    if (text.length >= 8) {
      text = text.substring(0, 7) + '...'
    }
    this.setText({ ctx: ctx, text: text, size: 34, x: 150, y: 354, color: "#000" });
  },
  /**
   * @title 文字绘制
   * @params obj object 绘制对象
   * @params obj.ctx object 画布对象
   * @params obj.text string 绘制文字
   * @params obj.size number 绘制文字尺寸
   * @params obj.x number 绘制文字x坐标
   * @params obj.y number 绘制文字y坐标
   * @params obj.color string 绘制文字颜色
   */
  setText(obj) {
    let { Rpx } = this.data
    let ctx = obj.ctx;
    let text = obj.text || "";
    let size = obj.size * Rpx || 30;
    let x = obj.x * Rpx || 0;
    let y = obj.y * Rpx || 0;
    let color = obj.color || "black";
    ctx.setTextBaseline('top')
    ctx.setFillStyle(color)
    ctx.setFontSize(size)
    ctx.fillText(text, x, y)
  },
  /**
   * @title 图片绘制
   * @params obj object 绘制对象
   * @params obj.ctx object 画布对象
   * @params obj.imageUrl string 绘制图片地址(真实地址)
   * @params obj.x number 绘制图片x坐标
   * @params obj.y number 绘制图片y坐标
   * @params obj.W number 绘制图片宽
   * @params obj.H string 绘制图片高
   */
  setImage(obj) {
    let { Rpx } = this.data
    let ctx = obj.ctx;
    let imageUrl = obj.imageUrl || "";
    let x = obj.x * Rpx || 0;
    let y = obj.y * Rpx || 0;
    let W = obj.W * Rpx || 0;
    let H = obj.H * Rpx || 0;
    ctx.drawImage(imageUrl, x, y, W, H);
  },
  // 绘制昵称下的线条
  setLine(ctx) {
    let { Rpx } = this.data
    let x1 = 50 * Rpx;
    let y1 = 493 * Rpx;
    let x2 = 700 * Rpx;
    let y2 = 493 * Rpx;
    ctx.setStrokeStyle('#d8d8d8')
    ctx.moveTo(x1, y1)
    ctx.lineTo(x2, y2)
    ctx.closePath()
    ctx.stroke()
  },
  // 绘制小程序码
  setQrcode(ctx) {
    let { qtsheXcxCode } = this.data
    this.setImage({ ctx: ctx, imageUrl: qtsheXcxCode, x: 50, y: 534, W: 180, H: 180 })
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    // 获取容器高度 需要提前预设好canvas的宽高啊 提前预设好canvas的宽高啊 提前预设好canvas的宽高啊 
    this.getContentHeight();
  },
})

下载网络图片保存临时路径

这是一个关键步骤,很重要,我们需要先将网络图片保存到本地,再去绘制海报,图片地址路径需要加入下载白名单
代码如下


  /**
   * 保存网络图片临时地址
   * @imgSrc string 图片地址
   * @dataValue string 图片地址存储属性
   * @func function 回调函数
   */ 
  savaImageUrl({ imgSrc, dataValue, func}={}){
    let that = this;
    wx.downloadFile({
      url: imgSrc,
      success: function (res) {
        console.log(res);
        that.setData({ [dataValue]: res.tempFilePath})
        func();
      },
      fail: function (res) {
        console.log(res.errMsg)
        wx.showToast({
          title: '保存失败,请刷新页面重试',
          icon: 'none'
        })
      }
    })
  },

当需要下载多张图片时, 调用演示如下

  // 获取用户信息 该函数挂在小程序的button上
  getUserInfo(res){
    console.log(res)
    // 获取用户信息失败
    if (res.detail.errMsg !== "getUserInfo:ok"){
      return false;
    }
    this.setData({
      userName: res.detail.userInfo.nickName,
      userHeadUrl: res.detail.userInfo.avatarUrl,
    });
    const { qtsheXcxCode, qtsheBackground ,userHeadUrl } = this.data;
    wx.showLoading({
      title: '正在生成海报...',
      mask: true
    })
    const func1 = ()=>{ 
      this.savaImageUrl({
        imgSrc: userHeadUrl,
        dataValue: "userHeadUrl1",
        func:()=>{
          this.createNewImg()
        } 
      })
    }
    const func2 = ()=>{
      this.savaImageUrl({
        imgSrc: qtsheXcxCode,
        dataValue: "qtsheXcxCode1",
        func: func1
      })
    }
    const func3 = ()=>{
      this.savaImageUrl({
        imgSrc: qtsheBackground,
        dataValue: "qtsheBackground1",
        func: func2
      })
    }
    func3();
  },

保存海报

弄个按钮点击保存当前生成的海报,放在生成canvas图片函数里也行

  // 保存海报
  saveImg() {
    let that = this;
    // 获取用户是否开启用户授权相册
    wx.getSetting({
      success(res) {
        // 如果没有则获取授权
        if (!res.authSetting['scope.writePhotosAlbum']) {
          wx.authorize({
            scope: 'scope.writePhotosAlbum',
            success() {
              wx.saveImageToPhotosAlbum({
                filePath: that.data.shareImagePath,
                success() {
                  wx.showToast({
                    title: '保存成功'
                  })
                },
                fail() {
                  wx.showToast({
                    title: '保存失败',
                    icon: 'none'
                  })
                }
              })
            },
            fail() {
              // 如果用户拒绝过或没有授权,则再次打开分享口
            }
          })
        } else {
          // 有则直接保存
          wx.saveImageToPhotosAlbum({
            filePath: that.data.shareImagePath,
            success() {
              wx.showToast({
                title: '保存成功'
              })
            },
            fail() {
              wx.showToast({
                title: '保存失败',
                icon: 'none'
              })
            }
          })
        }
      }
    })
  },

结语

看完了吧,canvas真的难吗,难!但是我们现在想要实现的效果难吗?现在这个效果就是切图画界面嘛。多去看看文档和大佬们的代码,有些以前的难,也就不难了

需要提前预设好canvas的宽高啊 提前预设好canvas的宽高啊 提前预设好canvas的宽高啊 重要的事情说三遍

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值