uniapp 使用canvas画海报(微信小程序)

news2025/6/21 3:44:38

效果展示:

 项目要求:点击分享绘制海报,并实现分享到好友,朋友圈,并保存

先实现绘制海报
 

<view class="data_item" v-for="(item,index) in dataList" :key="index"
			@click="goDetail(item,index)">
			<view class="data_bot">
				<view class="share" @click.stop="goShare(item,index)">
					<image :src="getimg('share_gray.png')" style="width: 36rpx;height: 36rpx;"></image>
					<view class="share_tt">分享</view>
				</view>
			</view>
		</view>

<view class="post_box" :class="isShare?' ':'post_box2'">
			<view class="post">
				<canvas style="width: 327px; height:482px;margin: 0 auto;" canvas-id="firstCanvas1"></canvas>
			</view>
		</view>
// 生成海报
			goShare(item, index) {
				let _this = this;
				this.shareId = item.id
				this.isShare = true
				// uni.hideTabBar()
				let userInfo = uni.getStorageSync('userInfo')
				uni.getImageInfo({
					src: userInfo.avatar,
					success: function(res) {
						if (res.path) {
							_this.canvasr(item, index, res.path, userInfo.nickname);
						}
					},
				});
			},
// 画海报
			canvasr(item, index, avatar, username) {
				let ctx = uni.createCanvasContext('firstCanvas1');
				// 背景图
				let postImage = '../../static/images/poster_bg.png'; //背景图
				ctx.globalAlpha = 0; // 设置图像透明度为0  为了让背景透明
				ctx.setFillStyle('#ffffff'); // 默认白色
				ctx.fillRect(0, 0, 327, 482);
				ctx.globalAlpha = 1; // 设置图像透明度为1
				ctx.drawImage(postImage, 0, 0, 327, 482); // (图片,x,y,宽,高)背景图
				ctx.save();
				// 头像
				ctx.restore();
				// let avatar = userInfo.avatar + "?timestamp=" + Date.now(); //地址栏加入新参数  解决canvas图片跨域问题
				this.drawAvatar(ctx, avatar, 30, 20, 15)
				// 昵称 +
				ctx.setFontSize(12); //设置字体字号
				ctx.setFillStyle('#000000'); //设置字体颜色
				// ctx.fillText(username, 65, 39); // (文字,x,y)
				let _strlineW = 0;
				let nickname = '';
				let actI = 0;
				for (let i = 0; i < username.length; i++) {
					_strlineW += ctx.measureText(username[i]).width;
					if(_strlineW <= 60){
						ctx.fillText(username, 65, 39); // (文字,x,y)
					}else if (_strlineW > 60 && _strlineW <= 70) {
						actI = i
						nickname = username.substring(0, i) + '…'
						ctx.fillText(nickname, 65, 39); // (文字,x,y)
					}else{
						nickname = username.substring(0, actI-1) + '…'
						ctx.fillText(nickname, 65, 39); // (文字,x,y)
					}
				}
				// 分享了一个点子
				let txt = '我发现了一个不错的点子';
				ctx.setFontSize(12); //设置字体字号
				ctx.setFillStyle('#999999'); //设置字体颜色
				ctx.fillText(txt, 135, 39); // (文字,x,y)
				// 内容开始引号图
				let startImage = '../../static/images/poster_quote_up.png'; //背景图
				ctx.drawImage(startImage, 30, 70, 32, 32); // (图片,x,y,宽,高)背景图
				ctx.save();
				ctx.restore();
				// 内容
				let content = item.idea_name;
				this.drawtext(ctx, content, 30, 130, 32, 267)
				// 内容结束引号图
				let endImage = '../../static/images/poster_quote_down.png'; //背景图
				ctx.drawImage(endImage, 265, 310, 32, 32); // (图片,x,y,宽,高)背景图
				ctx.save();
				ctx.restore();
				// 一条直线
				ctx.setFillStyle('#D8D8D8'); // 默认白色
				ctx.fillRect(30, 362, 267, 0.5); //x y 宽 高
				ctx.restore();
				// logo图
				let logoImage = '../../static/images/aiyop_logo.png'
				this.drawAvatar(ctx, logoImage, 30, 402, 15)
				ctx.save();
				ctx.restore();
				// AI有谱
				let title = 'AI有谱';
				ctx.setFontSize(13); //设置字体字号
				ctx.setFillStyle('#000000'); //设置字体颜色
				ctx.fillText(title, 70, 415); // (文字,x,y)
				// 心中有谱,奇思妙想也靠谱
				let intro = '心中有谱,奇思妙想也靠谱';
				ctx.setFontSize(10); //设置字体字号
				ctx.setFillStyle('#666666'); //设置字体颜色
				ctx.fillText(intro, 70, 430); // (文字,x,y)
				// 二维码
				let qrImage = '../../static/images/logo_program.jpg';
				this.drawAvatar(ctx, qrImage, 225, 381, 36)
				// 绘制
				ctx.draw();
			},
			// 绘制头像
			drawAvatar(ctx, img, x, y, r) {
				ctx.save();
				let d = r * 2;
				let cx = x + r;
				let cy = y + r;
				ctx.beginPath()
				ctx.arc(cx, cy, r, 0, 2 * Math.PI);
				ctx.strokeStyle = '#FFFFFF'; // 设置绘制圆形边框的颜色
				ctx.stroke(); // 绘制出圆形,默认为黑色,可通过 ctx.strokeStyle = '#FFFFFF', 设置想要的颜色
				ctx.clip();
				ctx.drawImage(img, x, y, d, d);
				ctx.restore();
			},
			// 文本n行换行与显示省略号
			// 1、canvas对象 2、文本 3、X轴 4、Y轴 5、单行行高 6、文本的宽度
			drawtext(ctx, str, axisX, axisY, titleHeight, maxWidth) {
				ctx.setFontSize(22); //设置字体字号
				ctx.setFillStyle('#000000'); //设置字体颜色
				// 文本处理
				let strArr = str.split("");
				let row = [];
				let temp = "";
				for (let i = 0; i < strArr.length; i++) {
					if (ctx.measureText(temp).width < maxWidth) {
						temp += strArr[i];
					} else {
						i--; //这里添加了i-- 是为了防止字符丢失,效果图中有对比
						row.push(temp);
						temp = "";
					}
				}
				row.push(temp); // row有多少项则就有多少行

				//如果数组长度大于6,现在只需要显示6行则只截取前两项,把第6行结尾设置成'...'
				if (row.length > 6) {
					let rowCut = row.slice(0, 6);
					let rowPart = rowCut[5];
					let test = "";
					let empty = [];
					for (let i = 0; i < rowPart.length; i++) {
						if (ctx.measureText(test).width < maxWidth) {
							test += rowPart[i];
						} else {
							break;
						}
					}
					empty.push(test);
					// let group = empty[0] //这里只显示两行,超出的用...表示
					let group = empty[0] + "..." //这里只显示两行,超出的用...表示
					rowCut.splice(5, 1, group);
					row = rowCut;
				}
				// 把文本绘制到画布中
				for (let i = 0; i < row.length; i++) {
					// 一次渲染一行
					ctx.fillText(row[i], axisX, axisY + i * titleHeight, maxWidth);
				}
				// 保存当前画布状态
				ctx.save()
				// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。
				// ctx.draw()
			},

