uni-app微信小程序合成海报并保存到相册

  页面有三部分的图片。logo。二维码和背景图。其中背景图是本地的。

logo和二维码是接口给的。

像这样的海报合成的例子我写过一篇,是在vue的项目中的vue把几张图片logo。二维码。背景合成一个海报并下载,使用canvas

但是小程序里面不支持使用var img=new Image();    他会有这个文章里面的报错

 //把网络图片改成本地图片
           getNetworkImage(url) {
                return new Promise((resolve, reject) => {
                    uni.downloadFile({
                        url,
                        success: (e) => {
                            const p = e.tempFilePath
                            if (p.indexOf('json') > -1) {
                                reject(p)
                                return false
                            }
                            resolve(p)
                        },
                        fail: (r) => {
                            reject(r)
                        }
                    })
                })
            },
            //把base64图片转为本地图片
            base64ToSave(base64data, FILE_BASE_NAME = 'tmp_base64src') {
                const fsm = uni.getFileSystemManager();
                return new Promise((resolve, reject) => {
                    //format这个跟base64数据的开头对应
                    const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
                    if (!format) {
                        reject(new Error('ERROR_BASE64SRC_PARSE'));
                    }
                    const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`;
                    const buffer = wx.base64ToArrayBuffer(bodyData);
                    fsm.writeFile({
                        filePath,
                        data: buffer,
                        encoding: 'binary',
                        success() {
                            resolve(filePath);
                        },
                        fail() {
                            reject(new Error('ERROR_BASE64SRC_WRITE'));
                        },
                    });
                });
            },

 我的二维码和logo都是接口传的数据。这里都转为本地的。

 genQrFile1() {
                var urlQR = this.base64ToSave(this.qrBase64);//二维码。base64转为本地图片
                var logo=this.getNetworkImage(this.userinfo.logo1);//机构logo网络图片转为本地图片
                let that = this;
                Promise.all([
                    urlQR,logo
                ]).then(res => {                    
                    that.qrUrl=res[0]                    
                    let ctx = uni.createCanvasContext('firstCanvas', this);
                    ctx.drawImage("/pages_student/static/images/posterbg1.png", 0, 0, 375, 812);
                    ctx.drawImage(res[1],  36, 25,80, 60);
                    ctx.drawImage(res[0], 201.5 / 2.0, 1098.67 / 2,
                        347.0 / 2.0, 347.0 / 2.0);
                        
                    ctx.draw(false, () => {
                        uni.canvasToTempFilePath({
                            x: 0,
                            y: 0,
                            width: 375,
                            height: 812,
                            destWidth: 375 * 2,
                            destHeight: 812 * 2,
                            canvasId: 'firstCanvas',
                            success: function(res) {
                                uni.getFileSystemManager().readFile({
                                    filePath: res.tempFilePath,
                                    encoding: 'base64',
                                    success: r => {
                                        that.wholeBase64 = "data:image/jpg;base64," + r.data;                                        
                                    }        
                                })
                                
                                that.imgurl = res.tempFilePath
                            },
                            fail(e) {
            
                            }
                        });
                    });
                })
            },

主要代码是上面的部分。下面贴一下全部的代码:保存合成海报保存到相册

<template>
	
	<view class="container">
		<scroll-view scroll-y="true" class="scroll-view" v-if=!isShowShare>

		  <image @click="cancel()"  class="back-btn" src="../static/images/back_btn.png" mode="aspectFit"></image>
		  
		  <!-- //多机构 -->
			<template v-if="notDw">
				<image class="bg-set"  src="https://ossvvwx.heebu.cn/public/growplan/mini/common/poster_bg1.png" ></image>
			    <image class="logo" :src="userinfo.logo1" mode="widthFix"></image>
			</template>
		
		<!-- 多维	 -->
			<template v-else>
					<image class="bg-set" src="https://ossvvwx.heebu.cn/public/growplan/mini/common/poster_bg.png"></image>
			</template>
			

			<view class="bottom-qr">
				<image class="image" src="/pages_student/static/images/poster_qr.png"></image>
			 
				<view class="center">
					<text class="test-qr">测评二维码 </text>
				</view>


				<view class="center">
					<image show-menu-by-longpress="true" class="qr-image" :src=qrUrl></image>
				</view>

				<view class="center">
					<text class="share-mid-tips">扫一扫二维码,了解自己的学习潜力 </text>
				</view>

				<view class="center">
					<view class="share-class" @click="share">去分享</view>
				</view>

				<view class="center">
					<view class="cancel-class" @click="cancel">取消</view>
				</view>
			</view>
		</scroll-view>

		<view class="wrapper" style="position: fixed;left: 1000rpx;"><canvas style="height: 1624rpx ;width:750rpx; backgroundColor: #FFFFFF;z-index: -100;"
			 canvas-id="firstCanvas">
			</canvas>
		</view>



		<view v-if=isShowShare style=" ">
			<view class="share-whole">
				<view class="top-image center">
					<image mode='widthFix' show-menu-by-longpress="true" class="share-class" 
					 :src=imgurl></image>
				</view>

				<view class="top-image" style="background-color: #EBF7FF;height: 200rpx;">
				</view>

				<view class="share-bottom">

					<view class="share-top">

						<view class="item" @click="showShareQr">
							<image class="item-image" src="/pages_student/static/images/wechat.png"></image>
							<text class="title">微信 </text>
							<!-- <button open-type="share" plain=true class="share" style=""></button> -->
						</view>

						<view class="item" @click="saveToAlbum">
							<image class="item-image" src="/pages_student/static/images/save.png"></image>
							<text class="title">保存到相册 </text>
						</view>

					</view>
					<view class="line">
					</view>
					<view class="share-cancel" @click="cancelClick">
						<text style="">取消
						</text>
					</view>
				</view>
			</view>

		</view>



		<luPopupWrapper ref="luPopupWrapper" :type="type" :transition="transition" :backgroundColor="backgroundColor" :active="active"
		 :height="height" :width="width" :popupId="popupId" :maskShow="maskShow" :maskClick="maskClick" :closeCallback="closeCallback">

			<view class="long-press">
				<view class="center">
					<view class="center top">
						<image class="top-image" show-menu-by-longpress="true" :src='imgurl' mode='scaleToFill' style="width: 395rpx;height: 833rpx;"></image>
					</view>
				</view>
				<view class="center">
					<image class="tips" src="/pages_student/static/images/student_long_press.png"></image>
				</view>
				<view class="center" @click="dismissPopup">
					<image class="close" src="/pages_student/static/images/student_close.png"></image>
				</view>
			</view>
		</luPopupWrapper>



	</view>
</template>

<script>
	import luPopupWrapper from "../components/lu-popup-wrapper/lu-popup-wrapper.vue";
	import useLocalStorage from '@/utils/useLocalStorage';
	const storage = useLocalStorage()
	import {
		mapMutations,
		mapState,
	} from 'vuex'
	export default {
		components: {
			luPopupWrapper
		},
		computed: {
			...mapState({
				zoneinfo: s => s.zoneinfo,
				userinfo: s => s.userinfo,
                isDW: s => s.isDW
			}),
		},
		data() {
			return {
				type: "bottom", // left right top bottom center
				transition: "slider", //none slider fade
				backgroundColor: '#FFF0',
				active: true,
				height: "100%",
				width: "100%",
				popupId: 1,
				maskShow: true,
				maskClick: true,
				qrUrl: '',
				qrBase64: "data:image/jpg;base64,",
				imgurl: '',
				isShowShare: false,
				wholeBase64: "",
				notDw:false,//不是多维
			}
		},
		onLoad() {
			this.getQrData();
			this.notDw=this.isDW=="false"?true:false;
		},
		methods: {
			 //多维生成海报
			genQrFile() {
				var urlQR = this.base64ToSave(this.qrBase64)
				let that = this;
				Promise.all([
					urlQR
				]).then(res => {					
					that.qrUrl=res[0]					
					let ctx = uni.createCanvasContext('firstCanvas', this);
					ctx.drawImage("/pages_student/static/images/posterbg.png", 0, 0, 375, 812);
					ctx.drawImage(res[0], 201.5 / 2.0, 1098.67 / 2,
						347.0 / 2.0, 347.0 / 2.0);
					ctx.draw(false, () => {
						uni.canvasToTempFilePath({
							x: 0,
							y: 0,
							width: 375,
							height: 812,
							destWidth: 375 * 2,
							destHeight: 812 * 2,
							canvasId: 'firstCanvas',
							success: function(res) {
								uni.getFileSystemManager().readFile({
									filePath: res.tempFilePath,
									encoding: 'base64',
									success: r => {
										that.wholeBase64 = "data:image/jpg;base64," + r.data
									}

								})
								that.imgurl = res.tempFilePath
							},
							fail(e) {

							}
						});
					});

				})
			},
			//多机构生成海报
			genQrFile1() {
				var urlQR = this.base64ToSave(this.qrBase64);//二维码。base64转为本地图片
				var logo=this.getNetworkImage(this.userinfo.logo1);//机构logo网络图片转为本地图片
				let that = this;
				Promise.all([
					urlQR,logo
				]).then(res => {					
					that.qrUrl=res[0]					
					let ctx = uni.createCanvasContext('firstCanvas', this);
					ctx.drawImage("/pages_student/static/images/posterbg1.png", 0, 0, 375, 812);
					ctx.drawImage(res[1],  36, 25,80, 60);
					ctx.drawImage(res[0], 201.5 / 2.0, 1098.67 / 2,
						347.0 / 2.0, 347.0 / 2.0);
						
					ctx.draw(false, () => {
						uni.canvasToTempFilePath({
							x: 0,
							y: 0,
							width: 375,
							height: 812,
							destWidth: 375 * 2,
							destHeight: 812 * 2,
							canvasId: 'firstCanvas',
							success: function(res) {
								uni.getFileSystemManager().readFile({
									filePath: res.tempFilePath,
									encoding: 'base64',
									success: r => {
										that.wholeBase64 = "data:image/jpg;base64," + r.data;										
									}		
								})
								
								that.imgurl = res.tempFilePath
							},
							fail(e) {
			
							}
						});
					});
				})
			},
			//返回上一页
			cancel() {
				uni.navigateBack({});
			},
			genAndShare(type) {			
				if (this.imgurl) {
					if (type === "save") {
						uni.saveImageToPhotosAlbum({
							filePath: this.imgurl,
							success: function() {
								uni.showToast({
									title: '保存成功',
									icon: 'none'
								});
							}
						});
					}
				}
			},
			saveToAlbum() {
				this.genAndShare("save")
			},
          //把网络图片改成本地图片
           getNetworkImage(url) {
                return new Promise((resolve, reject) => {
                    uni.downloadFile({
                        url,
                        success: (e) => {
                            const p = e.tempFilePath
                            if (p.indexOf('json') > -1) {
                                reject(p)
                                return false
                            }
                            resolve(p)
                        },
                        fail: (r) => {
                            reject(r)
                        }
                    })
                })
            },
			//把base64图片转为本地图片
			base64ToSave(base64data, FILE_BASE_NAME = 'tmp_base64src') {
				const fsm = uni.getFileSystemManager();
				return new Promise((resolve, reject) => {
					//format这个跟base64数据的开头对应
					const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
					if (!format) {
						reject(new Error('ERROR_BASE64SRC_PARSE'));
					}
					const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`;
					const buffer = wx.base64ToArrayBuffer(bodyData);
					fsm.writeFile({
						filePath,
						data: buffer,
						encoding: 'binary',
						success() {
							resolve(filePath);
						},
						fail() {
							reject(new Error('ERROR_BASE64SRC_WRITE'));
						},
					});
				});
			},
       // 获取二维码
			async getQrData() {
				this.$loading.show()
				let para = {
					"teacherid": storage.getItem('token'),
					"zoneid": this.userinfo.zoneid,
					"size": "240",
                    'type': this.isDW == 'true' ? 1 : 2
				};
                
				const {
					data
				} = await this.$http.fromteacherqrcode(para);

				this.$loading.hide()
				if (data.code == 1) {
					this.qrBase64 += data.data
					if(this.notDw){//是否是多维。获取canvas海报图片
						this.genQrFile1();
					}else{
						this.genQrFile();
					}
					
				}
			},

			share() {
				this.isShowShare = true;
			},
			showShareQr() {
				this.$refs.luPopupWrapper.show();
			},
			cancelClick() {
				this.isShowShare = false;
			},
			dismissPopup() {
				this.$refs.luPopupWrapper.close()
			}
		}
	}
