uniapp 微信小程序 分享海报的实现

news2024/12/24 10:14:32

在这里插入图片描述
在这里插入图片描述
主页面

<template>
	<view class="page">
		<!-- 自定义导航栏-->
		<Navbar title="我的海报"></Navbar>
		<view class="container">
			<poster ref="poster" :imageUrl="image" :imageWidth="750" :imageHeight="1000" :drawData="drawData"
				:config="config" :wechatCode="false" @wechatCodeConfig="wechatCodeConfig" @click="createdPoster"
				@loading="onLoading" @success="onSuccess" @fail="onFail" class="poster">
				<!-- <template v-slot:save>
					<view @click="saveToAlbum">保存</view>
				</template> -->
			</poster>
			<u-image width="222rpx" height="222rpx" :src="code" class="container_image"></u-image>
		</view>
		<!-- <view class="bottom"> -->
		<!-- <view class="bottom_item" @click="share('微信好友')">
				<u-image width="74rpx" height="74rpx" 
					src="xxxxxxxxxxxxxxstatic/poster/%E7%BC%96%E7%BB%84%206%402x.png"></u-image>
				<text>微信好友</text>
			</view> -->
		<!-- <view class="bottom_item" @click="share('朋友圈')">
				<u-image width="74rpx" height="74rpx"
					src="xxxxxxxxxxxposter/%E7%BC%96%E7%BB%84%208%402x.png"></u-image>
				<text>朋友圈</text>
			</view> -->
		<!-- <view class="bottom_item" @click="share('保存图片')">
				<u-image width="74rpx" height="74rpx"  
					src="xxxxxxxxxoster/%E7%BC%96%E7%BB%84%2012%402x.png"></u-image>
				<text>保存图片</text>
			</view> -->
		<!-- </view> -->
	</view>
</template>

<script>
	import poster from "@/components/poster/index";
	import {
		saveImageToPhotosAlbum
	} from '@/utils/poster.js';
	export default {
		components: {
			poster
		},
		data() {
			return {
				code: '',
				canvasImages: '',
				image: 'xxxxxxter/static/poster/%E7%BC%96%E7%BB%84%205%402x.png',
				config: {
					imageMode: 'aspectFit',
					posterHeight: '100%',
					// canvasWidth 和 convasHeight使用的是px,防止不同设备Dpr不统一,导致最后图片留白边问题
					canvasWidth: 275,
					convasHeight: 600,
				},
				drawData: [],
				wechatCodeConfig: {
					serverUrl: '',
					scene: '123123',
					config: {
						x: 84.5,
						y: 320,
						w: 100,
						h: 100
					}
				}
			}
		},
		created() {
			this.usercode()
			// 模拟异步请求获得到的数据
			// setTimeout(() => {
			// this.wechatCodeConfig.scene = '456787';
			// this.drawData = [{
			// 		type: 'image',
			// 		config: {
			// 			url: 'https://horifon.oss-cn-shanghai.aliyuncs.com/hongyunartcenter/static/poster/%E7%BC%96%E7%BB%84%205.png',
			// 			x: 0,
			// 			y: 0,
			// 			w: 275,
			// 			h: 490
			// 		},
			// 	},
			// 	{
			// 		type: 'image',
			// 		config: {
			// 			url: this.code,
			// 			x: 40,
			// 			y: 380,
			// 			w: 40,
			// 			h: 40
			// 		},
			// 	},
			// 	{
			// 		type: 'text',
			// 		config: {
			// 			text: '',
			// 			x: 140,
			// 			y: 60,
			// 			color: '#E60012',
			// 			font: 'normal bold 16px 仿宋',
			// 			textAlign: 'center'
			// 		}
			// 	}
			// ];

			// }, 1000)
			// this.createdPoster();
		},
		methods: {
			//二维码
			usercode() {
				let $this = this
				uni.request({
					url: "xxxxxxx/mobile/index.php?m=user&c=indexapi&a=affiliate",
					method: 'POST',
					header: {
						'Content-Type': 'application/x-www-form-urlencoded'
					},
					data: {
						"user_id": parseInt(uni.getStorageSync('USER_ID'))
					},
					success(res) {
						console.log(res.data.data, '数据');
						$this.code = res.data.data
						$this.wechatCodeConfig.serverUrl = res.data.data
					}
				});
			},
			// 保存到相册
			saveToAlbum() {
				this.$refs.poster.saveToAlbum()
			},
			onLoading() {
				console.log('Loading:正在加载静态资源')
			},
			onSuccess(e) {
				console.log('Success:创建海报成功', e)
				this.canvasImages = e
				wx.showShareImageMenu({
					path: this.canvasImages,
					style:"background-color:'black'"
				})
			},
			onFail(e) {
				console.log('Fail:创建海报失败', e)
			},
			createdPoster() {
				console.log('点击')
				// 调用 createImage 开始创建海报
				// this.$refs.poster.createImage()
				this.$refs.poster.cjian()
				// wx.showShareImageMenu({
				// 	path: this.canvasImages
				// })
			},
			share(e) {
				if (e === '微信好友') {
					wx.showShareImageMenu({
						path: this.canvasImages
					})

				} else if (e === '朋友圈') {
					uni.share({
						provider: "weixin",
						scene: "WXSceneTimeline",
						type: 2,
						imageUrl: this.canvasImages,
						success: function(res) {
							console.log("success:" + JSON.stringify(res));
						},
						fail: function(err) {
							console.log("fail:" + JSON.stringify(err));
						}
					});
				} else if (e === '保存图片') {
					saveImageToPhotosAlbum(this.canvasImages).then(res => {
						uni.showToast({
							icon: 'none',
							title: '保存成功'
						})
					}).catch(err => {

					})

				}
			}
		}
	}
