小程序 canvas 2d 新接口 绘制带小程序码的海报图

截止2020.3.26,小程序官方文档中,有两种绘制方式:Canvas 2D、webGL

文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html

而开发者工具中,官方推荐使用性能更好的2d模式,用法如下所示:

<canvas type="2d" id="myCanvas"></canvas>

但是网上大多数教程都是使用旧的接口,如:

<canvas canvas-id="canvasBox"></canvas>

本着学习和为后来人踩坑的目的,我们来尝试一下新接口,迎接未知的挑战 :)

需要注意的是:官方文档中CanvasContext的一些函数,在Canvas 2d模式下已经失效,这点,官方用了一句话做了描述:

canvas 组件的绘图上下文。CanvasContext 是旧版的接口, 新版 Canvas 2D 接口与 Web 一致。

出处:https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html

举个例子,比如设置填充色:

// 旧方式:
ctx.setFillStyle(‘red‘) // 在Canvas 2d 下会报错

// 新方式:
ctx.fillStyle = "red"; 

所以针对新接口的方法,可以参考html5的canvas api。

最终效果

下面就让我们抽丝剥茧,细细剖析。

以下代码均在官方开发者工具下编写



步骤:

wxml文件中,加入canvas标签以及保存按钮:

<canvas type="2d" id="canvasBox"></canvas>

js文件中:

1.设置数据:数据就相当于所有交通的枢纽

data: {

    // 数据区,从服务端拿到的数据
    name: "作者 Alpiny",    // 姓名
    phone: "13988887777",  // 电话
    posterUrl: "https://desk-fd.zol-img.com.cn/t_s1024x1024c5/g5/M00/00/0A/ChMkJlmfw7CIBpnCAAD3xQrT42EAAf9sgAH1ycAAPfd598.jpg", // 海报地址
    photoUrl:  "https://img2.woyaogexing.com/2020/03/27/3698eb92b78246e99d859f97f4227936!400x400.jpeg",                         // 头像地址
    qrcodeUrl: "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=144549786,228270254&fm=26&gp=0.jpg",                  // 小程序二维码

    // 设置区,针对部件的数据设置
    photoDiam: 50,                // 头像直径
    qrcodeDiam: 80,               // 小程序码直径
    infoSpace: 13,                // 底部信息的间距
    saveImageWidth: 500,          // 保存的图像宽度
    bottomInfoHeight: 100,        // 底部信息区高度
    tips: "微信扫码或长按了解更多",   // 提示语

    // 缓冲区,无需手动设定
    canvasWidth: 0,               // 画布宽
    canvasHeight: 0,              // 画布高
    canvasDom: null,              // 画布dom对象
    canvas:null,                  // 画布的节点
    ctx: null,                    // 画布的上下文
    dpr: 1,                       // 设备的像素比
    posterHeight: 0,              // 海报高
  },

这里数据分了三类:数据区是后端传送来的数据、设置区是可以定制画面的数据、缓冲区是用来暂存一些临时的数据,无需设置。

2.onReady 钩子中,执行 drawImage 函数

onReady: function () {
    this.drawImage()
  },

放到 onReady里的目的,是为了一进入页面就直接渲染画面。

3.创建drawImage函数,用来选择canvas节点并准备绘图:

