Rumah  >  Artikel  >  applet WeChat  >  Membawa anda langkah demi langkah untuk melaksanakan fungsi menyimpan komponen gambar dalam program mini

Membawa anda langkah demi langkah untuk melaksanakan fungsi menyimpan komponen gambar dalam program mini

青灯夜游
青灯夜游ke hadapan
2021-10-27 10:46:402396semak imbas

Artikel ini akan membolehkan anda bercakap tentang pembangunan WeChat applet komponen Simpan Gambar Saya harap ia akan membantu semua orang!

Membawa anda langkah demi langkah untuk melaksanakan fungsi menyimpan komponen gambar dalam program mini

Banyak program mini WeChat membolehkan pengguna berkongsi aktiviti dengan menyimpan poster untuk memberitahu lebih ramai orang tentang program mini mereka yang saya alami semasa membangunkan program mini. [Cadangan pembelajaran berkaitan: Tutorial Pembangunan Program Mini]

Hari ini saya akan berkongsi fungsi menyimpan poster dalam program kecil yang saya buat di syarikat. Mula-mula, izinkan saya menerangkan dahulu bagaimana keperluan dalam syarikat sebelum ini. Program mini dalam talian syarikat akan mempunyai tujuan jangka panjang untuk mempromosikan pengguna baharu Setiap pengguna mesti mempunyai poster mereka sendiri, dan promosi melalui poster peribadi hanyalah cara yang mudah.

Selepas menerima tugas, saya mula-mula pergi ke Universal Internet untuk membuat kajian, tetapi abang senior saya memberitahu saya bahawa dia telah melakukan sesuatu yang serupa, tetapi pada masa itu ia hanya untuk menyelesaikan tugas, jadi kod sangat berantakan, dan kemudian dia mula dari projek lain saya mencari dan mencari kod itu, dan akhirnya menemuinya untuk saya~~~ Tetapi masa yang diberikan kepada saya adalah padat dan tugas itu berat, jadi saya terpaksa membuat beberapa pelarasan. dan serahkan dahulu. Selepas itu, saya mengikuti artikel dalam talian dan mengikuti perangkap langkah demi langkah, dan melaksanakan komponen untuk menyimpan poster langkah demi langkah.

Idea

Pertama sekali, izinkan saya menyatakan bahawa komponen tersebut menggunakan uniapp, yang secara khusus melaksanakan fungsi asas melukis gambar, melukis teks dan menyimpan poster ke album mencukupi sepenuhnya semasa pembangunan Digunakan.

Lukis poster melalui kanvas. Gunakan uni.canvasToTempFilePath untuk menukar kanvas yang dilukis kepada gambar. Gunakan uni.saveImageToPhotosAlbum untuk menyimpan gambar dalam laluan sementara tempatan ke album telefon mudah alih. Idea saya adalah untuk merangkum semua kaedah yang digunakan ke dalam komponen, dan hanya menggunakan komponen induk untuk memanggil kaedah yang perlu digunakan dan melaraskan parameter yang berkaitan. Untuk penggunaan khusus, anda boleh melihat kod sampel

Susunan lukisan kandungan poster melalui kanvas

Tentukan susunan lukisan kandungan poster dengan menggunakan janji objek. Kaedah promise.all() melaksanakan langkah terakhir operasi lukisan kanvas context.draw()

Perhatikan bahawa uni.getImageInfo()

  • Apabila melukis gambar dan avatar, komponen melepasi uni.getImageInfo() Untuk mendapatkan maklumat imej yang berkaitan, prasyarat untuk berjaya memanggil kaedah ini ialah nama domain muat turun dan nama domain permintaan perlu dikonfigurasikan dalam latar belakang applet WeChat Sudah tentu, adalah yang terbaik untuk mengkonfigurasi domain uploadFile namakan bersama untuk mengelakkan kesilapan. Walau bagaimanapun, petua rasmi adalah untuk mengkonfigurasi senarai putih nama domain muat turun, tetapi maklumat imej tidak dapat diperoleh, yang merupakan perangkap besar.

  • Jika tiada konfigurasi yang berkaitan dilakukan, alat penyahpepijatan vconsole dibuka semasa penyahpepijatan atau versi percubaan, versi rasmi, dsb. uni.getImageInfo() boleh mendapatkan maklumat imej Setelah vconsole ditutup, uni.getImageInfo() akan gagal, yang juga merupakan perangkap.

Kaedah komponen ini, pengenalan pembolehubah