</script>

<style scoped lang="scss">
	.container {
		position: relative;
		width: 100%;
		height: 100%;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;

		.poster {
			width: 100%;
			height: 100%;
		}

		.container_image {
			position: absolute;
			bottom: 214rpx;
			left: 76rpx;
		}
	}

	.bottom {
		display: flex;
		justify-content: space-evenly;
		align-items: center;
		position: absolute;
		bottom: 0;
		width: 750rpx;
		height: 218rpx;
		background: #FFFFFF;
		border-radius: 40rpx 40rpx 0rpx 0rpx;
		// border: 2rpx solid #FF583D;
	}
</style>

components/poster/index 组件

<template>
	<view class="poster_wrapper">
		<slot name="header"></slot>
		<!-- 要生成海报的图片 -->
		<image :src="imageUrl" mode="aspectFill" :style="{width:imageWidth + 'rpx'}"  @click="click" class="imagebig"></image>
		<!-- 这里把canvas移到了屏幕外面,如果需要查看canvas的话把定位去掉 -->
		<!-- position:'fixed',left:'9999px',top:'0' -->
		<canvas :style="{width:canvasWidth + 'px',height:canvasHeight + 'px',position:'fixed',left:'9999px',top:'0'}"
		 canvas-id="myCanvas" id="myCanvas" class="canvas"></canvas>
		<!-- 遮罩层 -->
		<!-- <view class="mask" v-if="showMask" @click="hideMask"> -->
			<!-- 生成的海报图 -->
			<!-- <image :style="posterSize" :src="lastPoster" :mode="config.imageMode" @click.stop=""></image> -->
			<!-- <view class="btn_wrapper" @click.stop>
				<slot name="save">
					<button type="primary" @click="saveToAlbum">保存至相册</button>
				</slot>
			</view> -->
		<!-- </view> -->
	</view>
</template>

