微信小程序 新版canvas绘制名片

html 复制代码
<canvas canvas-id="posterId" id="posterId" type="2d" class="poster-canvas"></canvas>
css 复制代码
.poster-canvas {
		width: 375px;
		height: 280px;
		background-color: #fff;
		position: absolute;
		left: -9999999px;
}
javascript 复制代码
async createShare() {
	let _this = this;
	return await new Promise((resolve, reject) => {
		_this.cardDetail.cardBg = _this.cardBg;
		_this.drawPoster('posterId', _this.cardDetail, res => {
				if (res) {
					_this.posterImg = res;
					resolve(res);
				} else {
					uni.showToast({
						icon: "none",
						title: "生成海报失败,请稍后再试"
					})
				}
			}
		)
	})
},

drawPoster(canvasId, userInfo, callback) {
	let _this = this;
	let winWidth = 375;
	let winHeight = 280;
	
	wx.createSelectorQuery().select('#'+canvasId).fields({ node: true, size: true }).exec((res) => {
		let textCanvas = res[0].node  // 重点1
		// 设置canvas的显示大小和实际大小
		textCanvas.width = res[0].width;
		textCanvas.height = res[0].height;
		
		let canvasWidth = textCanvas.width;
		let canvasHeight = textCanvas.height;

		context = textCanvas.getContext('2d')  // 重点2
	
		// 缩放比例
		// const scaleX = renderWidth / canvasWidth;
		// const scaleY = renderHeight / canvasHeight;
		// context.scale(scaleX, scaleY);
		
		context.clearRect(0, 0, canvasWidth, canvasHeight);
		
		let loadedImages = 0;
		const totalImages = 2;
		
		// 等待所有图片加载完成后再绘制文字
		const checkAllLoaded = () => {
			loadedImages++;
			if (loadedImages === totalImages) {
				_this.drawTexts(context, userInfo);
				_this.createPoster(textCanvas, canvasWidth, canvasHeight, (res) => {
					callback && callback(res)
				});
			}
		};
		
		//绘制名片主要区域
		// 名片背景
		const image1 = textCanvas.createImage();
		image1.src = userInfo.cardBg
		image1.onload = () => {
			// 图片原始尺寸
			const imgWidth = image1.width;
			const imgHeight = image1.height;
				
			// 计算缩放比例(保持图片比例)
			const scaleX = canvasWidth / imgWidth;
			const scaleY = canvasHeight / imgHeight;
			const scale = Math.min(scaleX, scaleY); // 取较小比例,避免拉伸变形
			
			context.drawImage(image1, 0, 0, imgWidth, imgHeight,0,0,imgWidth * scale, imgHeight * scale);
			checkAllLoaded();
		}
		image1.onerror = () => {
			checkAllLoaded();
		}
		
		//头像
		const image = textCanvas.createImage()
		image.src = userInfo.profile_picture
		image.onload = () => {
			// 图片原始尺寸
			const imgWidth = image.width;
			const imgHeight = image.height;
				
			// 头像绘制位置和尺寸
			const radius = 25; // 圆形头像半径
			const centerX = 270 + radius; // 圆心x坐标
			const centerY = 25 + radius;  // 圆心y坐标
					
			// 开始绘制圆形裁剪区域
			context.save();
			context.beginPath();
			context.arc(centerX, centerY, radius, 0, Math.PI * 2);
			context.closePath();
			context.clip();	  
			// 计算图片的最佳缩放比例(保持原始比例)
				const imageRatio = imgWidth / imgHeight;
				const targetRatio = 1; // 圆形是1:1比例
			
				let drawWidth, drawHeight;
				if (imageRatio > targetRatio) {
				// 图片比目标区域宽,按高度缩放
				drawHeight = radius * 2;
				drawWidth = drawHeight * imageRatio;
				} else {
				// 图片比目标区域高,按宽度缩放
				drawWidth = radius * 2;
				drawHeight = drawWidth / imageRatio;
				}
			
				// 计算居中偏移量
				const offsetX = (drawWidth - radius * 2) / 2;
				const offsetY = (drawHeight - radius * 2) / 2;
			
				// 绘制圆形头像(居中且保持原始比例)
				context.drawImage(
				image,
				0, 0, imgWidth, imgHeight,
				centerX - radius - offsetX,
				centerY - radius - offsetY,
				drawWidth,
				drawHeight
				);
			
				// 恢复画布状态
				context.restore();
				
				// 绘制圆形边框(可选)
				context.strokeStyle = '#fff';
				context.lineWidth = 2;
				context.beginPath();
				context.arc(centerX, centerY, radius, 0, Math.PI * 2);
				context.stroke();
			checkAllLoaded();
		}
		image.onerror = () => {
			checkAllLoaded();
		}
});
	

},