props

  • objek info canvas (diperlukan)

    • lebar kanvasLebar kanvas

    • tinggi kanvasKetinggian kanvas

    • pengenalan kanvasId kanvas

  • isFullScreen Boolean
    • adalah benar, ini bermakna kanvas ialah skrin penuh skrin telefon mudah alih dan lebar serta ketinggian yang ditetapkan oleh canvasInfo akan menjadi tidak sah.
    • Lalai adalah palsu

kaedah
  • canvasInit(panggilan balik) Permulaan kanvas, semua operasi kanvas mesti dilakukan dalam fungsi panggil baliknya.
  • drawCanvasImage(context, src, _imageWidth, _imageHeight, dx, dy) Lukis imej pada kanvas
  • drawCircularAvatar, _circularXvatar(context, _circular , _circularY, _circularR) Lukis gambar bulat pada kanvas
  • drawText(options) Lukis teks tunggal atau berbilang baris pada kanvas
  • startDrawToImage( konteks, promiseArr, panggil balik) Lukis() pada kanvas
  • posterToPhotosAlbum(filePath) Simpan ke album telefon mudah alih

Kod sampel
<template>
	<view>
		<view class="savePosterItem">
			<image v-show="tempFilePath" :src="tempFilePath"></image>
			<save-poster-com v-show="!tempFilePath" ref="savePoster" :canvasInfo="canvasInfo"></save-poster-com>
		</view>
		
		
		<button class="savePosterBtn" type="primary" @click="saveBtnFun">保存海报</button>
	</view>
</template>

<script>
	import SavePosterCom from &#39;@/components/SavePosterCom/SavePosterCom.vue&#39;
	export default {
		components: {
			SavePosterCom
		},
		data() {
			return {
				canvasInfo: {
					canvasWidth: 620,
					canvasHeight: 950,
					canvasId: &#39;save-poster&#39;
				},
				tempFilePath: &#39;&#39;,
				canvasBgUrl: &#39;https://images.pexels.com/photos/4065617/pexels-photo-4065617.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500&#39;,
				avatarUrl: &#39;https://p9-passport.byteacctimg.com/img/user-avatar/4dbf31fa6dec9c65b78a70d28d843c04~300x300.image&#39;
			}
		},
		onLoad() {
			let {
				drawCanvasImage,
				drawCircularAvatar,
				drawText
			} = this.$refs.savePoster.$options.methods
			this.$refs.savePoster.canvasInit(({
				context,
				comThis
			}) => {
				// 获取画布宽高
				let canvasWH = comThis.canvasWH
				// 绘制海报背景图
				let promise_1 = drawCanvasImage(context, this.canvasBgUrl, canvasWH.canvasWidth, canvasWH.canvasHeight)
				// 必须先绘制玩海报背景图 再去操作其他绘制内容
				promise_1.then(res => {
					let promise_2 = drawCircularAvatar(context, this.avatarUrl, canvasWH.canvasWidth / 2, canvasWH.canvasHeight /
						7, 70)
					
					let promise_3 = drawText({
						context: context,
						text: &#39;皮皮虾仁&#39;,
						dx: (canvasWH.canvasWidth / 2) + 60,
						dy: canvasWH.canvasHeight / 4,
						fontSize: 30,
						fontColor: &#39;#5D4037&#39;
					})
					
					let promise_4 = drawCanvasImage(context, this.avatarUrl, 150, 150, (canvasWH.canvasWidth / 2) + 85, (canvasWH.canvasHeight -
						165))
					 
					this.$refs.savePoster.startDrawToImage(context, [promise_1,promise_2,promise_4], (tempFilePath) => {
						this.tempFilePath = tempFilePath
					})
				})
			})
		},
		methods: {
			saveBtnFun() {
				uni.showModal({
					title: &#39;保存海报&#39;,
					content: &#39;海报将被保存至相册中&#39;,
					confirmText: &#39;保存&#39;,
					success: (res) => {
						if(res.confirm) {
							this.$refs.savePoster.posterToPhotosAlbum(this.tempFilePath)
						}
					}
				})
			}
		}
	}
</script>

<style>
	.savePosterItem {
		text-align: center;
	}
	.savePosterItem > image {
		width: 620rpx;
		height: 950rpx;
	}
	
	.savePosterBtn {
		margin-top: 40rpx;
		width: 80%;
	}
</style>

Kod sumber komponen
<template>
	<view>
		<canvas :canvas-id="canvasInfo.canvasId" :style="{width: canvasWH.canvasWidth + &#39;px&#39;, height: canvasWH.canvasHeight + &#39;px&#39;}"></canvas>
	</view>
</template>