<script>
	import {
		loadImage,
		createPoster,
		canvasToTempFilePath,
		saveImageToPhotosAlbum
	} from '@/utils/poster.js';
	import {
		getWechatCode
	} from "@/utils/appletCode.js";
	export default {
		props: {
			// 展示图片的宽 单位 rpx
			imageWidth: {
				type: [String, Number],
				default: 550
			},
			// 展示图片的高 单位 rpx
			imageHeight: {
				type: [String, Number],
				default:980
			},
			// 展示图片的url
			imageUrl: {
				type: String,
				default: '',
				required: true
			},
			// 绘制海报的数据参数
			// drawData: {
			// 	type: Array,
			// 	default: () => ([]),
			// 	required: true
			// },
			// 海报的配置参数
			config: {
				type: Object,
				default: () => ({
					imageMode: 'aspectFit',
					posterHeight:1000,
				}),
			},
			// 是否需要小程序二维码
			wechatCode: {
				type: Boolean,
				default: false
			},
			// 小程序二维码的配置参数
			wechatCodeConfig: {
				type: Object,
				default: () => ({
					serverUrl: '',
					scene: '',
					config: {
						x: 0,
						y: 0,
						w: 100,
						h: 100
					}
				}),
			}
		},
		data() {
			return {
				// 资源是否加载成功的标志
				readyed: false,
				// 将网络图片转成静态图片后的绘制参数
				imageMap: [],
				// 最后生成的海报的本地缓存地址
				lastPoster: '',
				// 是否展示遮罩
				showMask: false,
				// 是否加载资源的标志
				loadingShow: false,
				// 是否可以创建海报
				disableCreatePoster:false,
				drawData :[
					{
						type: 'image',
						config: {
							url: '',
							x: 0,
							y: 0,
							w: 275,
							h: 490
						},
					},
					{
						type: 'image',
						config: {
							url: '',
							x: 40,
							y: 380,
							w: 40,
							h: 40
						},
					},
					{
						type: 'text',
						config: {
							text: '',
							x: 140,
							y: 60,
							color: '#E60012',
							font: 'normal bold 16px 仿宋',
							textAlign: 'center'
						}
					}
				],
				
			}
		},
		computed: {
			// 所生成海报图的大小
			posterSize() {
				let str = '';
				this.config.posterWidth && (str += `width:${this.config.posterWidth};`);
				this.config.posterHeight && (str += `height:${this.config.posterHeight};`);
				return str
			},
			// 画布的宽,优先使用配置的宽,如果没用配置默认使用图片的宽
			// 需要主要的是这里canvas和image的单位不同,不过并不影响
			// 我们在绘制时(配置drawData)以px为基准进行绘制就行,用px的原因时防止不同设备Dpr不同导致无法确定画布的具体宽高,使得最后的图片可能会留白边
			canvasWidth(){
				return this.config.canvasWidth ? this.config.canvasWidth : this.imageWidth
			},
			// 画布的高,优先使用配置的高,如果没用配置默认使用图片的高
			canvasHeight(){
				return this.config.convasHeight ? this.config.convasHeight : this.imageHeight
			}
		},
		watch: {
			// 监听外部绘制参数的变化,重新加载资源
			// drawData(newVlaue) {
			// 	this.loadingResources(newVlaue)
			// },
			// 监听readyed变化
			// readyed(newVlaue) {
				
			// 	if (newVlaue == true && this.loadingShow == true) {
			// 		uni.hideLoading()
			// 		this.loadingShow = false;
					
			// 		this.createImage();
			// 	}
			// }
			// 会存在异步问题,还没解决。
			// 目前的解决方法 1.在绘制之前先改变 scene 2.改变scene后手动调用this.loadingResources 函数,但是资源会重新加载
			// "wechatCodeConfig.scene":function (newVlaue){
			// 	console.log('wechatCodeConfig.scene',this.imageMap)
			// 	this.loadingWechatCode(this.imageMap)
			// }
		},
		created() {
			this.usercode()
			// this.$emit('click')
			// this.loadingResources(this.drawData)
		},
		methods: {
			cjian(){
				this.usercode()
			},
			//二维码
			usercode() {
				let $this=this
				uni.request({
					url: "xxxxxxx?m=user&c=indexapi&a=affiliate",
					method: 'POST',
					header: {
						'Content-Type': 'application/x-www-form-urlencoded'
					},
					data: {
						"user_id": parseInt(uni.getStorageSync('USER_ID'))
					},
					success(res) {
						console.log(res.data.data,'数据');
						$this.drawData=[{
								type: 'image',
								config: {
									url: 'xxxxxxx/static/poster/%E7%BC%96%E7%BB%84%205%403x.png',
									x: 0,
									y: 0,
									w: 275,
									h: 490
								},
							},
							{
								type: 'image',
								config: {
									url: res.data.data,
									x: 35,
									y: 350,
									w: 70,
									h:70
								},
							},
							{
								type: 'text',
								config: {
									text: '',
									x: 140,
									y: 60,
									color: '#E60012',
									font: 'normal bold 16px 仿宋',
									textAlign: 'center'
								}
							}
						]
						// $this.wechatCodeConfig.serverUrl=res.data.data
						$this.loadingResources($this.drawData)
					}
				});
			},
			// 加载静态资源,创建或更新组件内所下载本地图片集合
			async loadingResources(drawData) {
				// this.readyed = false;
				if (!drawData.length || drawData.length <= 0) return;
				// 加载静态图片,将所以图片的网络地址替换成本地缓存地址
				const tempMap = [];
				for (let i = 0; i < drawData.length; i++) {
					let temp
					if (drawData[i].type === "image") {
						temp = await loadImage(drawData[i].config.url);
						drawData[i].config.url = temp;
					}
					tempMap.push({ 
						...drawData[i],
						url: temp
					})
				}
				// 加载小程序二维码
				await this.loadingWechatCode(tempMap);
				// 赋值给imageMap保存
				this.imageMap = tempMap;
				setTimeout(() => {
					// this.readyed = true;
					this.createImage()
				}, 100)
			},
			// 绘制海报图
			async createImage() {
				// console.log('点击执行',this.imageMap)
				// 禁用生成海报,直接返回
				// if(this.disableCreatePoster) return
				// this.disableCreatePoster = true;
				try {
					// if (!this.readyed) {
					// 	uni.showLoading({
					// 		title: '静态资源加载中...'
					// 	});
					// 	this.loadingShow = true;
					// 	this.$emit('loading')
					// 	return
					// }
					// 获取上下文对象,组件内一定要传this
					const ctx = uni.createCanvasContext('myCanvas', this);
					await createPoster(ctx, this.imageMap);
					this.lastPoster = await canvasToTempFilePath('myCanvas', this);
					this.showMask = true;
					console.log('点击执行',this.imageMap,this.lastPoster)
					// this.disableCreatePoster = false;
					// 创建成功函数
					this.$emit('success',this.lastPoster)
				} catch (e) {
					// 创建失败函数
					this.disableCreatePoster = false;
					this.$emit('fail', e)
				}
			},
			// 加载或更新小程序二维码
			async loadingWechatCode(tempMap) {
				if (this.wechatCode) {
					if (this.wechatCodeConfig.serverUrl) {
						const code = await getWechatCode(this.wechatCodeConfig.serverUrl, this.wechatCodeConfig.scene || '');
						// 记录替换的索引,没有就替换length位,即最后加一个
						let targetIndex = tempMap.length;
						for (let i = 0; i < tempMap.length; i++) {
							if (tempMap[i].wechatCode) targetIndex = i;
						}
						tempMap.splice(targetIndex, 1, {
							type: 'image',
							url: code.path,
							// 标记是小程序二维码
							wechatCode: true,
							config: this.wechatCodeConfig.config,
						})
					} else {
						throw new Error('serverUrl请求二维码服务器地址不能为空')
					}
				}
				return tempMap
			},
			// 保存到相册
			saveToAlbum() {
				saveImageToPhotosAlbum(this.lastPoster).then(res => {
					this.showMask = false;
					uni.showToast({
						icon: 'none',
						title: '保存成功'
					})
				}).catch(err => {
					
				})
			},
			click() {
				this.$emit('click')
				this.showMask = false;
				this.$emit('hidemask')
			},
			hideMask(){
				this.showMask = false;
				this.$emit('hidemask')
			}
		},
	}
</script>

<style scoped>
	.poster_wrapper {
		width: 100%;
		height:100vh;
		display: flex;
		flex-direction: column;
		align-items: center;
	}
	.imagebig{
		height:100%;
	}

	.canvas {
		border: 1px solid #333333;
	}

	.mask {
		width: 100vw;
		height: 100vh;
		position: fixed;
		background-color: rgba(0,0,0,.4);
		left: 0;
		top: 0;
		display: flex;
		flex-direction: column;
		justify-content: space-around;
		align-items: center;
	}
</style>

@/utils/poster.js

// 错误提示集合
const errMsgMap = {
	'arc': {
		'x': '请指定圆的起始位置 x',
		'y': '请指定圆的起始位置 y',
		'r': '请指定圆的半径 r',
		'sAngle': '请指定圆的起始弧度 sAngle',
		'eAngle': '请指定圆的终止弧度 eAngle',
	},
	'rect': {
		'x': '请指定矩形的起始位置 x',
		'y': '请指定矩形的起始位置 y',
		'w': '请指定矩形的宽度 w',
		'h': '请指定矩形的高度 h',
	},
	'round_rect': {
		'x': '请指定矩形边框的起始位置 x',
		'y': '请指定矩形边框的起始位置 y',
		'w': '请指定矩形边框的宽度 w',
		'h': '请指定矩形边框的高度 h',
	},
	'stroke_rect': {
		'x': '请指定矩形边框的起始位置 x',
		'y': '请指定矩形边框的起始位置 y',
		'w': '请指定矩形边框的宽度 w',
		'h': '请指定矩形边框的高度 h',
	},
	'stroke_round_rect': {
		'x': '请指定矩形边框的起始位置 x',
		'y': '请指定矩形边框的起始位置 y',
		'w': '请指定矩形边框的宽度 w',
		'h': '请指定矩形边框的高度 h',
	},
	'text': {
		'x': '请指定文本的起始位置 x',
		'y': '请指定文本的起始位置 y',
		'text': '请指定文本的内容 text'
	},
	'image': {
		'x': '请指定图片的起始位置 x',
		'y': '请指定图片的起始位置 y',
		'w': '请指定图片的宽度 w',
		'h': '请指定图片的高度 h',
		'url': '请指定图片的路径 url'
	},
	'line': {
		'path': '请指定线的路径 path'
	},
	'points': {
		'points': '请指定点集合 points'
	}
};
// 绘制方法集合
const DrawFuncMap = {
	drawLine(ctx, config, i, isClip) {
		// clip 图形默认不需要 fill 和 stroke 颜色。
		const defaultColor = isClip ? 'transparent' : '#333333'
		// 检验必传参数
		checkNecessaryParam(config, 'line', i, 'path');
		// 每一个path就描述了一组线的开始到结束,这一组线段不一定是连续的,根据配置属性来具体描述这个线
		// 他们的形态是一样的(线的粗细,颜色),形状不一样且不一定是连续的
		for (let j = 0; j < config.path.length; j++) {
			const path = config.path[j];
			checkNecessaryParam(path, 'points', `${i}-${j}`, 'points');
			const lineWidth = path.lineWidth || 1;
			const lineJoin = path.lineJoin || 'round';
			const lineCap = path.lineCap || 'round';
			// ctx.beginPath();
			// 设置颜色
			ctx.setStrokeStyle(path.strokeStyle || defaultColor);
			// 设置填充色
			ctx.setFillStyle(path.fillStyle || defaultColor);
			// 设置粗细
			ctx.setLineWidth(lineWidth);
			// 设置线条交点样式
			ctx.setLineJoin(lineJoin);
			// 设置线条的断点样式
			ctx.setLineCap(lineCap);
			// 遍历线的点集合,根据每个点的不同属性来绘制成线
			for (let k = 0; k < path.points.length; k++) {
				// 拿到每一个点
				const pointSet = path.points[k];
				// 如果该点是一个数组集合,则点的类型直接当 lineTo 处理
				if (Object.prototype.toString.call(pointSet) === "[object Array]") {
					if (k === 0) ctx.moveTo(...pointSet);
					else ctx.lineTo(...pointSet);
				} else {
					// 默认的第一个点一定是起始点,且点类型为 moveTo 则执行 ctx.moveTo 移动画笔
					if (k === 0 || pointSet.type === 'moveTo') {
						ctx.moveTo(...pointSet.point);
						// 点的类型为 lineTo 或 没有 type 属性也默认为 lineTo 至执行 ctx.lineTo 连线
					} else if (pointSet.type === 'lineTo' || pointSet.type === undefined) {
						ctx.lineTo(...pointSet.point);
					} else if (pointSet.type === 'bezierCurveTo') {
						const P2 = pointSet.P2 ? pointSet.P2 : pointSet.P1;
						ctx.bezierCurveTo(...pointSet.P1, ...P2, ...pointSet.point);
					} else if (pointSet.type === 'closePath') {
						ctx.closePath()
						ctx.fill();
					}
				}
			}
			// 每一组点集合(path)结束 stroke
			ctx.stroke();
		}
	},
	// 绘制图片
	drawImage(ctx, config, i) {
		checkNecessaryParam(config, 'image', i, 'x', 'y', 'w', 'h', 'url');
		ctx.drawImage(config.url, config.x, config.y, config.w, config.h);
	},
	// 绘制圆
	drawArc(ctx, config, i, isClip) {
		const defaultColor = isClip ? 'transparent' : '#333333'
		checkNecessaryParam(config, 'arc', i, 'x', 'y', 'r', 'sAngle', 'eAngle');
		// ctx.beginPath();
		ctx.arc(config.x, config.y, config.r, config.sAngle, config.eAngle);
		ctx.setFillStyle(config.fillStyle || defaultColor);
		ctx.fill();
		ctx.setLineWidth(config.lineWidth || 1);
		ctx.setStrokeStyle(config.strokeStyle || defaultColor);
		ctx.stroke();
	},
	// 绘制文字
	drawText(ctx, config, i) {
		checkNecessaryParam(config, 'text', i, 'x', 'y', 'text');
		ctx.font = config.font || '10px sans-serif';
		ctx.setFillStyle(config.color || '#333333');
		ctx.setTextAlign(config.textAlign || 'center');
		ctx.fillText(config.text, config.x, config.y);
		ctx.stroke();
	},
	// 绘制矩形
	drawRect(ctx, config, i, isClip) {
		const defaultColor = isClip ? 'transparent' : '#333333'
		checkNecessaryParam(config, 'rect', i, 'x', 'y', 'w', 'h');
		// ctx.beginPath();
		ctx.rect(config.x, config.y, config.w, config.h);
		ctx.setFillStyle(config.fillStyle || defaultColor);
		ctx.setLineWidth(config.lineWidth || 1);
		ctx.setStrokeStyle(config.strokeStyle || defaultColor);
		ctx.stroke();
		ctx.fill();
	},
	// 绘制非填充矩形
	drawStrokeRect(ctx, config, i, isClip) {
		checkNecessaryParam(config, 'stroke_rect', i, 'x', 'y', 'w', 'h');
		// ctx.beginPath();
		ctx.setStrokeStyle(config.strokeStyle || '#333333');
		ctx.setLineWidth(config.lineWidth || 1);
		ctx.strokeRect(config.x, config.y, config.w, config.h);
		ctx.stroke();
	},
	// 绘制填充圆角矩形
	drawRoundRect(ctx, config, i, isClip) {
		// 当为裁剪图形,需要把 fill 和 stroke 颜色设置为透明
		const defaultColor = isClip ? 'transparent' : '#333333'
		checkNecessaryParam(config, 'stroke_rect', i, 'x', 'y', 'w', 'h');
		ctx.setFillStyle(config.fillStyle || defaultColor);
		this.drawRoundRectPath(ctx, config.x, config.y, config.w, config.h, config.r)
		ctx.fill();
	},
	// 绘制非填充圆角矩形
	drawStrokeRoundRect(ctx, config, i, isClip) {
		checkNecessaryParam(config, 'stroke_rect', i, 'x', 'y', 'w', 'h');
		ctx.setStrokeStyle(config.strokeStyle || '#333333');
		ctx.setLineWidth(config.lineWidth || 1);
		this.drawRoundRectPath(ctx, config.x, config.y, config.w, config.h, config.r)
		ctx.stroke();
	},
	// 绘制圆角矩形路径
	drawRoundRectPath(ctx, x, y, w, h, r) {
		// ctx.beginPath();
		// ctx.strokeRect(config.x, config.y, config.w, config.h);
		//从右下角顺时针绘制,弧度从0到1/2PI  
		ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2);
		//矩形下边线  
		ctx.lineTo(x + r, y + h);
		//左下角圆弧,弧度从1/2PI到PI  
		ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI);
		//矩形左边线  
		ctx.lineTo(x, y + r);
		//左上角圆弧,弧度从PI到3/2PI  
		ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
		//上边线  
		ctx.lineTo(x + w - r, y);
		//右上角圆弧  
		ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, Math.PI * 2);
		//右边线  
		ctx.lineTo(x + w, y + h - r);
		ctx.closePath();
	}
}

