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>
|