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

发布于:2025-06-19 ⋅ 阅读:(17) ⋅ 点赞:(0)

<canvas canvas-id="posterId" id="posterId" type="2d" class="poster-canvas"></canvas>
.poster-canvas {
		width: 375px;
		height: 280px;
		background-color: #fff;
		position: absolute;
		left: -9999999px;
}

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)
}
//调用 生成海报
let imageUrl = this.posterImg;
if (!imageUrl) {
	imageUrl = await this.createShare();
}


网站公告

今日签到

点亮在社区的每一天
去签到