Uniapp 微信小程序分享 - 自定义绘制分享图片

技术栈: Uniapp + Vue3

简介

因实际业务需求,需要实现微信小程序自定义分享,根据当前数据动态生成(绘制)分享卡片的图片。

基础分享使用 配置此处不在赘述,可查看上篇博客:Uniapp 微信小程序分享 - 自定义卡片内容 + 参数携带 接收

为了实现绘制分享卡片,核心需要使用 uni.createCanvasContext

先来看一下效果:

中间的图片绘制分四个部分:

  1. 卡片背景图(带小程序名称的红色边框和"马上抢")
  2. 用户头像
  3. 店铺图片
  4. 其余文字

注意:绘制图片必须需要专门使用uni.getImageInfo 获取图片的path后,再使用canvas.drawImage绘制,否则直接用图片路径绘制不成功,会成默认的分享图。

实现

DOM

需要

  1. 在DOM上创建canvas元素
  2. 设置 canvas-id和画布大小
  3. 设置样式让画布不在可视区域内。
html 复制代码
<template>
<button open-type="share" class="share-btn">分享</button>
<canvas canvas-id="hoCanvas" style="width: 300px;height: 240px;position: fixed; right: -999999999rpx;"></canvas>
<template>

onShareAppMessage事件

因为获取路片路径、canvas画图会比较慢,可提前加载好固定的图片素材。

javascript 复制代码
import { ref, getCurrentInstance } from 'vue'
import { getImgPath, savePoster } from './utils.js'
import { onLoad, onShareAppMessage } from '@dcloudio/uni-app';

const that = getCurrentInstance();

const shareImg = ref();

const info = ref({
		shopName: '店铺名'
		logoUrl: 'https://p1.meituan.net/business/a199b07951349e881d3a38b9f28b832d458281.png',
		nickName: '7788',
		price: 10,
		num: 100,
		avatar: '',
		shareBg: ''
	});
	
// 绘制分享卡片
const initShareImg = () => {
	savePoster(info.value, (res) => {
		shareImg.value = res;
	})
}

// 页面加载就开始绘制卡片
onLoad(() => {
	//	获取卡片背景path
	getImgPath('https://zj-biz-gov-free-eat.oss-cn-hangzhou.aliyuncs.com/free-eat/static-example/share-activity.png', '店铺分享卡片背景',(res) => {
		info.value.shareBg = res;
});
	// 获取头像path
	getImgPath('https://zj-biz-gov-free-eat.oss-cn-hangzhou.aliyuncs.com/free-eat/user/oR1KL7S9-m6ryWDar-1h_vaCOPXw/18860235410318-1731415218.jpg', '用户头像', (res) => {
		info.value.avatar = res;
	});
	
	// 背景图 头像路径获取完了再绘制分享图
	setTimeout(() => {
		initShareImg();
	},2000)
	
})

// 分享活动
onShareAppMessage((res) =>{
	that.proxy.mpShare.title =  `【${info.value.shopName}】开抢啦!限量${info.value.num}份,最高返${info.value.price}元!`;
	that.proxy.mpShare.path = `/pages/orderPage/joinDetailMt/indexNew`;
	// 绘制失败则使用微信默认的快照图片
	if (shareImg.value) that.proxy.mpShare.imageUrl = shareImg.value;
});

utils 工具函数

javascript 复制代码
export const fillRoundRect = (ctx, x, y, width, height, radius, img) => {
	// console.log(ctx, x, y, width, height, radius, /*optional*/ fillColor)
	ctx.save();
	ctx.translate(x, y);
	//绘制圆角矩形的各个边
	drawRoundRectPath(ctx, width, height, radius);
	ctx.clip();
	ctx.drawImage(img, 0, 0, width, height);
	// ctx.fillStyle = fillColor || "blue"; //若是给定了值就用给定的值否则给予默认值
	// ctx.fill();
	ctx.restore();
};


export const drawRoundRectPath = (ctx, width, height, radius) => {
	ctx.beginPath(0);
	//从右下角顺时针绘制,弧度从0到1/2PI
	ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
	//矩形下边线
	ctx.lineTo(radius, height);
	//左下角圆弧,弧度从1/2PI到PI
	ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
	//矩形左边线
	ctx.lineTo(0, radius);
	//左上角圆弧,弧度从PI到3/2PI
	ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);
	//上边线
	ctx.lineTo(width - radius, 0);
	//右上角圆弧
	ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);
	//右边线
	ctx.lineTo(width, height - radius);
	ctx.closePath();
}


