uni-app 用 canvas 合并头像生成群头像需三步:加载图片转临时路径、计算坐标逐张绘制、导出前确保 draw 完成;需注意 canvas-id 声明、平台差异、圆角阴影等原生绘制细节。

uni-app 用 canvas 合并多张头像生成群头像,核心就三步
能做,但不能直接“贴图叠加”,必须走 canvas 绘制流程。微信群头像那种 3×3、2×2 或 L 形排列,本质是计算每张图的 x、y、width、height,再逐张 drawImage 到画布上。小程序和 H5 行为一致,但 App 端(尤其是 iOS)对 canvas 尺寸和像素比更敏感,容易糊或裁切。
- 先用
uni.getImageInfo加载所有头像 URL,转成本地临时路径(网络图必须走这步,否则drawImage会静默失败) - 创建固定尺寸 canvas(比如 300×300 rpx),用
uni.createCanvasContext('id')获取上下文 - 手动算坐标:例如 4 人头像排成 2×2,每张图宽高 = (300 - 间隙×3) / 2,左上角依次为 (间隙, 间隙)、(间隙 + 宽 + 间隙, 间隙)……
小程序里 canvasId 必须唯一且提前声明,否则 canvasToTempFilePath 报错
错误现象:canvasToTempFilePath: fail canvas is empty 或控制台提示 “找不到 canvasId”。这不是图片没画,而是 canvas 节点没挂载好或 id 冲突了。尤其在 v-if 切换页面时,如果 canvas 是条件渲染的,可能上下文已失效。
-
canvas标签必须写死canvas-id="myCanvas"(注意是短横线,不是驼峰),不能动态绑定 - 不要用
ref+this.$refs.canvas拿节点,uni-app 小程序端不支持这种 DOM 访问方式 - 调用
uni.createCanvasContext前,确保 canvas 已渲染(可放在onReady或延时setTimeout(() => {}, 100)) - H5 端可用原生
标签 +getContext('2d'),但小程序必须用canvas-id+uni.createCanvasContext
头像圆角、边框、重叠阴影这些效果,得靠 canvas 原生 API 实现
uni-app 没有“一键群头像组件”,所有视觉效果都要自己画。比如圆角头像不是给 image 加 border-radius,而是在 canvas 上用 clip() + arc() 剪裁路径;边框要额外 strokeRect;重叠部分的阴影得用 shadowColor + shadowOffsetX 控制——而且这些属性只对后续绘制生效,顺序不能错。
- 画圆角头像:先
ctx.beginPath()→ctx.arc(x + r, y + r, r, 0, 2 * Math.PI)→ctx.clip()→ 再drawImage - 加 2px 白边:在
clip前,先ctx.strokeStyle = '#FFFFFF',ctx.lineWidth = 2,ctx.stroke() - 阴影只对文字或矩形有效,对
drawImage无效;想让头像带阴影,得先画个带阴影的灰色矩形,再把头像盖上去
导出图片前务必等 ctx.draw(true, callback) 完成,否则生成空白图
这是最常踩的坑:ctx.draw() 是异步的,但很多人写成同步思维,紧接着就调 uni.canvasToTempFilePath,结果导出的是白板。callback 不触发?大概率是 canvas 尺寸设得太小(比如 width=300,但实际画了 600px 宽的内容),导致绘制被截断。
- 一定要用
ctx.draw(true, () => { uni.canvasToTempFilePath({ ... }) }),true表示清空画布缓冲,避免旧内容残留 - 导出前检查 canvas 宽高是否匹配目标分辨率(小程序要求宽高 ≤ 1600,H5 无硬限制但超大会卡顿)
- 安卓 App 端若导出黑图,试试把 canvas 的
style宽高(如width: 300rpx)和实际width/height属性(如300)设成相同比值,避免像素拉伸失真
实际最难的不是画,是适配不同人数(2人、5人、9人)的布局算法和 canvas 动态尺寸计算——这个逻辑一旦写死,改起来很痛。建议把排列规则抽成函数,输入人数和画布总宽高,输出每张图的坐标和尺寸,别硬编码。