<script>
	export default {
		name: &#39;savePosterCom&#39;,
		data() {
			return {
				userPhoneWHInfo: {},
				canvasWH: {
					canvasWidth: 0,
					canvasHeight: 0
				}
			}
		},
		props: {
			// 决定保存下来的图片的宽高
			canvasInfo: {
				type: Object,
				default: () => {
					return {
						canvasWidth: 0,
						canvasHeight: 0,
						canvasId: &#39;canvasId&#39;
					}
				}
			},
			// canvas画布是不是全屏,默认是false。 false时使用必须传 canvasInfo
			isFullScreen: Boolean
		},
		created() {
			this.userPhoneWHInfo = this.getPhoneSystemInfo()
			if (this.isFullScreen) { // 画布全屏
				this.canvasWH.canvasWidth = this.userPhoneWHInfo.windowWidth
				this.canvasWH.canvasHeight = this.userPhoneWHInfo.windowHeight
			} else { // 指定宽高
				this.canvasWH.canvasWidth = this.canvasInfo.canvasWidth
				this.canvasWH.canvasHeight = this.canvasInfo.canvasHeight
			}
		},
		mounted() {},
		methods: {
			/**
			* 获取用户手机屏幕信息
			*/
			getPhoneSystemInfo() {
				const res = uni.getSystemInfoSync();
				return {
					windowWidth: res.windowWidth,
					windowHeight: res.windowHeight
				}
			},
			/** 获取 CanvasContext实例
			* @param {String} canvasId 
			*/
			getCanvasContextInit(canvasId) {
				return uni.createCanvasContext(canvasId, this)
			},
			/** 保存海报组件初始化
			* @param {Function} callback(context) 回调函数
			*/
			canvasInit(callback) {
				let context = this.getCanvasContextInit(this.canvasInfo.canvasId)
				if (context) {
					callback({
						context: context,
						comThis: this
					})
				}
			},
			/** 将上诉的绘制画到画布中 并且 将画布导出为图片
			*  @param context 画布
			*  @param {Promise[]} 存放Promise的数组 
			*  @param {Function} callback 保存图片后执行的回调函数(本地图片临时路径)
			*/
			startDrawToImage(context, promiseArr, callback) {
				// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
				let canvasId = this.canvasInfo.canvasId
				let tempFilePath = &#39;&#39;
				Promise.all(promiseArr).then(res => {
					context.draw(false, async () => {
						callback(await this.canvasToImage(canvasId))
					})
				})
			},
			/**
			* 在canvas绘制一张图片
			* @param context 画布
			* @param src 图片资源
			* @param _imageWidth 图片宽度
			* @param _imageHeight 图片高度 
			*/
			drawCanvasImage(context, src, _imageWidth, _imageHeight, dx, dy) {
				return new Promise((resolve, reject) => {
					uni.getImageInfo({
						src: src,
						success: res => {
							context.drawImage(res.path, (dx - _imageWidth), (dy - _imageHeight), _imageWidth, _imageHeight)
							resolve(context)
						},
					})
				})
			},
			/** 绘制一个圆形头像
			* @param  context 画布 
			* @param  url     图片地址
			* @param  _circularX  圆心X坐标
			* @param  _circularY  圆心Y坐标
			* @param  _circularR  圆半径
			*/
			drawCircularAvatar(context, url, _circularX, _circularY, _circularR) {
				let dx = _circularX - _circularR;
				let dy = _circularY - _circularR;
				let dwidth = _circularR * 2;
				let dheight = _circularR * 2
				return new Promise((resolve, reject) => {
					uni.downloadFile({
						url: url,
						success: res => {
							context.save()
							context.beginPath()
							// _circularX圆的x坐标  _circularY圆的y坐标  _circularR圆的半径
							context.arc(_circularX, _circularY, _circularR, 0, 2 * Math.PI)
							context.clip()
							// dx: 图像的左上角在目标canvas上 X 轴的位置
							// dy: 图像的左上角在目标canvas上 Y 轴的位置
							// dwidth: 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放
							// dheight: 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放
							context.drawImage(res.tempFilePath, dx, dy, dwidth, dheight)
							context.restore()
							// context.draw()
							resolve(context)
						}
					})
				})
			},
			/** 绘制多行文本 注:, 和 空格都算一个字
			* @param context 画布
			* @param text 需要被绘制的文本
			* @param dx 左上角x坐标
			* @param dy 右上角y坐标
			* @param rowStrnum 每行多少个字 (默认为text字体个数->单行)
			* @param fontSize 文字大小 (默认16)
			* @param fontColor 文字颜色 (默认black)
			* @param lineHeight 单行文本行高 (默认0)
			*/
			drawText(options) {
				let {
					context,
					text,
					dx,
					dy,
					rowStrnum = text.length,
					lineHeight = 0,
					fontSize = 16,
					fontColor = &#39;black&#39;
				} = options
				return new Promise((resolve, reject) => {
					context.setFontSize(fontSize)
					context.setFillStyle(fontColor)
					context.setTextBaseline(&#39;middle&#39;)
					// 获取需要绘制的文本宽度
					let textWidth = Number(context.measureText(text).width)
					// console.log(&#39;textWidth&#39;,textWidth)
					// 获取文本的字数 
					let textNum = text.length
					// 获取行数 向上取整
					let lineNum = Math.ceil(textNum / rowStrnum)
					// console.log(&#39;textNum&#39;,textNum)
					// console.log(&#39;lineNum&#39;,lineNum)
					for (let i = 0; i < lineNum; i++) {
						let sliceText = text.slice(i * rowStrnum, (i + 1) * rowStrnum)
						// fillText 的 dx = 文字最左边的距离到屏幕政策的距离
						context.fillText(sliceText, dx - textWidth, dy + i * lineHeight);
					}
					resolve(context)
				})
			},
			/** 将画布导出为图片
			* @param canvasId 画布标识
			*/
			canvasToImage(canvasId) {
				return new Promise((resolve, reject) => {
					uni.canvasToTempFilePath({
						canvasId: canvasId, // 画布标识
						success: res => {
							// 在H5平台下,tempFilePath 为 base64
							resolve(res.tempFilePath)
						},
						fail: err => {
							console.log(&#39;err&#39;, err)
							reject(err)
						}
					}, this)
				})
			},
			/** 保存生成的图片到本地相册中
			*  @param {String} filePath 图片临时路劲
			*/
			posterToPhotosAlbum(filePath) {
				console.log(&#39;filePath&#39;,filePath)
				uni.showLoading({
					title: &#39;保存中...&#39;
				})
				uni.saveImageToPhotosAlbum({
					filePath: filePath,
					success: (res) => {
						uni.showToast({
							title: &#39;保存成功,请前往手机相册中查看&#39;,
							mask: true,
							icon: &#39;none&#39;,
							duration: 2000
						})
					},
					fail: (err) => {
						console.log(&#39;err&#39;,err)
						if (err.errMsg.includes(&#39;deny&#39;)||err.errMsg.includes(&#39;denied&#39;)) { // 用户选择拒绝 
							this.openSetting()
						} else if (err.errMsg.includes(&#39;fail cancel&#39;)) { // 用户在保存图片时 取消了
							uni.showToast({
								title: &#39;已取消保存,无法保存至相册&#39;,
								mask: true,
								icon: &#39;none&#39;,
								duration: 2000
							})
							return
						}
					},
					complete: () => {
						uni.hideLoading()
					}
				})
			},
			/**
			* 打开摄像头设置权限页面
			*/
			openSetting() {
				uni.showModal({
					title: &#39;温馨提示&#39;,
					content: &#39;保存图片至相册中,需要您同意添加访问相册权限&#39;,
					cancelText: &#39;拒绝&#39;,
					confirmText: &#39;同意&#39;,
					success: res => {
						if (res.confirm) {
							uni.openSetting({
								success: settingdata => {
									if (settingdata.authSetting[&#39;scope.writePhotosAlbum&#39;]) {
										console.log(&#39;获取权限成功,给出再次点击图片保存到相册的提示。&#39;)
										uni.showToast({
											title: &#39;授权成功,请再次点击保存&#39;,
											icon: &#39;none&#39;,
											duration: 2000,
										})
									} else {
										console.log(&#39;获取权限失败,给出不给权限就无法正常使用的提示&#39;)
										uni.showToast({
											title: &#39;需要访问相册权限&#39;,
											icon: &#39;none&#39;,
											duration: 2000,
										})
									}
								},
								fail: (res) => {
									console.log(&#39;err&#39;, err)
								}
							})
						} else {
							uni.showToast({
								title: &#39;已拒绝授权,无法保存至相册&#39;,
								mask: true,
								icon: &#39;none&#39;,
								duration: 2000
							})
							return
						}
					}
				})
			}
		}
	}
</script>

<style>
</style>

Kesan

Membawa anda langkah demi langkah untuk melaksanakan fungsi menyimpan komponen gambar dalam program mini

Untuk lebih banyak pengetahuan berkaitan pengaturcaraan, sila lawati: Pengenalan kepada Pengaturcaraan

! !

Atas ialah kandungan terperinci Membawa anda langkah demi langkah untuk melaksanakan fungsi menyimpan komponen gambar dalam program mini. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam