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

uniapp 微信小程序 分享海报的实现_第1张图片
uniapp 微信小程序 分享海报的实现_第2张图片
主页面

<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);
			}
		})
	})
}




你可能感兴趣的:(前端,javascript,uniapp,微信小程序海报)