下面是海报下面的分享弹窗

<!-- 分享海报的遮罩层 -->
		<view v-if="isShare" class="over_box" @touchmove.stop.prevent="moveHandle"></view>
		<!-- 分享海报 -->
		<u-popup :show="isShare" @close="closeShare" @open="openShare" mode="bottom" :round="10" :overlay="false">
			<view class="trans_box">
				<view class="per_box">
					<button open-type="share" class="per_but" @click="shareWeixin('WXSceneSession')">
						<image :src="getimg('wehcat.png')" style="width:86rpx;height: 86rpx;"></image>
						<view class="per_txt">微信好友</view>
					</button>
					<button class="per_but" @click="shareWeixin('WXSceneTimeline')">
						<image :src="getimg('wechat-moments.png')" style="width:86rpx;height: 86rpx;"></image>
						<view class="per_txt">朋友圈</view>
					</button>
					<button @click="saveLocal" class="per_but">
						<image :src="getimg('save-poster.png')" style="width:86rpx;height: 86rpx;"></image>
						<view class="per_txt">保存图片</view>
					</button>
				</view>
				<view class="share_line"></view>
				<view class="close_tran" @click="closeShare">取消</view>
			</view>
		</u-popup>

因为分享到朋友圈实在没找到有使用自定义按钮的可能,所以还是需要点击右上角胶囊
 

onLoad(){
    //分享功能
			wx.showShareMenu({
				withShareTicket: true,
				//设置下方的Menus菜单,才能够让发送给朋友与分享到朋友圈两个按钮可以点击
				menus: ["shareAppMessage", "shareTimeline"]
			})

}
//点击分享朋友,朋友圈事件
shareWeixin(scene) {
				if (scene == 'WXSceneTimeline') {
					uni.showToast({
						title: '点击右上角胶囊分享朋友圈',
						icon: 'none',
						duration: 2000
					})
				} else {
					uni.share({
						provider: "weixin",
						scene: scene,
						type: 0,
						href: '/subPackage/index/like_details?id=' + this.shareId + '&type=1',
						title: '点子分享',
						success(res) {
							console.log(res);
						},
						fail(err) {
							console.log(err);
						}
					})
				}
			},
