微信小程序onShareAppMessage自定义分享图片

506 阅读3分钟

背景描述

最近在开发一个建筑业招聘小程序,遇到一个模仿某直聘分享求职者简历的需求,根据求职者简历头像、姓名(脱敏)、掌握技能、期望工作地,生成一张分享图片。原型图效果如下。

image.png

需求梳理

  • 获取到求职者简历头像、姓名(脱敏)、掌握技能、期望工作地
  • 使用微信小程序canvas2d api 将内容渲染到画布的指定位置
  • 文本单行溢出显示省略号
  • 使用 onShareAppMessage 函数将渲染完成的图片分享至微信联系人

代码编写

wxml部分

在 wxml 文件中写入 canvas 标签,使用定位将其移动至视口范围之外,设置canvas标签宽高,onShareAppMessage 建议图片宽高比例为 5:4

<canvas type="2d" id="myCanvas" style="width: 300px; height: 240px;left:9000px;position:fixed;"></canvas>

js部分

封装一个函数,根据id获取canvas实例canvas context

// 获取canvas实例和ctx画笔
  getMyCanvasAndCtx(id) {
    //id  canvas 2d的id
    return new Promise((resolve) => {

      const query = wx.createSelectorQuery();

      query

        .select(`#${id}`)

        .fields({

          node: true,

          size: true,

        })

        .exec((res) => {

          const canvas = res[0].node;

          const ctx = canvas.getContext("2d");

          const dpr = wx.getSystemInfoSync().pixelRatio;

          canvas.width = res[0].width * dpr;

          canvas.height = res[0].height * dpr;

          ctx.scale(dpr, dpr);

          const data = {

            canvas,

            ctx,

          };

          resolve(data);

        });

    });

  },

获取到canvascanvas context 后开始进行圆形头像渲染工作

注意事项:在canvas上渲染图片,建议使用本地图片或者本地图片临时链接,如果使用网络图片可以通过wx.getImageInfo将其转换成本地图片临时链接。获取到链接后,需要使用canvas.createImage()先生成一个canvas图片实例,将获取到的图片链接赋值给实例的src属性。渲染图片的操作需要在canvas图片实例的onload回调中执行。

async initCanvas() {
    const { ctx, canvas } = await this.getMyCanvasAndCtx("myCanvas");
    // 画圆形头像
    wx.getImageInfo({
      // src请自行替换
      src: this.data.resume.headPic,
      success: (res) => {
        // 创建canvas2d image对象
        let img = canvas.createImage();
        img.src = res.path;
        // 图片加载完成后再画圆形头像
        img.onload = () => {
          this.drawRoundImage(ctx, img, 125, 30, 50, 50, 50);
        };
      },
    });
}

drawRoundImage 渲染圆形图片封装方法,参数 ctx - canvas context, img - 图片本地临时链接, x - 图片摆放位置x坐标, y - 图片摆放位置y坐标, w - 图片宽度 h - 图片高度, r - 弧度

drawRoundImage(ctx, img, x, y, w, h, r) {
   if (w < 2 * r) r = w / 2;
   if (h < 2 * r) r = h / 2;
   ctx.save();
   ctx.beginPath();
   ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);
   ctx.clip();
   ctx.drawImage(img, x, y, w, h);
},

获取到canvascanvas context 后开始进行圆形头像渲染工作

注意事项:在canvas上渲染文字,需要设置文字的字号和字体,否则有可能出现文字显示不出的问题。

async initCanvas() {
    ............省略渲染头像代码............
    
    // ---------- 文字渲染从此处开始 ----------
    // 文字最大宽度
    const maxWidth = 270;
    const { resume } = this.data;

    // 画姓名
    ctx.font = "24px Arial";
    ctx.textAlign = "center";
    ctx.fillText(resume.name, 150, 120);

    // 画工种技能
    if (resume.skills) {
       ctx.font = "18px Arial";
       ctx.textAlign = "center";
       ctx.fillStyle = "#666666";
       ctx.fillText(this.fittingString(ctx, resume.skills, maxWidth), 150, 160);
    }

    // 画期望工作地
    if (resume.expectWorkLocation) {
       ctx.font = "18px Arial";
       ctx.textAlign = "center";
       ctx.fillText(this.fittingString(ctx, resume.expectWorkLocation, maxWidth), 150, 200);
    }
}

fittingString单行文本溢出省略方法,参数 ctx - canvas context,str - 文本内容, maxWidth - 单行文本宽度,超出显示省略号

  fittingString(ctx, str, maxWidth) {
  
    // canvas measureText 方法可以得到文本的宽度
    
    let width = ctx.measureText(str).width;

    const ellipsis = "…";

    let ellipsisWidth = ctx.measureText(ellipsis).width;

    if (width <= maxWidth || width <= ellipsisWidth) {

      return str;

    } else {

      let len = str.length;

      while (width >= maxWidth - ellipsisWidth && len-- > 0) {

        str = str.substring(0, len);

        width = ctx.measureText(str).width;

      }

      return str + ellipsis;

    }

  },

保存画布,并导出画布为临时图片链接

async initCanvas() {
    ............省略渲染头像、渲染文字代码............
    // 保存画布

    ctx.save();

    // sleep 500毫秒,等待图片加载完成 不然有可能圆形头像没有加载出来

    await this.sleep(500);

    let url = null;
    
    // 需要自行封装 canvasToTempFilePath

    const res = await wx.p.canvasToTempFilePath({ canvas });

    url = res.tempFilePath;

    return url;
}

sleep函数代码

sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
},

onShareAppMessage分享,利用onShareAppMessage的promise属性对分享做了兜底处理,如果promise reject 或三秒内不 resolve,分享会使用下面传入的默认参数

onShareAppMessage 详见 onShareAppMessage 微信小程序开发者文档

onShareAppMessage(e) {

    const { from } = e || {};

    const promise = new Promise(async (resolve) => {

      const url = await this.initCanvas();

      resolve({

        title: `自行替换`,

        path: `自行替换`,

        imageUrl: url,

      });

    });
    
    // 默认参数
    return {

      title: `自行替换`,

      path: `自行替换`,

      imageUrl: "",

      promise,

    };

  },

至此,onShareAppMessage自定义分享图片全流程结束

生产环境效果

文本省略触发

image.png

文本省略未触发

image.png

希望能帮到大家,感恩。