必须用 canvas + drawImage 叠加 Logo,且 Logo 需转 base64 或本地路径;CSS 叠加无效,因 uni.canvasToTempFilePath 仅捕获 canvas 内容,DOM 层叠不可见。

uni-app 给图片加 Logo 水印,核心不是“能不能”,而是“怎么避免糊、偏、闪、卡”。直接上结论:必须用 canvas + drawImage 叠加,且 Logo 图片需提前转为 base64 或本地临时路径;不能直接用 CSS 覆盖,否则导出无效、多端不一致。
为什么不能用 image 标签叠加 Logo?
很多人试过在 上盖一层绝对定位的 ,看起来像水印——但这是假象。uni.canvasToTempFilePath 只能捕获 canvas 内容,DOM 层叠完全不可见。H5 端可能靠截图勉强 workaround,但小程序和 App 端必然失败。
Logo 图片加载失败的三个常见原因
ctx.drawImage 加载 Logo 失败时,canvas 不报错、也不画图,静默失败。排查重点:
- Logo 路径是相对路径(如
./static/logo.png)→ 小程序不支持,必须转为base64或使用uni.getRealPathSync获取真实路径 - Logo 是网络地址(
https://xxx/logo.png)→ 小程序 canvas 不支持跨域图片,会白屏;App 和 H5 也可能因 CORS 拒绝 - Logo 尺寸过大或格式异常(比如带透明通道的 WebP 在某些安卓机解码失败)→ 导致
img.onload不触发
稳妥做法:把 Logo 放进 static 目录,构建时自动内联为 base64,或用 uni.getFileSystemManager().readFile 同步读取本地文件再创建 canvas.createImage()。
如何控制 Logo 位置、缩放和透明度?
Logo 不是简单贴角,要适配不同图幅和设备像素比(dpr)。关键参数都在 drawImage 的宽高参数和 ctx.globalAlpha 里:
- 位置计算建议用「锚点+偏移」:比如右下角水印,先算
imgWidth - logoWidth - 20(20px 右边距),而不是固定写死600 - 缩放推荐按原图宽度比例:Logo 宽设为
imgWidth * 0.15(即 15%),高按等比算,避免小图上 Logo 过大、大图上过小 - 透明度统一用
ctx.globalAlpha = 0.7,别用rgba()填充色模拟,后者对图片无效 - 务必在
img.onload回调里调用drawImage,否则图没加载完就画,留白
示例片段:
const img = canvas.createImage();
img.src = logoBase64; // 或 realPath
img.onload = () => {
const scale = width * 0.15 / img.width;
const logoW = img.width * scale;
const logoH = img.height * scale;
const x = width - logoW - 20;
const y = height - logoH - 20;
ctx.globalAlpha = 0.7;
ctx.drawImage(img, x, y, logoW, logoH);
ctx.globalAlpha = 1;
ctx.draw(false, () => { /* 导出 */ });
};
导出后 Logo 模糊或变形怎么办?
这不是代码 bug,是 canvas 像素未对齐 dpr 导致的。尤其在 iPhone 和高刷安卓机上明显:
- 创建 canvas 时没乘 dpr:正确写法是
canvas.width = width * dpr,canvas.height = height * dpr - 绘图前没调
ctx.scale(dpr, dpr),导致文字/Logo 被拉伸或压缩 - 导出时没指定
quality: 1(最高清),默认质量可能压缩 Logo 边缘
漏掉任意一项,Logo 都可能发虚、锯齿、位置偏移 1–2px——这种问题只在真机出现,开发工具里看不出来。
真正麻烦的不是加 Logo,而是让 Logo 在所有设备上清晰、居中、不遮挡主体、导出后不失真。这些细节藏在 dpr、scale、onload 时机和路径处理里,缺一不可。











