529 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			529 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | ||
| 	<view class="canvas">
 | ||
| 		<view ref="Content" class="theContent">
 | ||
| 			<slot></slot>
 | ||
| 		</view>
 | ||
| 		<canvas canvas-id="myCanvas" id="myCanvas" @onReady="onCanvasReady"
 | ||
| 			:style="{ width: width + 'px', height: height + 'px' }"></canvas>
 | ||
| 	</view>
 | ||
| </template>
 | ||
| <!-- 
 | ||
| list参数说明:
 | ||
|  图片渲染:
 | ||
|  type: 'image',
 | ||
|  x: X轴位置,
 | ||
|  y: Y轴位置,
 | ||
|  path: 图片路径,
 | ||
|  width: 图片宽度,
 | ||
|  height: 图片高度,
 | ||
|  rotate: 旋转角度
 | ||
|  mode:'center' 居中自适应
 | ||
|  shape: 形状,默认无,可选值:circle 圆形 如果是number,则生成圆角矩形
 | ||
|  area: {x,y,width,height}  // 绘制范围,超出该范围会被剪裁掉 该属性与shape暂时无法同时使用,area存在时,shape失效
 | ||
| 矩形渲染:
 | ||
|  type: 'square',
 | ||
|  x: X轴位置,
 | ||
|  y: Y轴位置,
 | ||
|  width: 图片宽度,
 | ||
|  height: 图片高度,
 | ||
|  rotate: 旋转角度
 | ||
|  shape: 形状,默认无,可选值:circle 圆形 如果是number,则生成圆角矩形
 | ||
|  fillStyle:填充的背景样式
 | ||
|  area: {x,y,width,height}  // 绘制范围,超出该范围会被剪裁掉 该属性与shape暂时无法同时使用,area存在时,shape失效
 | ||
|  文字渲染:
 | ||
|  type: 'text',
 | ||
|  x: X轴位置,
 | ||
|  y: Y轴位置,
 | ||
|  text: 文本内容,
 | ||
|  size: 字体大小,
 | ||
|  textBaseline: 基线 默认top  可选值:'top'、'bottom'、'middle'、'normal'
 | ||
|  color: 颜色
 | ||
|  多行文字渲染:
 | ||
|  type: 'textarea',
 | ||
|  x: X轴位置,
 | ||
|  y: Y轴位置,
 | ||
|  width:换行的宽度
 | ||
|  height: 高度,溢出会展示“...”
 | ||
|  lineSpace: 行间距
 | ||
|  text: 文本内容,
 | ||
|  size: 字体大小,
 | ||
|  textBaseline: 基线 默认top  可选值:'top'、'bottom'、'middle'、'normal'
 | ||
|  color: 颜色
 | ||
|  -->
 | ||
| <script>
 | ||
| 	let timer = null;
 | ||
