Uniapp 微信小程序分享 - 自定义绘制分享图片
技术栈: Uniapp + Vue3
简介
因实际业务需求,需要实现微信小程序自定义分享,根据当前数据动态生成(绘制)分享卡片的图片。
基础分享使用 配置此处不在赘述,可查看上篇博客:Uniapp 微信小程序分享 - 自定义卡片内容 + 参数携带 接收
为了实现绘制分享卡片,核心需要使用 uni.createCanvasContext。
先来看一下效果:
中间的图片绘制分四个部分:
- 卡片背景图(带小程序名称的红色边框和“马上抢”)
- 用户头像
- 店铺图片
- 其余文字
注意:绘制图片必须需要专门使用uni.getImageInfo获取图片的path后,再使用canvas.drawImage绘制,否则直接用图片路径绘制不成功,会成默认的分享图。
实现
DOM
需要
- 在DOM上创建canvas元素
- 设置 canvas-id和画布大小
- 设置样式让画布不在可视区域内。
<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画图会比较慢,可提前加载好固定的图片素材。
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(() => {// 获取卡片背景pathgetImgPath('https://zj-biz-gov-free-eat.oss-cn-hangzhou.aliyuncs.com/free-eat/static-example/share-activity.png', '店铺分享卡片背景',(res) => {info.value.shareBg = res;
});// 获取头像pathgetImgPath('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 工具函数
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/2PIctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2);//矩形下边线ctx.lineTo(radius, height);//左下角圆弧,弧度从1/2PI到PIctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);//矩形左边线ctx.lineTo(0, radius);//左上角圆弧,弧度从PI到3/2PIctx.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);}})
}
相关文章:

Uniapp 微信小程序分享 - 自定义绘制分享图片
技术栈: Uniapp Vue3 简介 因实际业务需求,需要实现微信小程序自定义分享,根据当前数据动态生成(绘制)分享卡片的图片。 基础分享使用 配置此处不在赘述,可查看上篇博客:Uniapp 微信小程序分…...
鸿蒙技术分享:Navigation页面容器封装-鸿蒙@fw/router框架源码解析(三)
本文是系列文章,其他文章见:鸿蒙fw/router框架源码解析(一)-router页面管理鸿蒙fw/router框架源码解析(二)-Navigation页面管理鸿蒙fw/router框架源码解析(四)-路由Hvigor插件实现原…...

三步入门Log4J 的使用
本篇基于Maven 的Project项目, 快速演示Log4j 的导入和演示。 第一步: 导入Log4j依赖 <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.24.2</version&…...

VBA中类的解读及应用第十八讲:利用类方法,判断任意单元格类型
《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程,目前已经是第一版修订了。这套教程定位于最高级,是学完初级,中级后的教程。 类,是非常抽象的,更具研究的价值。随着我们学习、应用VBA的深入࿰…...

查询品牌涉及两张表(brand、brand_admin_mapping)
文章目录 1、BrandController2、AdminCommonService3、BrandApiService3、BrandCommonService4、BrandSqlService涉及的表SQL 查询逻辑参数处理执行查询完整 SQL 逻辑参数映射总结 查询指定管理员下的品牌所涉及的表有哪些? http://127.0.0.1:8087/brand/admin/list…...
Eureka和Zookeeper、Nacos的区别
目录 一、Eureka与Zookeeper的区别 适用场景: 架构设计: 功能特性: 社区生态: 二、Eureka与Nacos的区别 接口方式: 实例类型: 健康检测: 服务发现: 一致性与可用性&#…...