onShareAppMessage(res) {
			if (res.from === 'button') { // 来自页面内分享按钮
				return {
					title: 'xxxxxxx', //分享的名称
					path: '/subPackage/index/like_details?id=' + this.shareId + '&type=1',
					mpId: 'wx000000000', //此处配置微信小程序的AppId
					imageUrl: ''
				}
			}
			return {
				title: 'xxxxx', //分享的名称
				path: '/subPackage/index/like_details?id=' + this.shareId + '&type=1',
				mpId: 'wx0000000000', //此处配置微信小程序的AppId
				imageUrl: ''
			}
		},
		//分享到朋友圈
		onShareTimeline(res) {
			return {
				title: this.timeItem,
				path: '/pages/index/index',
				type: 0,
				summary: "",
				imageUrl: ''
			}
		},

 以上就是画海报以及分享的全部过程了,另有一个点:
就是分享朋友的地址path: '/subPackage/index/like_details?id=' + this.shareId + '&type=1'
这里这个加个type=1是因为点击分享进入小程序的是个详情页,可能会出现点击左上角返回不能返回首页,所以加上这个type可以在分享页加个判断
 

        onLoad(option) {
			if(option.type && option.type==1){ //分享来的页面
				this.shareType = 1
			}
			
		},
//左上角的点击返回事件加判断,当是分享进入的时候回到首页
        goBack(){
				if(this.shareType==1){
					uni.switchTab({
						url:'/pages/index/index'
					})
				}else{
					uni.navigateBack()
					this.shareType = 0
				}
			},

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/862020.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发——线程池,Executor 框架

文章目录 1 简介2 Executor 框架结构(主要由三大部分组成)1) 任务(Runnable /Callable)2) 任务的执行(Executor)3) 异步计算的结果(Future) 3 Executor 框架的使用示意图 1 简介 Executor 框架是 Java5 之后引进的&#xff0c;在 Java 5 之后&#xff0c;通过 Executor 来启动…

vue+springboot基于web的火车高铁铁路订票管理系统

铁路订票管理系统按照权限的类型进行划分&#xff0c;分为用户和管理员两个模块。管理员模块主要针对整个系统的管理进行设计&#xff0c;提高了管理的效率和标准。主要功能包括个人中心、用户管理、火车类型管理、火车信息管理、车票预订管理、车票退票管理、系统管理等&#…

解决遥感技术在生态、能源、大气等领域的碳排放监测及模拟问题

以全球变暖为主要特征的气候变化已成为全球性环境问题&#xff0c;对全球可持续发展带来严峻挑战。2015年多国在《巴黎协定》上明确提出缔约方应尽快实现碳达峰和碳中和目标。2019年第49届 IPCC全会明确增加了基于卫星遥感的排放清单校验方法。随着碳中和目标以及全球碳盘点的现…

单源最短路

无负环 Dijkstra 迪杰斯特拉算法 采用的贪心的策略 每次遍历到始点距离最近且未访问过的顶点的邻接节点&#xff0c;直到扩展到终点为止 Dijkstra求最短路 I 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1 …

微服务 云原生:基于 Gogs + Drone 实现 CI/CD 自动化

一般构建部署 以一个简单的前后端项目来说&#xff0c;分别编写前后端的 Dockerfile 文件并构建镜像&#xff0c;然后编写 docker-compose.yml 构建部署&#xff0c;启动运行。每次代码变更后都需重新手动打包、构建、推送。 一个简单的例子&#xff1a; 前端&#xff1a; 项…

解读HTML-入门第一文

HTML详细解读 概念解读基本结构常用标签标题标签&#xff08;h1~h6&#xff09;段落标签&#xff08;p&#xff09;链接标签&#xff08;a&#xff09;图像标签&#xff08;img&#xff09;列表标签&#xff08;ul、ol、li&#xff09;表格标签&#xff08;table、tr、td&#…

轻量级锁实现1——结构体解析、初始化

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;14 文档用途 从底层理解轻量级锁的实现&#xff0c;从保护共享内存的角度理解轻量级锁的使用场景&#xff0c;包括上锁、等待、释放&#xff0c;理…

android 如何分析应用的内存(十六)——使用AS查看Android堆

