uniapp微信小程序保存海报到手机相册canvas

#VibeCoding·九月创作之星挑战赛#

在uniapp中实现微信小程序保存海报到手机相册,主要涉及Canvas绘制和图片保存。以下是关键步骤和代码示例:

一、关键代码展示:
1. 模板配置:页面展示该海报,可直接查看,也可下载保存到手机相册,html关键代码如下:
<view class="content">
	<canvas class="canvas-poster" canvas-id="canvasposter"></canvas>
</view>
2. 海报绘制:js关键代码如下:
//绘制到canvas
downloadQrcode: function() {
	var ctx = uni.createCanvasContext("canvasposter")
	let that = this

	// #ifdef MP-WEIXIN
	uni.downloadFile({
		url: 'https://xxx.zexun.tech/getfile/statics/cdn/images/large_invite_bg.png', // 网络图片链接
		success: downloadResult => {
			if (downloadResult.statusCode === 200) {
				/* 绘制第一张照片*/
				// 参数说明:('图片路径',canvas的横坐标,canvas的纵坐标,图片的宽度,图片的宽度)
				ctx.drawImage(downloadResult.tempFilePath, 0, 0, 750, 1624)

				// 绘制第二张图片-base64-转换真机可读
				wx.getFileSystemManager().writeFile({
					filePath: wx.env.USER_DATA_PATH + "/test.png",
					data: that.codeBase64,
					encoding: 'base64',
					success: (res) => {
						ctx.drawImage(wx.env.USER_DATA_PATH + "/test.png", 164,
							816, 420, 420)
						ctx.setTextAlign("center")
						ctx.setFontSize(34)  // 第一行文字
						ctx.fillStyle = '#2B5370'; // 设置文字颜色为白色
						ctx.fillText('我的邀请码:'+ that.$userInfo.agentNo, 375,
							668) //文字内容、x坐标,y坐标
						ctx.setFontSize(26) // 第二行文字
						ctx.fillStyle = '#636D86'; // 设置文字颜色为白色
						ctx.fillText('扫一扫下方二维码,成为我的业务伙伴', 365,
							782) //文字内容、x坐标,y坐标
						uni.showLoading({
							title: "图片生成中...",
						})
						ctx.draw(
							false,
							setTimeout(() => {
								that.getTempFilePath()
							}, 2500)
						)
					}
				})

			}
		},
		fail: () => {
			uni.showToast({
				title: '下载失败',
				icon: 'none'
			});
		}
	});
	// #endif
},
//获取临时路径
getTempFilePath: function() {
	// 当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径
	uni.canvasToTempFilePath({
		canvasId: "canvasposter",
		success: (res) => {
			console.log(res.tempFilePath)
			this.saveImageToPhotosAlbum(res.tempFilePath) //保存到相册
		},
	})
},
//把生成的图片保存至相册
saveImageToPhotosAlbum: function(imgUrl) {
	uni.hideLoading()
	if (imgUrl) {
		uni.saveImageToPhotosAlbum({
			filePath: imgUrl,
			success: (res) => {
				uni.showToast({
					title: "保存成功",
					icon: "success",
					duration: 2000,
				})
			},
			fail: (err) => {
				uni.showToast({
					title: "保存失败",
					icon: "none",
					duration: 2000,
				})
			},
		})
	} else {
		uni.showToast({
			title: "绘制中……",
			icon: "loading",
			duration: 3000,
		})
	}
},
3. 样式css关键代码如下:
/* 绘制图片canvas样式 */
.canvas-poster {
	position: fixed;
	width: 750px;
	height: 1624px;
	top: 100%;
	left: 100%;
}
二、完整代码附录:
<template>
	<view class="content">
		<!-- #ifdef MP-WEIXIN -->
		<view class="guide_header_page2">
			<view :style="{ height: statusBarHeight }"></view>
			<view class="guide_title" :style="{ height: navigationBarHeight, lineHeight:navigationBarHeight  }">
				<uni-icons type="back" size="22" color="#232323" @click="goBack" class="icon"></uni-icons>
				<text>我的邀请码</text>
			</view>
		</view>
		<!-- #endif -->

		<view class="invite">
			<image src="https://xxx.zexun.tech/getfile/statics/cdn/images/large_invite_bg.png" mode="widthFix"
				class="bg" />
			<view class="invite_title">我的邀请码:{{$userInfo.agentNo}}</view>
			<view class="invite_tip">
				扫一扫下方二维码,成为我的业务伙伴
			</view>
			<image :src="'data:image/png;base64,' + codeBase64" mode="widthFix" class="qrcode" />
			<view class="invite_btn">
				<u-button text="保存图片" shape="circle" color="linear-gradient(to right, #26B6B9, #21CE98)" @click="downloadQrcode"></u-button>
			</view>
		</view>
		<canvas class="canvas-poster" canvas-id="canvasposter"></canvas>
	</view>
</template>

<script>
	import {
		getInvitationCode
	} from '@/common/api.js'
	export default {
		data() {
			return {
				// #ifdef MP-WEIXIN
				statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px', // 状态栏高度
				capBarHeight: uni.getMenuButtonBoundingClientRect().height + 'px', // 胶囊高度
				navigationBarHeight: (uni.getMenuButtonBoundingClientRect().top - uni.getSystemInfoSync()
					.statusBarHeight) * 2 + uni.getMenuButtonBoundingClientRect().height + 'px', // 导航栏高度
				barCapHeight: (uni.getMenuButtonBoundingClientRect().top - uni.getSystemInfoSync()
						.statusBarHeight) * 2 + uni.getMenuButtonBoundingClientRect().height + uni.getSystemInfoSync()
					.statusBarHeight + 'px', //导航栏高度+状态栏高度
				// #endif
				codeBase64: null,
				type: '2', // 2-服务商邀请码,3-业务员邀请码
			}
		},
		onLoad() {
			this.getInvitationCode()
		},
		methods: {
			goBack() {
				uni.navigateBack()
			},
			// 获取二维码
			getInvitationCode() {
				uni.showLoading({
					mask: true
				})
				getInvitationCode({
						inviteCode: this.$store.state.$userInfo.agentId,
						type: this.type
					})
					.then((res) => {
						uni.hideLoading()
						this.codeBase64 = res.data
					})
					.catch((err) => {
						uni.hideLoading()
						console.log(err)
					})
			},
			//绘制到canvas
			downloadQrcode: function() {
				var ctx = uni.createCanvasContext("canvasposter")
				let that = this

				// #ifdef MP-WEIXIN
				uni.downloadFile({
					url: 'https://xxx.zexun.tech/getfile/statics/cdn/images/large_invite_bg.png', // 网络图片链接
					success: downloadResult => {
						if (downloadResult.statusCode === 200) {
							/* 绘制第一张照片*/
							// 参数说明:('图片路径',canvas的横坐标,canvas的纵坐标,图片的宽度,图片的宽度)
							ctx.drawImage(downloadResult.tempFilePath, 0, 0, 750, 1624)

							// 绘制第二张图片-base64-转换真机可读
							wx.getFileSystemManager().writeFile({
								filePath: wx.env.USER_DATA_PATH + "/test.png",
								data: that.codeBase64,
								encoding: 'base64',
								success: (res) => {
									ctx.drawImage(wx.env.USER_DATA_PATH + "/test.png", 164,
										816, 420, 420)
									ctx.setTextAlign("center")
									ctx.setFontSize(34)  // 第一行文字
									ctx.fillStyle = '#2B5370'; // 设置文字颜色为白色
									ctx.fillText('我的邀请码:'+ that.$userInfo.agentNo, 375,
										668) //文字内容、x坐标,y坐标
									ctx.setFontSize(26) // 第二行文字
									ctx.fillStyle = '#636D86'; // 设置文字颜色为白色
									ctx.fillText('扫一扫下方二维码,成为我的业务伙伴', 365,
										782) //文字内容、x坐标,y坐标
									uni.showLoading({
										title: "图片生成中...",
									})
									ctx.draw(
										false,
										setTimeout(() => {
											that.getTempFilePath()
										}, 2500)
									)
								}
							})

						}
					},
					fail: () => {
						uni.showToast({
							title: '下载失败',
							icon: 'none'
						});
					}
				});
				// #endif


				// #ifndef MP-WEIXIN
				/* 绘制第一张照片*/
				// 参数说明:('图片路径',canvas的横坐标,canvas的纵坐标,图片的宽度,图片的宽度)
				ctx.drawImage("https://jsfzapi.zexun.tech/getfile/statics/cdn/images/invite_bg.png", 0, 0,
					750, 1624)
				/* 绘制第二张照片*/
				ctx.drawImage("data:image/png;base64," + this.codeBase64, 164, 912, 420, 420)
				ctx.setTextAlign("center")
				ctx.setFontSize(32)
				ctx.setTextAlign("center")
				ctx.fillText(this.$realAgentInfo.shortName, 375, 880) //文字内容、x坐标,y坐标
				uni.showLoading({
					title: "图片生成中...",
				})
				ctx.draw(
					false,
					setTimeout(() => {
						this.getTempFilePath()
					}, 1500)
				)
				// #endif
			},
			//获取临时路径
			getTempFilePath: function() {
				// 当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径
				uni.canvasToTempFilePath({
					canvasId: "canvasposter",
					success: (res) => {
						console.log(res.tempFilePath)
						this.saveImageToPhotosAlbum(res.tempFilePath) //保存到相册
					},
				})
			},
			//把生成的图片保存至相册
			saveImageToPhotosAlbum: function(imgUrl) {
				uni.hideLoading()
				if (imgUrl) {
					uni.saveImageToPhotosAlbum({
						filePath: imgUrl,
						success: (res) => {
							uni.showToast({
								title: "保存成功",
								icon: "success",
								duration: 2000,
							})
						},
						fail: (err) => {
							uni.showToast({
								title: "保存失败",
								icon: "none",
								duration: 2000,
							})
						},
					})
				} else {
					uni.showToast({
						title: "绘制中……",
						icon: "loading",
						duration: 3000,
					})
				}
			},
		},
	}
</script>

<style lang="scss" scoped>
	/* 绘制图片canvas样式 */
	.canvas-poster {
		position: fixed;
		width: 750px;
		height: 1624px;
		top: 100%;
		left: 100%;
	}

	.content {
		.invite {
			width: 100%;
			position: relative;

			.bg {
				width: 750rpx;
			}

			.invite_title {
				min-width: 418rpx;
				position: absolute;
				left: 52%;
				transform: translateX(-50%);
				top: 630rpx;
				font-weight: bold;
				font-size: 34rpx;
				color: #2B5370;
			}
			
			.invite_tip {
				min-width: 442rpx;
				position: absolute;
				left: 50%;
				transform: translateX(-50%);
				top: 766rpx;
				font-weight: 500;
				font-size: 26rpx;
				color: #636D86;
			}

			.qrcode {
				width: 420rpx;
				height: 420rpx;
				left: 50%;
				transform: translateX(-50%);
				top: 830rpx;
				position: absolute;
			}

			.invite_btn {
				width: 484rpx;
				height: 76rpx;
				bottom: 176rpx;
				position: absolute;
				z-index: 999;
				left: 50%;
				transform: translateX(-50%);
			}
		}
	}
</style>
三、效果图展示:

在这里插入图片描述

提示:使用 uni.downloadFile 下载网络图片 https://xxx.xxx 等,需要在小程序后台配置服务器域名;
在这里插入图片描述

有什么疑问,可以评论区回复,大家一起探讨~

<think>好的,我现在需要帮助用户解决在UniApp中为微信小程序生成海报的问题。用户提到参考了两个引用,其中提到了snapshot组件和canvas的使用。首先,我需要理清这些步骤。 首先,用户提供的引用[1]详细说明了如何配置manifest.json和页面设置,包括启用Skyline渲染引擎和自定义导航栏。这可能是因为生成海报需要更高效的渲染能力,而Skyline可能提供了更好的性能支持。不过,用户提到不确定snapshot的效果,可能需要进一步验证步骤是否正确。 引用[2]则提到了在模板中使用canvas组件,并指定了type为2d,这可能是微信小程序的新版Canvas API,需要确认uniapp是否支持这种写法。同时,用户的需求是生成海报保存,通常需要绘制图片、文字等到Canvas上,然后转换为图片保存到本地。 接下来,我需要将这两个引用中的信息结合起来,形成一个完整的教程。首先,配置项目的manifest.json,确保微信小程序的设置正确,特别是Skyline相关的配置和自定义导航栏。然后,在页面中添加Canvas组件,注意使用type="2d"以确保使用新版API。接着,可能需要使用uniapp的JS API来操作Canvas,比如通过uni.createCanvasContext或者微信的原生API进行绘制。这里需要注意uniapp的语法和微信原生API之间的差异,可能需要条件编译来处理不同平台的问题。 另外,用户提到snapshot组件的示例代码需要复制到项目中,如果是vue3的setup写法,可能需要创建另一个script标签来兼容vue2的方法。这说明用户可能在处理不同版本的语法时遇到了问题,需要指导如何正确整合代码。 然后,生成海报的具体步骤可能包括:设置Canvas的宽高,绘制背景图片、文本、二维码等内容,最后调用CanvasToTempFilePath将Canvas内容导出为图片路径,再保存相册。在此过程中,需要注意权限问题,比如用户需要授权相册写入权限,否则保存会失败。 同时,用户可能遇到的常见问题包括Canvas绘制内容不显示、图片跨域问题、真机调试时的兼容性问题等。需要提醒用户检查网络请求是否合法(HTTPS)、图片资源是否加载完成,以及使用真机调试确认功能正常。 最后,生成相关问题需要考虑用户可能进一步的需求,比如如何动态调整海报内容、如何优化性能、处理不同屏幕尺寸的适配等。确保问题覆盖用户可能遇到的后续挑战,帮助他们深入理解和解决问题。</think>### UniApp微信小程序生成海报实现教程 根据官方文档和开发实践,以下是实现步骤及注意事项: #### 一、环境配置 1. **manifest.json配置** ```json "mp-weixin": { "appid": "你的小程序ID", "lazyCodeLoading": "requiredComponents", "rendererOptions": { "skyline": { "defaultDisplayBlock": true, "disableABTest": true, "sdkVersionBegin": "3.0.1", "sdkVersionEnd": "15.255.255" } }, "style": "v2", "componentFramework": "glass-easel" } ``` *注意:Skyline渲染引擎可提升性能,但需要基础库3.0.1+支持[^1]* #### 二、页面配置 ```json // page.json { "navigationStyle": "custom", "renderer": "skyline", "componentFramework": "glass-easel" } ``` *必须设置自定义导航栏以适应海报全屏布局[^1]* #### 三、视图层实现 ```html <template> <view class="container"> <!-- 2D Canvas容器 --> <canvas canvas-id="posterCanvas" id="posterCanvas" type="2d" :style="{ width: '655rpx', height: '960rpx' }"> </canvas> <!-- 保存按钮 --> <button @tap="savePoster">保存海报</button> </view> </template> ``` *使用type="2d"获取Canvas 2D上下文[^2]* #### 四、逻辑层实现 ```javascript // Vue3 Composition API import { onMounted } from 'vue' import { useBase64 } from '@/utils/util' export default { setup() { let canvasCtx = null onMounted(async () => { // 初始化Canvas const query = uni.createSelectorQuery() query.select('#posterCanvas') .fields({ node: true, size: true }) .exec(async (res) => { const canvas = res[0].node const dpr = uni.getSystemInfoSync().pixelRatio canvas.width = res[0].width * dpr canvas.height = res[0].height * dpr // 获取2D上下文 canvasCtx = canvas.getContext('2d') canvasCtx.scale(dpr, dpr) // 开始绘制 await drawPoster() }) }) // 绘制海报 const drawPoster = async () => { try { // 绘制背景 canvasCtx.fillStyle = '#FFFFFF' canvasCtx.fillRect(0, 0, 655, 960) // 添加图片(示例) const img = await loadImage('/static/logo.png') canvasCtx.drawImage(img, 50, 50, 100, 100) // 添加文字 canvasCtx.font = '20px sans-serif' canvasCtx.fillStyle = '#333333' canvasCtx.fillText('扫码加入我们', 200, 100) } catch (e) { console.error('绘制失败:', e) } } // 图片加载方法 const loadImage = (src) => { return new Promise((resolve, reject) => { const img = canvas.createImage() img.onload = () => resolve(img) img.onerror = reject img.src = src }) } // 保存海报 const savePoster = () => { uni.canvasToTempFilePath({ canvasId: 'posterCanvas', success: (res) => { uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: () => uni.showToast({ title: '保存成功' }), fail: () => uni.showToast({ title: '请授权相册权限' }) }) } }) } return { savePoster } } } ``` #### 五、注意事项 1. **图片跨域问题**:网络图片需配置downloadFile域名白名单 2. **真机调试**:务必在真机测试保存功能,模拟器可能无法正常保存 3. **权限处理**:需处理相册授权逻辑`scope.writePhotosAlbum` 4. **单位转换**:rpx到px的转换需考虑设备屏幕密度 5. **性能优化**:复杂海报建议预生成或服务端生成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值