微信小程序怎么实现非tabbar页面显示tabbar,自定义组件实现
微信小程序没有发现可以实现非tabbar页面显示tabbar的方法,但是可以在tabbar页面当中隐藏tabbar,使用wx.hideTabBar()方法就可以实现,在非tabbar页面调用wx.showTabBar()方法却会显示失败,不能显示tabbar onLoad() {wx.showTabBar…...

SpringBoot如何使用EasyExcel实现表格导出(简洁快速入门版本)
前言 前面给大家介绍了动态表头的导入,这篇文章给大家介绍如何实现导出 前面给大家介绍了动态表头的导入,我们了解了如何通过EasyExcel灵活地读取结构不固定的Excel文件。这次,我们将目光转向数据导出——即如何将数据以Excel文件的形式输出…...
多种平台上安装部署调试Open5GS(四)
OpenWRT 源码安装 UERANSIM 安装依赖openwrt源码安装cmake其他依赖准备UERANSIM安装测试验证Open5GS 是一个功能完善的开源5G项目,具备5G、4G核心网功能,最新代码支持R17标准, 本系列文章介绍Open5GS在x86、ARM平台上的安装部署方法,并通过搭建UERANSIN、商用5G基站和终端两…...
单片机的基本构成与工作原理
单片机,即微控制器(Microcontroller Unit,MCU),是一种将中央处理器(CPU)、存储器(ROM/RAM)、定时/计数器(Timer/Counter)、中断系统、输入输出(I/O)接口等集成在一块芯片上的微型计算机。它具有体积小、功耗低、成本低…...
opencv常用图像处理操作
OpenCV 处理图像的通用流程通常包括以下几个步骤,根据具体需求可以调整或跳过某些步骤。以下是一个通用的框架: 读取图像 加载图像文件到内存中以进行后续处理。 import cv2 读取图像 image cv2.imread(‘image.jpg’) # 彩色图像 gray_image cv2…...

Svn如何切换删除账号
记录Svn清除切换账号 1.首先打开小乌龟的设置如下图 打开设置后单击已保存数据,然后选择清除 接上图选择清除后,就可以打勾选择清除已保存的账号,我们再次检出的就可以切换账号了 👉总结 本次记录Svn清除切换账号 如能帮助到你…...
使用PaddleOCR遇到的问题Bug
Q1: 训练模型的预测效果比inference模型的预测效果差距很大,怎么办?原因是什么? A1: 训练模型:使用训练数据集进行训练后的模型。 inference模型:把模型结构和模型参数保存在文件中的固化模型,多用于预测部署场景。训练过程中保存的模型是checkpoints模型,保存的只有模…...
了解Xcode在iOS开发中的作用和功能有哪些
Xcode是什么?它在iOS开发中的作用和功能有哪些? 一、Xcode是什么? Xcode是苹果公司针对macOS平台开发的一款集成开发环境(Integrated Development Environment,简称IDE)。它主要用于开发iOS、iPadOS、mac…...

《船舶物资与市场》是什么级别的期刊?是正规期刊吗?能评职称吗?
问题解答 问:《船舶物资与市场》是不是核心期刊? 答:不是,是知网收录的正规学术期刊。 问:《船舶物资与市场》级别? 答:国家级。主管单位:中国船舶集团有限公司 主办单…...

商汤完成组织架构调整,改革完成的商汤未来何在?
首先,从核心业务的角度来看,商汤科技通过新架构明确了以AI云、通用视觉模型等为核心业务的战略方向。这一举措有助于商汤科技集中资源,加强在核心业务领域的研发和市场拓展,提高市场竞争力。同时,坚定生成式AI为代表的…...

MyBatis异常体系中ErrorContext和ExceptionFactory原理分析
🎮 作者主页:点击 🎁 完整专栏和代码:点击 🏡 博客主页:点击 文章目录 exceptions包分包设计ExceptionFactory类介绍为什么使用工厂不是直接new呢?【统一的异常处理机制】【异常的封装与转化】【…...

WHLUG丨deepin、华中科技大学开放原子开源俱乐部、 RustSBI 和清华大学开源操作系统训练营共话开源新生代成长之路
2024年11月30日下午,由 deepin(深度)社区联合华中科技大学开放原子开源俱乐部、 RustSBI 开源社区和清华大学开源操作系统训练营共同举办的WHLUG(武汉Linux用户组)线下沙龙在华中科技大学成功举办。 本次活动聚集了50余…...

通过HTML Canvas 在图片上绘制文字
目录 前言 一、HTML Canvas 简介 二、准备工作 三、绘制图片 四、绘制文字 五、完整代码 效果演示: 前言 HTML canvas 为我们提供了无限的创意可能性。今天,我们就来探索一下如何通过 HTML canvas 将图片和文字绘制到图片上,创造出独特…...
C# 冒泡的算法
C# 冒泡的算法 public void BubbleSort(int[] arr) {int temp;for (int j 0; j < arr.Length - 2; j){for (int i 0; i < arr.Length - 2; i){if (arr[i] > arr[i 1]){temp arr[i 1];arr[i 1] arr[i];arr[i] temp;}}} }使用方法 int[] array new int[] { 5,…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...