// 分享卡片绘制
export const savePoster = (info, callback) => {
	const { logoUrl, nickName, price, num, shareBg, avatar } = info;

	const canvas = uni.createCanvasContext('hoCanvas');
	uni.getImageInfo({
		src: logoUrl,
		success: (logoRes) => {
			canvas.drawImage(shareBg, 0, 0, 300, 240);
			fillRoundRect(canvas, 10, 37, 160, 160, 10, logoRes.path);
			canvas.setFontSize(12);
			canvas.setFillStyle("#333");
			// canvas.setStrokeStyle('#333');
			canvas.fillText('最高返', 180, 88);

			canvas.setFontSize(12);
			canvas.setFillStyle("#FD3540");
			canvas.setStrokeStyle('#FD3540');
			canvas.fillText('¥', 216, 88);

			canvas.setFontSize(24);
			canvas.setFillStyle("#FD3540");
			canvas.setStrokeStyle('#FD3540');
			canvas.fillText(price, 225, 88);

			canvas.setFontSize(16);
			canvas.setFillStyle("#3D3D3D");
			canvas.setStrokeStyle('#3D3D3D');
			canvas.fillText('霸王餐', 174, 110);

			canvas.setFontSize(16);
			canvas.setFillStyle("#FD3540");
			canvas.setStrokeStyle('#FD3540');
			canvas.fillText(`限量${num}份!`, 222, 110);

			fillRoundRect(canvas, 4, 210, 22, 22, 11, avatar);

			canvas.setFontSize(12);
			canvas.setFillStyle("#FFFFFF");
			canvas.setStrokeStyle('#FFFFFF');
			canvas.fillText(`${nickName}为您推荐`, 30, 225);
			canvas.draw(false, () => {
				// 获取 canvas 的临时路径
				uni.canvasToTempFilePath({
					canvasId: 'hoCanvas',
					success: (tempFilePathRes) => {
						const tempFilePath = tempFilePathRes.tempFilePath;

						callback(tempFilePath);
						console.log('临时路径:', tempFilePath);
					},
					fail: (err) => {
						console.error('获取临时路径失败:', err);
					}
				});
			});
		},
		fail: (err) => {
			console.error('获取分享图片信息失败:', err);
			message(`获取店铺图片信息失败,请稍后重试`)
		}
	});
}

// 获取图片path【店铺分享背景、用户头像】
export const getImgPath = (img, name, callback) => {
	uni.getImageInfo({
		src: img,
		success: (imgRes) => {
			callback(imgRes.path);
		},
		fail: (err) => {
			console.error(`获取${name}图片信息失败,请稍后重试`, err);
		}
	})
}
相关推荐
聊询QQ:276998856 小时前
基于Matlab的转速开环恒压频比异步电动机调速系统设计报告与仿真程序
uni-app
2501_915106328 小时前
iOS 抓不到包怎么办?从 HTTPS 代理排查到 TCP 数据流捕获的全链路解决方案
android·tcp/ip·ios·小程序·https·uni-app·iphone
游戏开发爱好者89 小时前
APP上架苹果应用商店经验教训与注意事项
android·ios·小程序·https·uni-app·iphone·webview
济南壹软网络科技有限公司9 小时前
沉浸式体验革命:壹软科技2025新版盲盒源码前端3D渲染与个性化运营技术解析
前端·科技·uni-app·开源·php·盲盒源码
Qin_jiangshan11 小时前
flutter和reactNative以及uniapp区别
flutter·react native·uni-app
MrTan11 小时前
Uni-App 鸿蒙应用微信相关功能上架踩坑:自制微信安装检测插件
uni-app·harmonyos
Dest1ny-安全11 小时前
CTF入门:国内线上CTF比赛时间及部分题目资源
网络·安全·web安全·微信小程序·php
2501_9160074712 小时前
苹果应用商店上架的系统逻辑,从产品开发到使用 开心上架 上架IPA 交付审核流程
android·ios·小程序·https·uni-app·iphone·webview
IT 前端 张12 小时前
Uni-app 实现全局无操作监听:自动退出弹窗倒计时功能
运维·服务器·uni-app
一只月月鸟呀12 小时前
使用node和@abandonware/bleno写一个ble模拟设备,用Uni-app模拟终端连接
uni-app