// 绘制文字内容
drawTexts(context, userInfo) {
	// 姓名
	context.textAlign = 'left';
	context.font = 'bold 24px Arial, sans-serif';
	context.fillStyle = "#ffffff";
	context.fillText(userInfo.name || '未设置姓名', 20, 40);
	
	// 职称
	context.textAlign = 'left';
	context.font = '16px Arial, sans-serif';
	context.fillStyle = "#ffffff";
	context.fillText(userInfo.position || '未设置职位', 20, 70);
	
	// 公司
	context.textAlign = 'left';
	context.font = '16px Arial, sans-serif';
	context.fillStyle = "#ffffff";
	context.fillText(userInfo.company || '未设置公司', 20, 90);
	
	// 电话
	context.textAlign = 'left';
	context.font = '16px Arial, sans-serif';
	context.fillStyle = "#ffffff";
	context.fillText(userInfo.phone_number || '未设置电话', 20, 130);
	
	// 邮箱
	context.textAlign = 'left';
	context.font = '16px Arial, sans-serif';
	context.fillStyle = "#ffffff";
	context.fillText(userInfo.email || '未设置邮箱', 20, 160);
	
	// 地址
	const address = (userInfo.region || '') + (userInfo.detailed_address || '') || '未设置地址';
	context.textAlign = 'left';
	context.font = '16px Arial, sans-serif';
	context.fillStyle = "#ffffff";
	context.fillText(address, 20, 190);
},

createPoster(textCanvas, winWidth, winHeight, callback) {
	setTimeout(()=>{
		wx.canvasToTempFilePath({
			x: 0,
			y: 0,
			width:winWidth,
			height:winHeight,
			canvas: textCanvas,
			fileType: 'png',
			quality: 1,
			success: function(res) {
				console.log(res.tempFilePath)
				callback && callback(res.tempFilePath)
			},
			fail() {
				callback && callback(false)
			}
		})
	},3000)
}
javascript 复制代码
//调用 生成海报
let imageUrl = this.posterImg;
if (!imageUrl) {
	imageUrl = await this.createShare();
}
相关推荐
人工智能训练师3 小时前
Ubuntu22.04如何安装新版本的Node.js和npm
linux·运维·前端·人工智能·ubuntu·npm·node.js
Seveny073 小时前
pnpm相对于npm,yarn的优势
前端·npm·node.js
yddddddy4 小时前
css的基本知识
前端·css
昔人'4 小时前
css `lh`单位
前端·css
前端君5 小时前
实现最大异步并发执行队列
javascript
Nan_Shu_6146 小时前
Web前端面试题(2)
前端
知识分享小能手6 小时前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
蚂蚁RichLab前端团队7 小时前
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
前端·javascript·人工智能
孩子 你要相信光7 小时前
css之一个元素可以同时应用多个动画效果
前端·css
萌萌哒草头将军7 小时前
Oxc 和 Rolldown Q4 更新计划速览!🚀🚀🚀
javascript·vue.js·vite