Canvas 2D 基础

生成分享海报、给图片加文案、画签到卡片,这些场景都离不开 Canvas。新版小程序推荐用 Canvas 2D 接口,它在渲染性能和清晰度上都比旧版 canvas 组件好,写法也更接近浏览器标准。

页面里先放一个 canvas 节点,通过 type 属性声明 2d 类型,然后在脚本里用 SelectorQuery 拿到节点和画布上下文,就能开始画了。canvas-id 是旧接口的用法,新项目建议直接上 2d。

拿到上下文之后,绘制流程基本固定:先画背景,再贴图片,然后写字,最后整体导出。下面按这个顺序拆开讲。

本文核心知识点
  • 画布Canvas 2D 创建与上下文。
  • 绘制drawImage 与 fillText。
  • 高清按像素比放大画布。
  • 落地导出临时文件并保存相册。
小程序 Canvas 与图片合成配图:蓝调渐变背景上一张画布面板中拼合图片与文字块并导出海报的扁平科技插画
图片、文字在画布上合成,一张海报就出来了。

绘制图片与文字

绘图上下文拿到手,先设画布尺寸和背景色,再用 drawImage 画图片、fillText 写文字。字号、颜色、对齐方式都要显式设置,避免真机上样式不一致。

const query = wx.createSelectorQuery();
query.select('#posterCanvas').fields({ node: true, size: true })
  .exec(function (res) {
    const canvas = res[0].node;
    const ctx = canvas.getContext('2d');
    const dpr = wx.getWindowInfo().pixelRatio;
    canvas.width = 375 * dpr;
    canvas.height = 667 * dpr;
    ctx.scale(dpr, dpr);

    ctx.fillStyle = '#1b3a6b';
    ctx.fillRect(0, 0, 375, 667);
    ctx.fillStyle = '#ffffff';
    ctx.font = 'bold 20px sans-serif';
    ctx.fillText('吉极云码', 24, 60);
  });

文字换行要自己处理:小程序没有现成的自动换行,写一个按字符宽度拆行的工具函数,遍历文案,累加测量宽度,超过画布宽度就换一行。

多行文字对齐也常见:左对齐、居中、两端对齐各写一个分支。海报里标题和正文的字号、行距分开配置,看起来才有层次。

网络图片先落地

Canvas 不能直接画网络地址的图片,必须先下载到本地临时文件再画。用 wx.getImageInfo 可以把网络图转成本地路径,同时拿到图片宽高,方便计算贴图的位置和尺寸。

wx.getImageInfo({
  src: 'https://cdn.example.com/avatar.jpg',
  success: function (res) {
    const img = canvas.createImage();
    img.src = res.path;
    img.onload = function () {
      ctx.drawImage(img, 24, 120, 120, 120);
    };
  }
});

头像、商品图这类图,最好先做一次服务端压缩,缩到海报需要的尺寸再下发,既省流量又省绘制时间。

图片域名要配进 downloadFile 合法域名,否则 getImageInfo 直接失败。本地图、临时文件路径可以直接画,不受域名限制。

另外,海报里要用的字体如果比较特殊,先确认设备上有没有。小程序 Canvas 只能用系统字体或已加载的字体,别在绘制时才临时下载字体,加载时序会打乱绘制流程。

高清输出不模糊

海报导出模糊,九成是画布尺寸没按像素比放大。逻辑尺寸 375 宽的画布,在 2 倍屏上导出前要把 canvas.width 设成 750,再 scale 一下上下文,画的内容才会以高清分辨率落盘。

屏幕像素比画布逻辑宽导出像素宽
1375375
2375750
33751125

导出时用 wx.canvasToTempFilePath,可以指定输出图片的宽度高度和 fileType。导出 PNG 清晰但体积大,纯色多的海报用 JPEG 加质量参数更划算。

画布尺寸和导出尺寸保持一致,预览时用逻辑尺寸显示,导出时按物理像素出图,这样预览不占内存,成品又不糊。

高清海报的关键就一句:画布物理像素按像素比放大,导出前别缩放回去。

保存到相册

海报导出成临时文件之后,用 wx.saveImageToPhotosAlbum 存进相册。第一次调用会弹授权,用户拒绝之后再次调用会直接失败,所以要在设置页给一个手动开启入口。

保存成功要提示用户,失败要区分原因:拒绝授权引导去设置页,相册权限没开同样引导,文件太大失败就提示重试。用户点击保存按钮之前,最好先展示一张预览图,让用户确认内容再保存。

预览可以用临时文件路径直接渲染到 image 组件,确认后保存。整条链路:绘制、导出、预览、保存,四步各司其职,出错点都看得见。

海报内容如果是用户生成的,记得在绘制前把文案长度、敏感词都检查一遍。文案太长超出画布、或者包含违规内容,导出出来再返工就慢了。