/**
 * 检测绘制的必要属性
 * @param {Object} configObj 配置对象
 * @param {String} type 对应校验的类型
 * @param {String|Number} index 当前的错误位置 从0开始对应绘画(drawData)配置中的索引,
 * 当为 String 类型时会以'-'间隔出第几层的第几个,如1-2 表示是绘画(drawData)配置中第一个配置里的第二个子配置对象有问题,依次类推
 * @param {Array} keyArr 搜集到的所以需要进行检验的键名
 **/
function checkNecessaryParam(configObj, type, index, ...keyArr) {
	// 这里要注意由于,绘画配置有些参数可能会漏写,所以 errMsgMap[type] 作为遍历对象进行比较
	for (let prop in errMsgMap[type]) {
		if (configObj[prop] === undefined) {
			throw new Error(`${index}顺位:${errMsgMap[type][prop]}`)
		}
	}
}
/**
 * 根据不同的图形类型绘制图形
 * @param {String} type 图形的类型
 * @param {Object} ctx 当前 canvas 的上下文对象
 * @param {Object} config 图形对应的配置属性对象,
 * @param {String|Number} index 从0开始对应绘画(drawData)配置中的索引
 * @param {Boolean} isClip 是否应用于剪切
 **/
function drawFigureByType(type, ctx, config, index, isClip) {
	if (type === 'image') {
		!isClip && DrawFuncMap.drawImage(ctx, config, index);
	} else if (type === 'text') {
		!isClip && DrawFuncMap.drawText(ctx, config, index);
	} else if (type === 'arc') {
		DrawFuncMap.drawArc(ctx, config, index, isClip);
	} else if (type === 'rect') {
		DrawFuncMap.drawRect(ctx, config, index, isClip);
	} else if (type === 'stroke_rect') {
		// 这里非填充矩形也按照矩形的方式绘制裁剪区域
		isClip ? DrawFuncMap.drawRect(ctx, config, index, isClip) : DrawFuncMap.drawStrokeRect(ctx, config, index,
			isClip);
	} else if (type === 'stroke_round_rect') {
		// 这里非填充圆角矩形也按照圆角矩形的方式绘制裁剪区域
		isClip ? DrawFuncMap.drawRoundRect(ctx, config, index, isClip) : DrawFuncMap.drawStrokeRoundRect(ctx, config,
			index, isClip);
	} else if (type === 'round_rect') {
		DrawFuncMap.drawRoundRect(ctx, config, index, isClip);
	} else if (type === 'line') {
		DrawFuncMap.drawLine(ctx, config, index, isClip)
	}
}
/**
 * 不同类型的图形都可以绘制不同的剪切图形
 * @param {String} type 绘制图形的类型
 * @param {Object} ctx 当前 canvas 的上下文对象
 * @param {Object} config 绘制图形对应的配置属性对象,
 * @param {String|Number} index 绘制图形的索引 从0开始对应绘画(drawData)配置中的索引
 * @param {String} clipType 剪切图形的类型
 * @param {Object} clipConfig 剪切图形对应的配置属性对象,
 **/