android 如何分析应用的内存&#xff08;十六&#xff09;——使用AS查看Android堆 在前面&#xff0c;先介绍了如何使用jdb和VS code查看应用栈相关内容。 本文将介绍&#xff0c;如何查看堆中的内容。大概有&#xff1a; 堆中的对象&#xff0c;有哪些堆中的对象&#xff0…

“Can‘t open perl script configure : No such file or directory”的解决办法

编译OpenSSL的时候执行到 perl configure 时提示找不到configure&#xff0c; 然后在网上搜了搜&#xff0c;大家给的解决办法一般都是说设置环境变量或者指定configure路径再执行&#xff1b;我试了都不行&#xff0c; 最后我把perl卸了重装就正常了&#xff1b; 然后我换了…

QEMU源码全解析32 —— Machine(2)

接前一篇文章&#xff1a;QEMU源码全解析31 —— Machine&#xff08;1&#xff09; 本文内容参考&#xff1a; 《趣谈Linux操作系统》 —— 刘超&#xff0c;极客时间 《QEMU/KVM》源码解析与应用 —— 李强&#xff0c;机械工业出版社 特此致谢&#xff01; 上一篇文章给m…

【力扣每日一题】2023.8.11 矩阵对角线元素的和

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一个矩阵&#xff0c;让我们把矩阵对角线上的元素都加起来返回。 那么矩阵的对角线是有两条的&#xff0c;一条是从左上到右下…

Maven安装与配置教程

目录 一、前言 1.什么是Maven 2.为什么要使用Maven 二、Maven安装与配置 1.官网下载 2.Maven配置 3.修改Maven仓库下载镜像及修改仓库位置 3.1.修改仓库下载镜像地址 3.2.修改默认Maven的仓库位置 三、eclipse配置Maven 四、eclipse部署Maven项目 注意事项&#xff…

Python非线性全局优化

文章目录 全局优化函数简介详解性能测试 全局优化函数简介 scipy的optimize模块非常强大&#xff0c;也是我个人使用最多的scipy模块&#xff0c;这里面封装的都是成熟且高效的算法&#xff0c;久经考验。对于参加数学竞赛的同学来说&#xff0c;辛辛苦苦撸出来的遗传算法、模…

Eudic欧路词典 for Mac v4.4.5增强版

欧路词典 (Eudic)是一个功能强大的英语学习工具&#xff0c;它包含了丰富的英语词汇、短语和例句&#xff0c;并提供了发音、例句朗读、单词笔记等功能。 多语种支持&#xff1a;欧路词典支持多种语言&#xff0c;包括英语、中文、日语、法语等等&#xff0c;用户可以方便地进…

Kubernetes 调度 约束

调度约束 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面建立 Pod 和 Container。 APIServer…

python——案例14:斐波那契数列

兔子生殖为例子而引入&#xff0c;故又称“兔子数列”&#xff0c; 其数值为&#xff1a;1、1、2、3、5、8、13、21、34……在数学上&#xff0c; 这一数列以如下递推的方法定义&#xff1a; F(0)1&#xff0c;F(1)1, F(n)F(n - 1)F(n - 2)&#xff08;n ≥ 2&#xff0c;n ∈ …

液体神经网络:LNN是个啥概念?

一、说明 在在人工智能领域&#xff0c;神经网络已被证明是解决复杂问题的非常强大的工具。多年来&#xff0c;研究人员不断寻求创新方法来提高其性能并扩展其能力。其中一种方法是液体神经网络&#xff08;LNN&#xff09;的概念&#xff0c;这是一个利用动态计算功能的迷人框…

Simpack助力中国铁路创新发展

中国铁路尤其是高铁的迅速发展是中国装备制造业走向世界一张名片&#xff0c;不仅为人们出行提供了便利&#xff0c;也为中国经济的快速增长提供了有力的支撑。同时&#xff0c;高速铁路的发展给产品研发带来了新的课题和挑战。尤其在动力学领域&#xff0c;各部件或子系统之间…

DP(区间DP)

石子合并 设有 N 堆石子排成一排&#xff0c;其编号为 1,2,3,…,N。 每堆石子有一定的质量&#xff0c;可以用一个整数来描述&#xff0c;现在要将这 N 堆石子合并成为一堆。 每次只能合并相邻的两堆&#xff0c;合并的代价为这两堆石子的质量之和&#xff0c;合并后与这两堆…

远程通信-RPC

项目场景&#xff1a; 在分布式微服务架构中&#xff0c;远程通信是最基本的需求。 常见的远程通信方式&#xff0c;有基于 REST 架构的 HTTP协议、RPC 框架。 下面&#xff0c;从三个维度了解一下 RPC。 1、什么是远程调用 2、什么是 RPC 3、RPC 的运用场景和优 什么是远程调用…