</script>

<style lang="scss" scoped>
	.container {
		.back-btn {
			position: fixed;
			top: 80rpx;
			left: 20rpx;
			width: 78rpx;
			height: 60rpx;
			z-index: 999;
		}
		.scroll-view{
			position:relative;
		}
		.logo{
			position:absolute;
			width: 160rpx;
			top: 80rpx;
			left:60rpx;
			display:block;
		}
		.long-press {
			width: 750rpx;
			height: 1757rpx;
			.center {
				display: flex;
				justify-content: center;
			}


			.top {
				background-color: #21A3FE;
				border-radius: 20rpx;
				bottom: 206rpx;
				position: fixed;
			}


			.top-image {
				width: 500rpx !important;
				height: 1083rpx  !important;
				border-radius: 20rpx;
				top: 60rpx;
			}

			.close {
				width: 55rpx;
				height: 55rpx;
				bottom: 60rpx;
				position: fixed;
			}

			.tips {
				width: 518rpx;
				height: 81rpx;
				bottom: 139rpx;
				position: fixed;

			}

		}

		.bg-set {
			width: 750rpx;
			height: 1757rpx;
			top: 0;
			left: 0;
			z-index: -1;

		}

		.bottom-qr {
			.image {
				width: 674rpx;
				height: 804rpx;
			}

			left:36.7rpx;
			position: absolute;
			bottom: 0rpx;

			.share-mid-tips {
				text-align: center;
				font-size: 27rpx;
				font-family: PingFang SC;
				font-weight: bold;
				color: #757575;
				line-height: 27rpx;
				margin-top: -256rpx;
				position: absolute;
			}

			.qr-image {
				width: 366rpx;
				height: 366rpx;
				margin-top: -633.6rpx;
			}

			.test-qr {
				width: 276rpx;
				height: 56rpx;
				background: #1BA0FE;
				border-radius: 18rpx;
				margin-top: -716rpx;
				position: absolute;
				text-align: center;

				font-size: 29rpx;
				font-family: Source Han Sans CN;
				font-weight: bold;
				color: #FFFFFF;
				line-height: 56rpx;
			}

			.center {
				display: flex;
				justify-content: center;
			}

			.share-class {
				width: 426.6rpx;
				height: 60rpx;
				line-height: 60rpx;
				border: 4rpx solid #1BA0FE;
				border-radius: 45rpx;
				margin-top: -189rpx;
				position: absolute;
				text-align: center;
				font-size: 28rpx;
				font-family: PingFang SC;
				font-weight: bold;
				color: #1BA0FE;
			}

			.cancel-class {
				width: 426.6rpx;
				height: 60rpx;
				line-height: 60rpx;
				border: 4rpx solid #BAC2C8;
				border-radius: 45rpx;
				margin-top: -106rpx;
				position: absolute;
				text-align: center;
				font-size: 28rpx;
				font-family: PingFang SC;
				font-weight: bold;
				color: #BAC2C8;
			}

		}

		.share-whole {
			text-align: center;
			width: 100%;
			height: 100%;
			background-color: #EBF7FF;

			.top-image {
				width: 100%;
				display: flex;
				justify-content: center;

			} 
			.share-class{
				width: 500rpx;height: 1082.67rpx;margin-top: 117rpx;
				
			}

			.share-cancel {
				width: 100%;
				font-size: 28rpx;
				font-family: PingFang SC;
				font-weight: bold;
				color: #91979C;
				// position: absolute;
				// bottom: 0;
				height: 127.34rpx;
				line-height: 127.34rpx;
			}

			.line {
				width: 100%;
				border-bottom: 1px solid #F2F2F2;
				margin-top: 28rpx;
				// margin-bottom: 40px;
			}

			.share-bottom {
				height: 375rpx;
				background-color: #FfFFFF;
				position: fixed;
				bottom: 0rpx;
				width: 100%;
				border-radius: 20rpx 20rpx 0rpx 0rpx;

			}

			.share-top {
				// margin-bottom: 50rpx;
				width: 100%;
				display: flex;
				justify-content: space-around;
				flex-direction: row;
				align-items: center;
				height: 213rpx;
				border-radius: 20rpx 20rpx 0rpx 0rpx;


				.item {
					flex: 1;
					display: flex;
					flex-direction: column;
					justify-content: center;
					align-items: center;
					background-color: transparent;
					border: 0rpx;
					border-radius: 0rpx;
					margin-top: 50rpx;

					.share {
						position: absolute;
						width: 30%;
						height: 40%;
						border: 0rpx;
					}

					.title {
						margin-top: 26.6rpx;
						font-size: 24rpx;
						font-family: PingFang SC;
						font-weight: bold;
						color: #989898;
					}

					.item-image {
						width: 86.67rpx;
						height: 86.67rpx;
					}
				}
			}
		}



	}
</style>

 

おすすめ

転載: blog.csdn.net/qq_33769914/article/details/120187966