function drawClipFigure(type, ctx, config, index, clipType, clipConfig) {
	ctx.beginPath();
	drawFigureByType(clipType, ctx, clipConfig, index, true)
	ctx.clip()
	ctx.beginPath();
	drawFigureByType(type, ctx, config, index, false)
}

// 获取图片信息,这里主要要获取图片缓存地址
export function loadImage(url) {
	return new Promise((resolve, reject) => {
		wx.getImageInfo({
			src: url,
			success(res) {
				resolve(res.path)
			},
			fail(err) {
				reject('海报图资源加载失败')
			}
		})
	})
}
// 解析海报对象,绘制canvas海报
export function createPoster(ctx, posterItemList) {
	return new Promise((resolve, reject) => {
		try {
			for (let i = 0; i < posterItemList.length; i++) {
				const temp = posterItemList[i];
				// 如果有 clip 属性需要先建立 clip 剪切区域
				if (temp.clip) {
					ctx.save()
					// 绘制剪切图形
					drawClipFigure(temp.type, ctx, temp.config, i, temp.clip.type, temp.clip.config)
					ctx.restore()
				} else {
					ctx.beginPath();
					//正常绘制图形
					drawFigureByType(temp.type, ctx, temp.config, i, false)
				}

			}
			ctx.draw();
			resolve({
				result: 'ok',
				msg: '绘制成功'
			})
		} catch (e) {
			console.error(e)
			reject({
				result: 'fail',
				msg: e
			})
		}
	})
}
// canvas转image图片
export function canvasToTempFilePath(canvasId, vm, delay = 50) {
	return new Promise((resolve, reject) => {
		// 这里canvas绘制完成之后想要存缓存需要一定时间,这里设置了50毫秒
		setTimeout(() => {
			uni.canvasToTempFilePath({
				canvasId: canvasId,
				x:0,
				y:0,
				width: 300,
				height: 490,
				destWidth: 300,
				destHeight:490,
				success(res) {
					if (res.errMsg && res.errMsg.indexOf('ok') != -1) resolve(res.tempFilePath);
					else reject(res)
				},
				fail(err) {
					reject(err)
				}
			}, vm);
		}, delay)
	})
}
// 保存图片到相册
export function saveImageToPhotosAlbum(imagePath) {
	return new Promise((resolve, reject) => {
		uni.saveImageToPhotosAlbum({
			filePath: imagePath,
			success(res) {
				resolve(res)
			},
			fail(err) {
				reject(err)
			}
		})
	})
}

