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);
		}
	})
}
相关推荐
兔C4 小时前
微信小程序的轮播图学习报告
学习·微信小程序·小程序
用户48062260414156 小时前
使用uniapp开发微信小程序-框架搭建
微信小程序·uni-app
嘟嘟实验室6 小时前
微信小程序xr-frame透明视频实现
微信小程序·ffmpeg·音视频·xr
TttHhhYy8 小时前
uniapp+vue开发app,蓝牙连接,蓝牙接收文件保存到手机特定文件夹,从手机特定目录(可自定义),读取文件内容,这篇首先说如何读取,手机目录如何寻找
开发语言·前端·javascript·vue.js·uni-app
Funky_oaNiu8 小时前
uniapp实现按钮防重复点击(防抖)完整解决方案
uni-app
原克技术8 小时前
uniapp验证码
uni-app
Stanford_11069 小时前
高级的SQL查询技巧有哪些?
sql·微信小程序·twitter·微信开放平台
美美的海顿11 小时前
spring boot 火车售票微信小程序LW
spring boot·后端·微信小程序·小程序·毕业设计
Kika写代码13 小时前
【微信小程序】1|底部图标 | 我的咖啡店-综合实训
微信小程序·小程序
JSON_L13 小时前
小程序 - 模拟时钟
微信·微信小程序·小程序