// 查询节点信息,并准备绘制图像
  drawImage() {
    const query = wx.createSelectorQuery()  // 创建一个dom元素节点查询器
    query.select(‘#canvasBox‘)              // 选择我们的canvas节点
      .fields({                             // 需要获取的节点相关信息
        node: true,                         // 是否返回节点对应的 Node 实例
        size: true                          // 是否返回节点尺寸(width height)
      }).exec((res) => {                    // 执行针对这个节点的所有请求,exec((res) => {alpiny})  这里是一个回调函数

        const dom = res[0]                            // 因为页面只存在一个画布,所以我们要的dom数据就是 res数组的第一个元素
        const canvas = dom.node                       // canvas就是我们要操作的画布节点
        const ctx = canvas.getContext(‘2d‘)           // 以2d模式,获取一个画布节点的上下文对象
        const dpr = wx.getSystemInfoSync().pixelRatio // 获取设备的像素比,未来整体画布根据像素比扩大
        this.setData({
          canvasDom: dom,   // 把canvas的dom对象放到全局
          canvas: canvas,   // 把canvas的节点放到全局
          ctx: ctx,         // 把canvas 2d的上下文放到全局
          dpr: dpr          // 屏幕像素比
        },function(){
          this.drawing()    // 开始绘图
        })
      })
      // 对以上设置不明白的朋友
      // 可以参考 createSelectorQuery 的api地址
      //https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createSelectorQuery.html
  },

看,上面的代码第20行执行了drawing函数,drawimg 函数里制定了绘制的整体流程,下面我们来创建它。

4.创建 drawimg 函数

// 绘制画面
  drawing() {
    const that = this;
    wx.showLoading({title:"生成中"}) // 显示loading
    that.drawPoster()               // 绘制海报
      .then(function () {           // 这里用同步阻塞一下,因为需要先拿到海报的高度计算整体画布的高度
        that.drawInfoBg()           // 绘制底部白色背景
        that.drawPhoto()            // 绘制头像
        that.drawQrcode()           // 绘制小程序码
        that.drawText()             // 绘制文字
        wx.hideLoading()            // 隐藏loading
      })
  },

这其中要注意的是,为了让最终生成的图片自适应高,所以要提前拿到海报的高度来设置画布,所以,第一步绘制海报是阻塞运行的(采用了Promise来完成阻塞)。

5.创建 drawPoster 函数,绘制海报

// 绘制海报
  drawPoster() {
    const that = this
    return new Promise(function (resolve, reject) {
      let poster = that.data.canvas.createImage();          // 创建一个图片对象
      poster.src = that.data.posterUrl                      // 图片对象地址赋值
      poster.onload = () => {
        that.computeCanvasSize(poster.width, poster.height) // 计算画布尺寸
          .then(function (res) {
            that.data.ctx.drawImage(poster, 0, 0, poster.width, poster.height, 0, 0, res.width, res.height);
            resolve()
          })
      }
    })
  },

而drawPoster大约第7行,又进行了阻塞,是因为,我们要用拿到的海报数据先设置一下画布,否则直接绘图会导致失败。

6.创建 computeCanvasSize 函数,用来计算画布尺寸

// 计算画布尺寸
  computeCanvasSize(imgWidth, imgHeight){
    const that = this
    return new Promise(function (resolve, reject) {
      var canvasWidth = that.data.canvasDom.width                   // 获取画布宽度
      var posterHeight = canvasWidth * (imgHeight / imgWidth)       // 计算海报高度
      var canvasHeight = posterHeight + that.data.bottomInfoHeight  // 计算画布高度 海报高度+底部高度
      that.setData({
        canvasWidth: canvasWidth,                                   // 设置画布容器宽
        canvasHeight: canvasHeight,                                 // 设置画布容器高
        posterHeight: posterHeight                                  // 设置海报高
      }, () => { // 设置成功后再返回
        that.data.canvas.width = that.data.canvasWidth * that.data.dpr // 设置画布宽
        that.data.canvas.height = canvasHeight * that.data.dpr         // 设置画布高
        that.data.ctx.scale(that.data.dpr, that.data.dpr)              // 根据像素比放大
        setTimeout(function(){
          resolve({ "width": canvasWidth, "height": posterHeight })    // 返回成功
        },1200)
      })
    })
  },

7.创建第4步所需的其他几个函数:drawInfoBg(绘制底部白色背景)、drawPhoto(绘制头像)、drawQrcode(绘制二维码)、drawText(绘制文本)、alpiny(作者本人)

  // 绘制白色背景
  // 注意:这里使用save 和 restore 来模拟图层的概念,防止污染
  drawInfoBg() {
    this.data.ctx.save();
    this.data.ctx.fillStyle = "#ffffff";                                         // 设置画布背景色
    this.data.ctx.fillRect(0, this.data.canvasHeight - this.data.bottomInfoHeight, this.data.canvasWidth, this.data.bottomInfoHeight); // 填充整个画布
    this.data.ctx.restore();
  },

  // 绘制头像
  drawPhoto() {
    let photoDiam = this.data.photoDiam               // 头像路径
    let photo = this.data.canvas.createImage();       // 创建一个图片对象
    photo.src = this.data.photoUrl                    // 图片对象地址赋值
    photo.onload = () => {
      let radius = photoDiam / 2                      // 圆形头像的半径
      let x = this.data.infoSpace                     // 左上角相对X轴的距离
      let y = this.data.canvasHeight - photoDiam - 35 // 左上角相对Y轴的距离 :整体高度 - 头像直径 - 微调
      this.data.ctx.save()
      this.data.ctx.arc(x + radius, y + radius, radius, 0, 2 * Math.PI) // arc方法画曲线,按照中心点坐标计算,所以要加上半径
      this.data.ctx.clip()
      this.data.ctx.drawImage(photo, 0, 0, photo.width, photo.height, x, y, photoDiam, photoDiam) // 详见 drawImage 用法
      this.data.ctx.restore();
    }
  },
  // 绘制小程序码
  drawQrcode() {
    let diam = this.data.qrcodeDiam                    // 小程序码直径
    let qrcode = this.data.canvas.createImage();       // 创建一个图片对象
    qrcode.src = this.data.qrcodeUrl                   // 图片对象地址赋值
    qrcode.onload = () => {
      let radius = diam / 2                                             // 半径,alpiny敲碎了键盘
      let x = this.data.canvasWidth - this.data.infoSpace - diam        // 左上角相对X轴的距离:画布宽 - 间隔 - 直径
      let y = this.data.canvasHeight - this.data.infoSpace - diam + 5   // 左上角相对Y轴的距离 :画布高 - 间隔 - 直径 + 微调
      this.data.ctx.save()
      this.data.ctx.arc(x + radius, y + radius, radius, 0, 2 * Math.PI) // arc方法画曲线,按照中心点坐标计算,所以要加上半径
      this.data.ctx.clip()
      this.data.ctx.drawImage(qrcode, 0, 0, qrcode.width, qrcode.height, x, y, diam, diam) // 详见 drawImage 用法
      this.data.ctx.restore();
    }
  },
  // 绘制文字
  drawText() {
    const infoSpace = this.data.infoSpace         // 下面数据间距
    const photoDiam = this.data.photoDiam         // 圆形头像的直径
    this.data.ctx.save();
    this.data.ctx.font = "14px Arial";             // 设置字体大小
    this.data.ctx.fillStyle = "#333333";           // 设置文字颜色
    // 姓名(距左:间距 + 头像直径 + 间距)(距下:总高 - 间距 - 文字高 - 头像直径 + 下移一点 )
    this.data.ctx.fillText(this.data.name, infoSpace * 2 + photoDiam, this.data.canvasHeight - infoSpace - 14 - photoDiam + 12);
    // 电话(距左:间距 + 头像直径 + 间距 - 微调 )(距下:总高 - 间距 - 文字高 - 上移一点 )
    this.data.ctx.fillText(this.data.phone, infoSpace * 2 + photoDiam - 2, this.data.canvasHeight - infoSpace - 14 - 16);
    // 提示语(距左:间距 )(距下:总高 - 间距 )
    this.data.ctx.fillText(this.data.tips, infoSpace, this.data.canvasHeight - infoSpace);
    this.data.ctx.restore();
  },

到此,在开发者工具中,你应该可以预览到画面啦~!

至于保存图片的部分,代码我就不贴了。留一些给大家去思考、探索,学无止境。

至于小程序码图片的获取,不在本文范围内,大致思路是 后端拿着 appid和key 去微信 api 获取 token,然后拿着token再获取小程序二维码。其实我也还没做到这。: )