| 	export default {
 | ||
| 		name: "Poster",
 | ||
| 		props: {
 | ||
| 			// 绘制队列
 | ||
| 			list: {
 | ||
| 				type: Array,
 | ||
| 				required: true,
 | ||
| 			},
 | ||
| 			// 海报宽度(默认设备宽度放大两倍) 建议都放大两倍
 | ||
| 			width: {
 | ||
| 				type: [Number, String],
 | ||
| 				default: uni.getSystemInfoSync().windowWidth * 2,
 | ||
| 			},
 | ||
| 			// 海报高度(默认设备高度放大两倍)
 | ||
| 			height: {
 | ||
| 				type: [Number, String],
 | ||
| 				default: uni.getSystemInfoSync().windowHeight * 2,
 | ||
| 			},
 | ||
| 			//背景颜色
 | ||
| 			backgroundColor: {
 | ||
| 				type: String,
 | ||
| 				default: "rgba(0,0,0,0)",
 | ||
| 			},
 | ||
| 		},
 | ||
| 		data() {
 | ||
| 			return {
 | ||
| 				posterUrl: "",
 | ||
| 				renderList: [],
 | ||
| 				ctx: null, //画布上下文
 | ||
| 				counter: -1, //计数器
 | ||
| 				drawPathQueue: [], //画图路径队列
 | ||
| 			};
 | ||
| 		},
 | ||
| 		watch: {
 | ||
| 			drawPathQueue(newVal, oldVal) {
 | ||
| 				// 绘制单行文字
 | ||
| 				const fillText = (textOptions) => {
 | ||
| 					this.ctx.setFillStyle(textOptions.color);
 | ||
| 					this.ctx.setFontSize(textOptions.size);
 | ||
| 					this.ctx.setTextBaseline(textOptions.textBaseline || "top");
 | ||
| 					this.ctx.fillText(textOptions.text, textOptions.x, textOptions.y);
 | ||
| 				};
 | ||
| 				// 绘制段落
 | ||
| 				const fillParagraph = (textOptions) => {
 | ||
| 					this.ctx.setFontSize(textOptions.size);
 | ||
| 					let tempOptions = JSON.parse(JSON.stringify(textOptions));
 | ||
| 					// 如果没有指定行间距则设置默认值
 | ||
| 					tempOptions.lineSpace = tempOptions.lineSpace ? tempOptions.lineSpace : 10;
 | ||
| 					// 获取字符串
 | ||
| 					let str = textOptions.text;
 | ||
| 					// 计算指定高度可以输出的最大行数
 | ||
| 					let lineCount = Math.floor((tempOptions.height + tempOptions.lineSpace) / (tempOptions.size +
 | ||
| 						tempOptions.lineSpace));
 | ||
| 					// 初始化单行宽度
 | ||
| 					let lineWidth = 0;
 | ||
| 					let lastSubStrIndex = 0; //每次开始截取的字符串的索引
 | ||
| 
 | ||
| 					// 构建一个打印数组
 | ||
| 					let strArr = str.split("");
 | ||
| 					let drawArr = [];
 | ||
| 					let text = "";
 | ||
| 					while (strArr.length) {
 | ||
| 						let word = strArr.shift();
 | ||
| 						text += word;
 | ||
| 						let textWidth = this.ctx.measureText(text).width;
 | ||
| 						if (textWidth > textOptions.width) {
 | ||
| 							// 因为超出宽度 所以要截取掉最后一个字符
 | ||
| 							text = text.substr(0, text.length - 1);
 | ||
| 							drawArr.push(text);
 | ||
| 							text = "";
 | ||
| 							// 最后一个字还给strArr
 | ||
| 							strArr.unshift(word);
 | ||
| 						} else if (!strArr.length) {
 | ||
| 							drawArr.push(text);
 | ||
| 						}
 | ||
| 					}
 | ||
| 
 | ||
| 					if (drawArr.length > lineCount) {
 | ||
| 						// 超出最大行数
 | ||
| 						drawArr.length = lineCount;
 | ||
| 						let pointWidth = this.ctx.measureText("...").width;
 | ||
| 						let wordWidth = 0;
 | ||
| 						let wordArr = drawArr[drawArr.length - 1].split("");
 | ||
| 						let words = "";
 | ||
| 						while (pointWidth > wordWidth) {
 | ||
| 							words += wordArr.pop();
 | ||
| 							wordWidth = this.ctx.measureText(words).width;
 | ||
| 						}
 | ||
| 						drawArr[drawArr.length - 1] = wordArr.join("") + "...";
 | ||
| 					}
 | ||
| 					// 打印
 | ||
| 					for (let i = 0; i < drawArr.length; i++) {
 | ||
| 						let _h = i > 0 ? tempOptions.size + tempOptions.lineSpace : 0;
 | ||
| 						tempOptions.y = tempOptions.y + _h; // y的位置
 | ||
| 						tempOptions.text = drawArr[i]; // 绘制的文本
 | ||
| 						fillText(tempOptions);
 | ||
| 					}
 | ||
| 				};
 | ||
| 				const roundRect = (x, y, w, h, r) => {
 | ||
| 					// 开始绘制
 | ||
| 					this.ctx.beginPath();
 | ||
| 					// 因为边缘描边存在锯齿,最好指定使用 transparent 填充
 | ||
| 					// 这里是使用 fill 还是 stroke都可以,二选一即可
 | ||
| 					this.ctx.setFillStyle("transparent");
 | ||
| 					// ctx.setStrokeStyle('transparent')
 | ||
| 					// 左上角
 | ||
| 					this.ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
 | ||
| 					// border-top
 | ||
| 					this.ctx.moveTo(x + r, y);
 | ||
| 					this.ctx.lineTo(x + w - r, y);
 | ||
| 					this.ctx.lineTo(x + w, y + r);
 | ||
| 					// 右上角
 | ||
| 					this.ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
 | ||
| 
 | ||
| 					// border-right
 | ||
| 					this.ctx.lineTo(x + w, y + h - r);
 | ||
| 					this.ctx.lineTo(x + w - r, y + h);
 | ||
| 					// 右下角
 | ||
| 					this.ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
 | ||
| 
 | ||
| 					// border-bottom
 | ||
| 					this.ctx.lineTo(x + r, y + h);
 | ||
| 					this.ctx.lineTo(x, y + h - r);
 | ||
| 					// 左下角
 | ||
| 					this.ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
 | ||
| 					// border-left
 | ||
| 					this.ctx.lineTo(x, y + r);
 | ||
| 					this.ctx.lineTo(x + r, y);
 | ||
| 					// 这里是使用 fill 还是 stroke都可以,二选一即可,但是需要与上面对应
 | ||
| 					this.ctx.fill();
 | ||
| 					// ctx.stroke()
 | ||
| 					this.ctx.closePath();
 | ||
| 					// 剪切
 | ||
| 					this.ctx.clip();
 | ||
| 				};
 | ||
| 				// 图片自适应
 | ||
| 				const adaptiveImg = (img, x, y, mode) => {
 | ||
| 					const {
 | ||
| 						imgW: w,
 | ||
| 						imgH: h,
 | ||
| 						width: dWidth,
 | ||
| 						height: dHeight
 | ||
| 					} = img;
 | ||
| 					let dw = dWidth / w; //canvas与图片的宽比
 | ||
| 					let dh = dHeight / h; //canvas与图片的高比
 | ||
| 					// 裁剪图片中间部分
 | ||
| 					if ((w > dWidth && h > dHeight) || (w < dWidth && h < dHeight)) {
 | ||
| 						if (dw > dh) {
 | ||
| 							this.ctx.drawImage(img.path, 0, (h - dHeight / dw) / 2, w, dHeight / dw, x, y, dWidth,
 | ||
| 								dHeight);
 | ||
| 						} else {
 | ||
| 							this.ctx.drawImage(img.path, (w - dWidth / dh) / 2, 0, dWidth / dh, h, x, y, dWidth,
 | ||
| 								dHeight);
 | ||
| 						}
 | ||
| 					} else {
 | ||
| 						// 拉伸图片
 | ||
| 						if (w < dWidth) {
 | ||
| 							this.ctx.drawImage(img.path, 0, (h - dHeight / dw) / 2, w, dHeight / dw, x, y, dWidth,
 | ||
| 								dHeight);
 | ||
| 						} else {
 | ||
| 							this.ctx.drawImage(img.path, (w - dWidth / dh) / 2, 0, dWidth / dh, h, x, y, dWidth,
 | ||
| 								dHeight);
 | ||
| 						}
 | ||
| 					}
 | ||
| 					// 裁剪图片中间部分
 | ||
| 					// this.ctx.drawImage(img.path, sx, sy, sWidth, sHeight, x, y, dWidth, dHeight);
 | ||
| 					// this.ctx.drawImage(img.path, x, y, temp.dWidth, temp.dHeight);
 | ||
| 				};
 | ||
| 				// 绘制背景
 | ||
| 				this.ctx.setFillStyle(this.backgroundColor);
 | ||
| 				this.ctx.fillRect(0, 0, this.width, this.height);
 | ||
| 				/* 所有元素入队则开始绘制 */
 | ||
| 				if (newVal.length === this.renderList.length) {
 | ||
| 					if (newVal.length == 0) {
 | ||
| 						this.$emit("on-error", {
 | ||
| 							msg: "数据为空",
 | ||
| 						});
 | ||
| 						return;
 | ||
| 					}
 | ||
| 					try {
 | ||
| 						// console.log('生成的队列:' + JSON.stringify(newVal));
 | ||
| 						console.log("开始绘制...");
 | ||
| 						for (let i = 0; i < this.drawPathQueue.length; i++) {
 | ||
| 							for (let j = 0; j < this.drawPathQueue.length; j++) {
 | ||
| 								let current = this.drawPathQueue[j];
 | ||
| 								/* 按顺序绘制 */
 | ||
| 								if (current.index === i) {
 | ||
| 									/* 文本绘制 */
 | ||
| 									if (current.type === "text") {
 | ||
| 										console.log("开始绘制...text");
 | ||
| 										fillText(current);
 | ||
| 										this.counter--;
 | ||
| 									}
 | ||
| 									/* 多行文本 */
 | ||
| 									if (current.type === "textarea") {
 | ||
| 										console.log("开始绘制...textarea");
 | ||
| 										fillParagraph(current);
 | ||
| 										this.counter--;
 | ||
| 									}
 | ||
| 									/* 多行文本 */
 | ||
| 									if (current.type === "square") {
 | ||
| 										console.log("开始绘制...square");
 | ||
| 										this.ctx.save(); // 保存上下文,绘制后恢复
 | ||
| 										this.ctx.beginPath(); //开始绘制
 | ||
| 										//画好了圆 剪切  原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内 这也是我们要save上下文的原因
 | ||
| 										// 设置旋转中心
 | ||
| 										let offsetX = current.x + Number(current.width) / 2;
 | ||
| 										let offsetY = current.y + Number(current.height) / 2;
 | ||
| 										this.ctx.translate(offsetX, offsetY);
 | ||
| 										let degrees = current.rotate ? Number(current.rotate) % 360 : 0;
 | ||
| 										this.ctx.rotate((degrees * Math.PI) / 180);
 | ||
| 										this.ctx.fillStyle = current.fillStyle;
 | ||
| 										this.ctx.fillRect(current.x - offsetX, current.y - offsetY, current.width, current
 | ||
| 											.height);
 | ||
| 										this.ctx.closePath();
 | ||
| 										this.ctx.restore(); // 恢复之前保存的上下文
 | ||
| 										this.counter--;
 | ||
| 									}
 | ||
| 									/* 图片绘制 */
 | ||
| 									if (current.type === "image") {
 | ||
| 										console.log("开始绘制...image");
 | ||
| 										if (current.area) {
 | ||
| 											// 绘制绘图区域
 | ||
| 											this.ctx.save();
 | ||
| 											this.ctx.beginPath(); //开始绘制
 | ||
| 											this.ctx.rect(current.area.x, current.area.y, current.area.width, current.area
 | ||
| 												.height);
 | ||
| 											this.ctx.clip();
 | ||
| 											// 设置旋转中心
 | ||
| 											let offsetX = current.x + Number(current.width) / 2;
 | ||
| 											let offsetY = current.y + Number(current.height) / 2;
 | ||
| 											this.ctx.translate(offsetX, offsetY);
 | ||
| 											let degrees = current.rotate ? Number(current.rotate) % 360 : 0;
 | ||
| 											this.ctx.rotate((degrees * Math.PI) / 180);
 | ||
| 											this.ctx.drawImage(current.path, current.x - offsetX, current.y - offsetY,
 | ||
| 												current.width, current.height);
 | ||
| 											this.ctx.closePath();
 | ||
| 											this.ctx.restore(); // 恢复之前保存的上下文
 | ||
| 										} else if (current.shape == "circle") {
 | ||
| 											this.ctx.save(); // 保存上下文,绘制后恢复
 | ||
| 											this.ctx.beginPath(); //开始绘制
 | ||
| 											//先画个圆   前两个参数确定了圆心 (x,y) 坐标  第三个参数是圆的半径  四参数是绘图方向  默认是false,即顺时针
 | ||
| 											let width = current.width / 2 + current.x;
 | ||
| 											let height = current.height / 2 + current.y;
 | ||
| 											let r = current.width / 2;
 | ||
| 											this.ctx.arc(width, height, r, 0, Math.PI * 2);
 | ||
| 											this.ctx.lineTo(current.x, current.y);
 | ||
| 											this.ctx.fill();
 | ||
| 											//画好了圆 剪切  原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内 这也是我们要save上下文的原因
 | ||
| 											this.ctx.clip();
 | ||
| 											// 设置旋转中心
 | ||
| 											let offsetX = current.x + Number(current.width) / 2;
 | ||
| 											let offsetY = current.y + Number(current.height) / 2;
 | ||
| 											this.ctx.translate(offsetX, offsetY);
 | ||
| 											let degrees = current.rotate ? Number(current.rotate) % 360 : 0;
 | ||
| 											this.ctx.rotate((degrees * Math.PI) / 180);
 | ||
| 											current.mode ?
 | ||
| 												adaptiveImg(current, current.x - offsetX, current.y - offsetY, current
 | ||
| 													.mode) :
 | ||
| 												this.ctx.drawImage(current.path, current.x - offsetX, current.y - offsetY,
 | ||
| 													current.width, current.height);
 | ||
| 											this.ctx.closePath();
 | ||
| 											this.ctx.restore(); // 恢复之前保存的上下文
 | ||
| 										} else if (typeof current.shape == "number") {
 | ||
| 											this.ctx.save(); // 保存上下文,绘制后恢复
 | ||
| 											this.ctx.beginPath(); //开始绘制
 | ||
| 											roundRect(current.x, current.y, current.width, current.height, current.shape);
 | ||
| 											//画好了圆 剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内 这也是我们要save上下文的原因
 | ||
| 											// 设置旋转中心
 | ||
| 											let offsetX = current.x + Number(current.width) / 2;
 | ||
| 											let offsetY = current.y + Number(current.height) / 2;
 | ||
| 											this.ctx.translate(offsetX, offsetY);
 | ||
| 											let degrees = current.rotate ? Number(current.rotate) % 360 : 0;
 | ||
| 											this.ctx.rotate((degrees * Math.PI) / 180);
 | ||
| 											current.mode ?
 | ||
| 												adaptiveImg(current, current.x - offsetX, current.y - offsetY, current
 | ||
| 													.mode) :
 | ||
| 												this.ctx.drawImage(current.path, current.x - offsetX, current.y - offsetY,
 | ||
| 													current.width, current.height);
 | ||
| 											this.ctx.closePath();
 | ||
| 											this.ctx.restore(); // 恢复之前保存的上下文
 | ||
| 										} else {
 | ||
| 											this.ctx.drawImage(current.path, current.x, current.y, current.width, current
 | ||
| 												.height);
 | ||
| 										}
 | ||
| 										this.counter--;
 | ||
| 									}
 | ||
| 								}
 | ||
| 							}
 | ||
| 						}
 | ||
| 					} catch (err) {
 | ||
| 						console.log(err);
 | ||
| 						this.$emit("on-error", err);
 | ||
| 					}
 | ||
| 				}
 | ||
| 			},
 | ||
| 			counter(newVal, oldVal) {
 | ||
| 				if (newVal === 0) {
 | ||
| 					this.ctx.draw(false, (draw) => {
 | ||
| 						uni.canvasToTempFilePath({
 | ||
| 								canvasId: "myCanvas",
 | ||
| 								success: (res) => {
 | ||
| 									// 在H5平台下,tempFilePath 为 base64
 | ||
| 									console.log("图片已保存至本地:", res.tempFilePath);
 | ||
| 									this.posterUrl = res.tempFilePath;
 | ||
| 									this.$emit("on-success", res.tempFilePath);
 | ||
| 								},
 | ||
| 								fail: (error) => {
 | ||
| 									this.$emit("on-error", error);
 | ||
| 								},
 | ||
| 							},
 | ||
| 							this
 | ||
| 						);
 | ||
| 					});
 | ||
| 				}
 | ||
| 			},
 | ||
| 		},
 | ||
| 		mounted() {
 | ||
| 			// this.generateImg();
 | ||
| 			// console.log('mounted');
 | ||
| 		},
 | ||
| 		methods: {
 | ||
| 			onCanvasReady() {
 | ||
| 				// #ifdef MP-ALIPAY
 | ||
| 				const query = my.createSelectorQuery();
 | ||
| 				query
 | ||
| 					.select("#myCanvas")
 | ||
| 					.node()
 | ||
| 					.exec((res) => {
 | ||
| 						const canvas = res[0].node;
 | ||
| 						const ctx = canvas.getContext("2d");
 | ||
| 					});
 | ||
| 				// #endif
 | ||
| 			},
 | ||
| 			/**
 | ||
| 			 * @param {*} elClass 元素名称
 | ||
| 			 * @param {*} slot 是否采用slot方式
 | ||
| 			 * @param {*} startX  X偏移
 | ||
| 			 * @param {*} startY Y偏移
 | ||
| 			 * @return {*}
 | ||
| 			 */
 | ||
| 			createForElRect(elClass = "Poster", slot = true, startX = 0, startY = 0) {
 | ||
| 				uni
 | ||
| 					.createSelectorQuery()
 | ||
| 					.selectAll("." + elClass)
 | ||
| 					.fields({
 | ||
| 							dataset: true,
 | ||
| 							size: true,
 | ||
| 							rect: true,
 | ||
| 							value: true,
 | ||
| 							scrollOffset: true,
 | ||
| 							properties: ["src", "mode"],
 | ||
| 							computedStyle: ["margin", "padding", "backgroundColor", "fontSize", "color", "fontWeight",
 | ||
| 								"borderRadius"
 | ||
| 							],
 | ||
| 							context: true,
 | ||
| 						},
 | ||
| 						(res) => {
 | ||
| 							let list = [];
 | ||
| 							const sys = uni.getSystemInfoSync();
 | ||
| 							let multiple = sys.windowWidth / this.width;
 | ||
| 							res.forEach((val, index) => {
 | ||
| 								let src = val.src || val.dataset.enode || "";
 | ||
| 								let type = val.src ? "image" : val.dataset.etype || "text";
 | ||
| 								let text = val.dataset.enode || "";
 | ||
| 								let size = val.fontSize.replace("px", "") || 13;
 | ||
| 								let shape = val.borderRadius == "50%" ? "circle" : val.borderRadius.replace("px",
 | ||
| 									"") * 2;
 | ||
| 								let x = (startX + val.left - (slot ? sys.screenWidth : 0)) / multiple;
 | ||
| 								let y = (startY + val.top) / multiple;
 | ||
| 								y = (startY + val.top - (slot ? 50 : 0)) / multiple;
 | ||
| 								// #ifdef H5
 | ||
| 								y = (startY + val.top) / multiple;
 | ||
| 								// #endif
 | ||
| 								list.push({
 | ||
| 									type: type,
 | ||
| 									shape,
 | ||
| 									text,
 | ||
| 									mode: "center",
 | ||
| 									x,
 | ||
| 									y,
 | ||
| 									path: src,
 | ||
| 									width: val.width / multiple,
 | ||
| 									height: val.height / multiple,
 | ||
| 									size: size / multiple,
 | ||
| 									color: val.color,
 | ||
| 								});
 | ||
| 							});
 | ||
| 							let canvas = uni.createCanvasContext("myCanvas", this);
 | ||
| 							this.ctx = canvas;
 | ||
| 							this.renderList = [...this.list, ...list];
 | ||
| 							this.generateImg();
 | ||
| 						}
 | ||
| 					)
 | ||
| 					.exec();
 | ||
| 			},
 | ||
| 			create() {
 | ||
| 				let canvas = uni.createCanvasContext("myCanvas", this);
 | ||
| 				this.ctx = canvas;
 | ||
| 				this.renderList = this.list;
 | ||
| 				this.generateImg();
 | ||
| 			},
 | ||
| 			async generateImg() {
 | ||
| 				console.log("generateimg");
 | ||
| 				this.counter = this.renderList.length;
 | ||
| 				this.drawPathQueue = [];
 | ||
| 				const getImgInfo = async (current) => {
 | ||
| 					// console.log("current", current);
 | ||
| 					return new Promise((resolve, reject) => {
 | ||
| 						uni.getImageInfo ?
 | ||
| 							uni.getImageInfo({
 | ||
| 								src: current.path,
 | ||
| 								success: (res) => {
 | ||
| 									current.path = res.path;
 | ||
| 									current.imgW = res.width;
 | ||
| 									current.imgH = res.height;
 | ||
| 									// this.drawPathQueue.push(current);
 | ||
| 									resolve(current);
 | ||
| 								},
 | ||
| 							}) :
 | ||
| 							resolve(current);
 | ||
| 					});
 | ||
| 				};
 | ||
| 				const delayedLog = async (v, i) => {
 | ||
| 					let current = this.renderList[i];
 | ||
| 					current.index = i;
 | ||
| 					/* 如果是文本直接放入队列 */
 | ||
| 					if (current.type === "text" || current.type === "textarea" || current.type === "square") {
 | ||
| 						// this.drawPathQueue.push(current);
 | ||
| 						return current;
 | ||
| 					} else {
 | ||
| 						return await getImgInfo(current);
 | ||
| 						/* 图片需获取本地缓存path放入队列 */
 | ||
| 					}
 | ||
| 				};
 | ||
| 				const processArray = async (array) => {
 | ||
| 					// map array to promises
 | ||
| 					const promises = array.map((v, i) => delayedLog(v, i));
 | ||
| 					// wait until all promises are resolved
 | ||
| 					const allData = await Promise.all(promises);
 | ||
| 					console.log("Done!", allData);
 | ||
| 					return allData;
 | ||
| 				};
 | ||
| 				this.drawPathQueue = await processArray(this.renderList);
 | ||
| 			},
 | ||
| 			saveImg() {
 | ||
| 				uni.canvasToTempFilePath({
 | ||
| 						canvasId: "myCanvas",
 | ||
| 						success: (res) => {
 | ||
| 							// 在H5平台下,tempFilePath 为 base64
 | ||
| 							uni.saveImageToPhotosAlbum({
 | ||
| 								filePath: res.tempFilePath,
 | ||
| 								success: () => {
 | ||
| 									console.log("save success");
 | ||
| 								},
 | ||
| 							});
 | ||
| 						},
 | ||
| 					},
 | ||
| 					this
 | ||
| 				);
 | ||
| 			},
 | ||
| 		},
 | ||
| 	};
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| 	.canvas {
 | ||
| 		position: fixed;
 | ||
| 		top: 100rpx;
 | ||
| 		left: 750rpx;
 | ||
| 		width: 100vw;
 | ||
| 	}
 | ||
| 
 | ||
| 	.theContent {}
 | ||
| </style> |