@/utils/appletCode.js

import {base64src} from "@/utils/base64src.js";
/**
 * 微信获取小程序二维码
 * @param {String} url 微信服务器地址
 * @param {String} scene 二维码所携带的信息
 * @return {Object} 返回的二维码对象
 **/
export function getWechatCode (url,scene) {
	return new Promise((resolve,reject)=>{
		wx.request({
			url: url,
			method: 'POST',
			header: {
				'content-type': 'application/x-www-form-urlencoded'
			},
			// 二维码携带的信息
			data: {
				scene: scene
			},
			success(res) {
				//将base64图片转换成本地路径
				base64src("data:image/PNG;base64," + res.data.qcode, res => { 
					// 获取图片信息
					wx.getImageInfo({
						src: res,
						success(res) {
							resolve(res);
						},
						fail(err) {
							reject(err);
						}
					})
				})
			},
			fail(err){
				reject(err);
			}
		})
	})
}




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1627184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MHA】MySQL高可用MHA源码1-主库故障监控

1 阅读之前的准备工作 1 一个IDE工具 &#xff0c;博主自己尝试了vscode安装perl的插件&#xff0c;但是函数 、变量 、模块等都不能跳转&#xff0c;阅读起来不是很方便。后来尝试使用了pycharm安装perl插件&#xff0c;阅读支持跳转&#xff0c;自己也能写一些简单的测试样例…

达梦(DM) SQL日期操作及分析函数

达梦DM SQL日期操作及分析函数 日期操作SYSDATEEXTRACT判断一年是否为闰年周的计算确定某月内第一个和最后一个周末某天的日期确定指定年份季度的开始日期和结束日期补充范围内丢失的值按照给定的时间单位查找使用日期的特殊部分比较记录 范围处理分析函数定位连续值的范围查找…

【定制化体验:使用Spring Boot自动配置,打造个性化Starter】

项目结构 Pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4…

LabVIEW高效目标跟踪系统

LabVIEW高效目标跟踪系统 随着机器视觉技术的飞速发展&#xff0c;设计和实现高效的目标跟踪系统成为了众多领域关注的焦点。基于LabVIEW平台&#xff0c;结合NI Vision机器视觉库&#xff0c;开发了一种既高效又灵活的目标跟踪系统。通过面向对象编程方法和队列消息处理器程序…

【CTF Crypto】CTFShow 萌新 密码3 Writeup(摩尔斯电码+培根密码)

萌新 密码3 3 题目名称&#xff1a;我想吃培根 题目描述&#xff1a; – — .-. … . …–.- … … …–.- -.-. — — .-… …–.- -… …- - …–.- -… .- -.-. — -. …–.- … … …–.- -.-. — — .-… . .-. …–.- – – -… -… – -… – -… – – – -… -… -……

鸿蒙(HarmonyOS)性能优化实战-Trace使用教程

概述 OpenHarmony的DFX子系统提供了为应用框架以及系统底座核心模块的性能打点能力&#xff0c;每一处打点即是一个Trace&#xff0c;其上附带了记录执行时间、运行时格式化数据、进程或线程信息等。开发者可以使用SmartPerf-Host调试工具对Trace进行解析&#xff0c;在其绘制…

yudao-cloud微服务系统系统模块+后台管理系统成功运行

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列文章目录 第一章 芋…

精酿啤酒:酿造工艺的自动化与智能化发展