对文中有不理解的地方,欢迎留言探讨。创作不易,转载请留下出处。

Promise

原文地址:https://www.cnblogs.com/alpiny/p/12574017.html

时间: 2024-10-13 02:27:02

小程序 canvas 2d 新接口 绘制带小程序码的海报图的相关文章

用工具实现在微信里面生成带二维码的海报?

公众号粉丝裂变.快速增粉,利用微信第三方平台,自动生成海报二维码,实现统计粉丝来源查询统计系统,将营销活动设计成一张精美的海报背景图,粉丝就可以在公众号里生成一个带自己微信头像.昵称及带统计的专属二维码活动海报,通过活动激励方式让粉丝分享到朋友圈,一而二,二而三--源源不断分裂形成风暴增长! 1.在高级功能中选择->生成二维码海报注意:海报二维码功能状态 开启/关闭 ⑴数据统计报表,查看每天粉丝数量情况汇总 ⑵生成海报设置生成海报触发关键词 ⑶消息提醒设置(扫二维码后推送信息/设置扫二维码数量限

手动设置系统默认程序版本(可以替换系统自带同名程序)

在root下一切正常,在当前用户下就出现了问题.如下所示: [email protected]:/usr/local$ java -version 程序“java”已包含在下列软件包中: * gcj-4.4-jre-headless  * openjdk-6-jre-headless  * cacao  * gij-4.3  * jamvm 请尝试:sudo apt-get install <选定的软件包> 解决方法:(手动设置系统默认JDK) 在终端中依次输入命令: jdk1.6.0_26

微信小程序 | canvas绘图

1.新的尺寸单位 rpx rpx(responsive pixel): 可以根据屏幕宽度进行自适应. 规定屏幕宽为750rpx.如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素. 设备 rpx换算px (屏幕宽度/750): px换算rpx (750/屏幕宽度) 2.小程序canvas总结 小程序绘制的单位是px, 基础知识点请参考文档 : canvas中存在一个坐标系,二维的,左上

新的JavaScript库邀请程序员使用Canvas进行创意编程

为了模糊技术和艺术之间的界限,Sandpit库使用JavaScript和Canvas 2D元素进行创意编程. 通过GitHub或NPM并内置在ECMAScript 6中,Sandpit仍然使用着一个可能会在1.0版本之前更改的API进行开发."Sandpit的目标是规范和简化创建编码的过程,利用代码来做漂亮的东西,"文档介绍里提到."不管是在2D还是3D中,通常都会绘制到Canvas元素中."Sandpit使用dat.GUI(一个改变JavaScript中的变量的轻

小程序云函数调用webservice接口

https://www.jianshu.com/p/2692e56251ac 小程序最近新出来了云开发能力,主要依赖了node.js(运行在服务器上的js),可以让我们在没有服务器的情况下,使用云开发的数据库,编写云函数(相当于接口)来完成一整套小程序的开发,后端什么的,我们不需要! 以上粗略介绍一下云开发的能力,下面言归正传,关于调用webservice接口(wsdl),小程序在不使用云函数直接用普通开发模式的js通过request也能请求,但这样存在的缺点是: 1.域名必须认证(否则手机只能

小程序canvas文本换行生成图片

一.图片透明及旋转 let ctx = wx.createCanvasContext('shareImg') ctx.drawImage('../../../' + res[0].path, 0, 0, w, h) ctx.save() ctx.globalAlpha = 0.2 ctx.rotate(-15*Math.PI/180) 二.字体加粗 let text = '这是加粗的文本' ctx.fillText(text, 194, 378 + 0.5) ctx.fillText(text,

微信小程序的Web API接口设计及常见接口实现

微信小程序给我们提供了一个很好的开发平台,可以用于展现各种数据和实现丰富的功能,通过小程序的请求Web API 平台获取JSON数据后,可以在小程序界面上进行数据的动态展示.在数据的关键 一环中,我们设计和编写Web API平台是非常重要的,通过这个我们可以实现数据的集中控制和管理,本篇随笔介绍基于Asp.NET MVC的Web API接口层的设计和常见接口代码的展示,以便展示我们常规Web API接口层的接口代码设计.参数的处理等内容. 1.Web API整体性的架构设计 我们整体性的架构设计

微信小程序&amp;PHP 微信支付接口调用

小程序端 /** * 微信支付接口 */ wxPaymoney:function (out_trade_no, true_money){ //out_trade_no 后台统一下单接口需要用 var that = this wx.hideToast() //隐藏toast wx.request({ method: 'POST', data: { openid: '************',    //调用人的openid out_trade_no: out_trade_no, body: '答

小程序开发运营必看:微信小程序平台运营规范

一.原则及相关说明 ? 微信最核心的价值,就是连接——提供一对一.一对多和多对多的连接方式,从而实现人与人.人与智能终端.人与社交化娱乐.人与硬件设备的连接,同时连接服务.资讯.商业. ? 微信团队一直致力于将微信打造成一个强大的.全方位的服务工具.在此基础上,我们推出了微信小程序这个产品,提供给微信小程序的开发者在微信内搭建和实现特定服务.功能的平台.通过全面开放的能力,我们将更多连接的可能给予企业和服务提供者,并为微信小程序提供基础的接入能力.运营环境和规则体系,进而帮助更多的企业和服务提供