在移动端应用中,分享海报是一种非常常见的营销和传播手段。用户可以将带有个人邀请码、商品信息或活动内容的图片保存到相册,再分享到微信、朋友圈等社交平台。在 UniApp 中,由于需要兼容 App、H5 以及各家小程序,使用 Canvas 绘制海报是最稳妥的方案。本文将完整讲解在 UniApp 中如何利用 Canvas 生成分享海报,包括布局思路、代码实现以及常见问题的解决方案。
一、为什么选择 Canvas 绘制海报
在 UniApp 中生成海报通常有两种思路:
- 服务端生成:由后端使用 Node.js、Java 等生成图片,前端只负责展示和保存。优点是兼容性好,缺点是依赖网络、响应慢、服务器压力大。
- 前端 Canvas 生成:利用 UniApp 提供的
uni.createCanvasContextAPI 在本地绘制。优点是速度快、不依赖网络、可离线使用,缺点是需要处理各端的兼容性问题。
对于大多数场景,前端 Canvas 方案已经足够,并且用户体验更好。下面我们以一个典型的“商品分享海报”为例进行实现。
二、整体实现思路
生成海报的流程可以概括为以下几步:
- 在页面中放置一个
<canvas>组件,设置好宽高(注意使用rpx与px的转换)。 - 使用
uni.createCanvasContext获取绘图上下文。 - 依次绘制背景、图片、文字、二维码等内容。
- 调用
ctx.draw()完成绘制。 - 使用
uni.canvasToTempFilePath将 Canvas 导出为临时图片路径。 - 通过
uni.saveImageToPhotosAlbum保存到相册,或通过uni.previewImage预览。
三、页面结构与基础样式
首先在页面中定义 Canvas 组件。需要注意的是,Canvas 的宽高建议使用 px 单位,并通过 uni.getSystemInfoSync() 获取屏幕宽度进行适配。
<template>
<view class="poster-page">
<canvas
canvas-id="posterCanvas"
:style="{ width: canvasW + 'px', height: canvasH + 'px' }"
class="poster-canvas"
></canvas>
<button @click="generatePoster">生成海报</button>
<image v-if="posterUrl" :src="posterUrl" mode="widthFix" />
</view>
</template>
export default {
data() {
return {
canvasW: 300,
canvasH: 500,
posterUrl: ''
};
},
mounted() {
const systemInfo = uni.getSystemInfoSync();
// 以 750rpx 设计稿为基准,换算成 px
this.canvasW = systemInfo.windowWidth * 0.8;
this.canvasH = this.canvasW * 1.6;
}
};
四、绘制海报的核心代码
下面是一个完整的绘制方法,包含了背景、头像、文字、商品图和二维码。
methods: {
async generatePoster() {
const ctx = uni.createCanvasContext('posterCanvas', this);
const W = this.canvasW;
const H = this.canvasH;
// 1. 绘制背景
ctx.setFillStyle('#ffffff');
ctx.fillRect(0, 0, W, H);
// 2. 绘制顶部背景色块
ctx.setFillStyle('#ff6b00');
ctx.fillRect(0, 0, W, H * 0.35);
// 3. 绘制头像(圆形)
const avatarSize = W * 0.2;
const avatarX = W * 0.5 - avatarSize / 2;
const avatarY = H * 0.08;
ctx.save();
ctx.beginPath();
ctx.arc(W * 0.5, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
// 注意:图片需要先下载到本地
const avatarPath = await this.downloadImage('https://example.com/avatar.png');
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize);
ctx.restore();
// 4. 绘制昵称
ctx.setFillStyle('#ffffff');
ctx.setFontSize(16);
ctx.setTextAlign('center');
ctx.fillText('用户昵称', W * 0.5, avatarY + avatarSize + 24);
// 5. 绘制商品图片
const goodsW = W * 0.7;
const goodsH = goodsW;
const goodsX = (W - goodsW) / 2;
const goodsY = H * 0.4;
const goodsPath = await this.downloadImage('https://example.com/goods.png');
ctx.drawImage(goodsPath, goodsX, goodsY, goodsW, goodsH);
// 6. 绘制商品标题
ctx.setFillStyle('#333333');
ctx.setFontSize(14);
ctx.setTextAlign('left');
ctx.fillText('商品名称示例', goodsX, goodsY + goodsH + 30);
// 7. 绘制价格
ctx.setFillStyle('#ff6b00');
ctx.setFontSize(18);
ctx.fillText('¥ 99.00', goodsX, goodsY + goodsH + 60);
// 8. 绘制二维码
const qrSize = W * 0.25;
const qrX = W - qrSize - 20;
const qrY = H - qrSize - 20;
const qrPath = await this.downloadImage('https://example.com/qrcode.png');
ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize);
// 9. 绘制提示文字
ctx.setFillStyle('#999999');
ctx.setFontSize(12);
ctx.fillText('长按识别二维码', qrX - 80, qrY + qrSize / 2);
// 10. 完成绘制
ctx.draw(false, () => {
setTimeout(() => {
this.canvasToImage();
}, 300);
});
}
}
五、图片下载与跨域处理
Canvas 绘制网络图片时,必须先将图片下载到本地,否则在小程序和 App 端会绘制失败。这里封装一个 downloadImage 方法:
downloadImage(url) {
return new Promise((resolve, reject) => {
uni.downloadFile({
url,
success: (res) => {
if (res.statusCode === 200) {
resolve(res.tempFilePath);
} else {
reject(new Error('下载失败'));
}
},
fail: reject
});
});
}
在 H5 端,如果图片存在跨域问题,需要服务端配置 Access-Control-Allow-Origin,或者将图片转为 base64 后再绘制。
六、导出图片并保存到相册
绘制完成后,调用 uni.canvasToTempFilePath 导出图片:
canvasToImage() {
uni.canvasToTempFilePath({
canvasId: 'posterCanvas',
success: (res) => {
this.posterUrl = res.tempFilePath;
this.saveToAlbum(res.tempFilePath);
},
fail: (err) => {
console.error('导出失败', err);
}
}, this);
},
saveToAlbum(filePath) {
uni.saveImageToPhotosAlbum({
filePath,
success: () => {
uni.showToast({ title: '已保存到相册', icon: 'success' });
},
fail: (err) => {
if (err.errMsg.includes('auth deny')) {
uni.showModal({
title: '提示',
content: '需要您授权保存图片到相册',
success: (res) => {
if (res.confirm) {
uni.openSetting();
}
}
});
}
}
});
}
七、常见问题与优化建议
- Canvas 层级问题:在小程序中,Canvas 是原生组件,层级最高,会遮挡其他元素。可以通过
cover-view或绘制完成后隐藏 Canvas 来解决。 - 绘制时机:必须等图片全部下载完成后再调用
ctx.draw(),否则会出现图片空白。 - 圆角图片:Canvas 本身不支持圆角,需要结合
arc和clip实现。 - 多行文字:
fillText不支持自动换行,需要手动计算文字宽度并逐行绘制。 - 性能优化:海报尺寸不宜过大,建议宽度不超过屏幕宽度的 1.5 倍,避免导出慢或内存溢出。
- 兼容性:App 端建议使用
uni.createCanvasContext的旧版接口,或升级到type="2d"的新版 Canvas,后者性能更好但需要额外适配。
八、总结
通过 UniApp 的 Canvas API,我们可以灵活地生成各种样式的分享海报。核心步骤包括:搭建 Canvas 容器、下载网络图片、按顺序绘制元素、导出临时文件并保存到相册。虽然过程中需要处理图片下载、跨域、层级等细节,但整体方案成熟稳定,适用于电商、活动推广、邀请注册等多种业务场景。掌握这套流程后,你可以根据设计稿自由扩展海报内容,打造更具传播力的分享体验。
未经允许不得转载:任鹏个人博客 » UniApp 中使用 Canvas 生成分享海报的完整实现


朋友圈点赞图在线生成源码