随着科技的不断进步&#xff0c;自动化与智能化已成为啤酒酿造工艺的重要发展方向。Fendi Club啤酒紧跟时代潮流&#xff0c;积极推动酿造工艺的自动化与智能化发展&#xff0c;旨在提高生产效率、确保产品品质和满足市场需求。 Fendi Club啤酒引入自动化生产设备。他们采用自动…

rabbitmq集群配置

1&#xff0c;配置环境变量 MY_POD_NAME&#xff1a;当前Pod的名称 RABBITMQ_ERLANG_COOKIE&#xff1a;设置Erlang Cookie用于节点间通信安全验证&#xff0c;值来自/nfs/rabbitmq/lib/.erlang.cookie文件内容 RABBITMQ_NODENAME&#xff1a;根据Pod名称动态生成了RabbitMQ…

Typora for Mac:轻量级Markdown编辑器

Typora for Mac是一款专为Mac用户设计的轻量级Markdown编辑器&#xff0c;它以其简洁的界面和强大的功能&#xff0c;成为了Markdown写作爱好者的首选工具。 Typora for Mac v1.8.10中文激活版下载 Typora的最大特色在于其所见即所得的编辑模式&#xff0c;用户无需关心复杂的M…

【软件】ERETCAD-Env:在轨空间环境3D动态仿真软件

文章介绍了Extreme-environment Radiation Effect Technology Computer-Aided Design – Environment (ERETCAD-Env)软件&#xff0c;文章的介绍和展示了ERETCAD-Env软件的功能和特点&#xff0c;这是一款用于动态模拟在轨卫星所处空间环境的计算机辅助设计软件。强调了该软件在…

TCP关闭连接时的一些思考

TCP协议是TCP/IP栈中最复杂的协议&#xff0c;它最大的优点是传输的可靠性&#xff0c;这通过面向连接、按序传输、超时重传、流量控制等机制保证其传输的可靠性。但这并不是我们今天要讨论的重点&#xff01; TCP通信的过程分别是三个阶段&#xff1a;建立连接、传输数据、关…

SpringCloud Hystrix 实战

一、配置 1.引入jar包 单独使用hystrix &#xff0c;不配合openFegin使用的话&#xff0c;单独使用hystrix,需要引入spring-cloud-starter-netflix-hystrix包。要使用的hystrix-dashboard 界面的话需要引入spring-boot-starter-actuator 包和spring-cloud-starter-netflix-hy…

SpringBoot中多数据源灵活切换解决方案

本篇内容介绍了“SpringBoot中如何使用Dynamic Datasource配置多数据源”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 源码地址/文档说明 功能特性: 支持 数据源分组…

通信原理(2)--随机过程

通信原理(2)–随机过程 3.1随机过程的基本概念 随机过程{x(t)}由一族时间函数 x i ( t ) x_i(t) xi​(t)&#xff0c;i1,2.3…组成&#xff0c;每一个时间函数 x i ( t ) x_i(t) xi​(t)称为随机过程{x(t)}的一个样本函数&#xff08;一个实现&#xff09; 每个样本函数在时间…

python-opencv实现最近邻插值和双线性插值对图片上采样

使用背景 当我们需要把图像进行放大或者缩小的时候&#xff0c;第一反应是使用resize()实现。很多情况下&#xff0c;我们会调用最近邻插值和双线性插值去放大图片&#xff0c;当然要说没有分辨率的损失那是不可能的&#xff0c;只能说在放大图片的过程中尽可能增加了图片的分…

Hybrid Homomorphic Encryption:SE + HE

参考文献&#xff1a; [NLV11] Naehrig M, Lauter K, Vaikuntanathan V. Can homomorphic encryption be practical?[C]//Proceedings of the 3rd ACM workshop on Cloud computing security workshop. 2011: 113-124.[MJS16] Maux P, Journault A, Standaert F X, et al. To…

Matlab|交直流系统潮流计算(含5种控制模式)

目录 1 主要内容 程序参考流程图 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《交直流系统潮流计算及相互关联特性分析》&#xff0c;采用5种交直流潮流控制方式&#xff1a;1.定电流定电压 2.定电流定熄弧角 3.定功率定电压 4.定功率定熄弧角 5.定触发角…

Stable Diffusion 常用放大算法详解

常用放大算法 图像放大算法大致有两种&#xff1a; 传统图像放大算法&#xff08;Lantent、Lanczos、Nearest&#xff09;AI图像放大算法&#xff08;4x-UltraSharp、BSRGAN、ESRGAN等&#xff09; 传统图像放大算法是基于插值算法&#xff0c;计算出图像放大后新位置的像素…

用友政务财务系统 FileDownload 任意文件读取漏洞复现

0x01 产品简介 用友政务财务系统具有多项核心功能,旨在满足各类组织的财务管理需求。首先,它提供了财务核算功能,能够全面管理企业的总账、固定资产、现金、应付应收等模块,实时掌握企业的财务状况,并通过科目管理、凭证处理、报表分析等功能为决策提供有力支持。 0x02 …