You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1323 lines
37 KiB

import _app from './app.js';
import QRCodeAlg from './QRCodeAlg.js';
import { base64ToPath } from './image-tools.js';
const ShreUserPosterBackgroundKey = 'ShrePosterBackground_'; // 背景图片缓存名称前缀
const idKey = 'QSSHAREPOSTER_IDKEY'; //drawArray自动生成的idkey
var isMp = false;
// #ifdef MP
isMp = true;
// #endif
var nbgScale = 1;
// export default
function getSharePoster(obj) {
return new Promise(async (resolve, reject) => {
try {
const result1 = await returnPromise(obj);
resolve(result1);
} catch (e) {
//TODO handle the exception
try {
if(obj.bgScale) {
obj.bgScale = Number(obj.bgScale) - 0.1
}else{
nbgScale = nbgScale - 0.1
}
console.log('------------清除缓存后, 开始第二次尝试------------');
const result2 = await returnPromise(obj);
resolve(result2);
} catch (e) {
//TODO handle the exception
reject(e);
}
}
})
}
function returnPromise(obj) {
let {
type,
formData,
background,
posterCanvasId,
backgroundImage,
reserve,
textArray,
drawArray,
qrCodeArray,
imagesArray,
setCanvasWH,
setCanvasToTempFilePath,
setDraw,
bgScale,
Context,
_this,
delayTimeScale,
drawDelayTime
} = obj;
return new Promise(async (rs, rj) => {
try {
_app.showLoading('正在准备海报数据');
if (!Context) {
_app.log('没有画布对象,创建画布对象');
Context = uni.createCanvasContext(posterCanvasId, (_this || null));
}
let bgObj;
if (background && background.width && background.height) {
bgObj = background;
} else {
bgObj = await getShreUserPosterBackground({
backgroundImage,
type,
formData
});
}
bgScale = bgScale || nbgScale;
bgObj.width = bgObj.width * bgScale;
bgObj.height = bgObj.height * bgScale;
_app.log('获取背景图信息对象成功:' + JSON.stringify(bgObj));
const params = {
bgObj,
type,
bgScale,
getBgObj: function() {
return params.bgObj;
},
setBgObj: function(newBgObj){
const n = {...params.bgObj, ...newBgObj};
params.bgObj = n;
bgObj = n;
}
};
if (imagesArray) {
if (typeof(imagesArray) == 'function')
imagesArray = imagesArray(params);
_app.showLoading('正在生成需绘制图片的临时路径');
_app.log('准备设置图片');
imagesArray = await setImage(imagesArray);
_app.hideLoading();
}
if (textArray) {
if (typeof(textArray) == 'function')
textArray = textArray(params);
textArray = setText(Context, textArray);
}
if (qrCodeArray) {
if (typeof(qrCodeArray) == 'function')
qrCodeArray = qrCodeArray(params);
_app.showLoading('正在生成需绘制图片的临时路径');
for (let i = 0; i < qrCodeArray.length; i++) {
_app.log(i);
if (qrCodeArray[i].image)
qrCodeArray[i].image = await _app.downloadFile_PromiseFc(qrCodeArray[i].image);
}
_app.hideLoading();
}
if (drawArray) {
if (typeof(drawArray) == 'function') {
drawArray = drawArray(params);
}
if (_app.isPromise(drawArray)) {
drawArray = await drawArray;
}
if (_app.isArray(drawArray) && drawArray.length > 0) {
let hasAllInfoCallback = false;
for (let i = 0; i < drawArray.length; i++) {
const drawArrayItem = drawArray[i];
if (_app.isFn(drawArrayItem.allInfoCallback) && !hasAllInfoCallback) hasAllInfoCallback = true;
drawArrayItem[idKey] = i;
let newData;
switch (drawArrayItem.type) {
case 'image':
newData = await setImage(drawArrayItem);
break;
case 'text':
newData = setText(Context, drawArrayItem);
break;
case 'qrcode':
if (drawArrayItem.image)
newData = {
image: await _app.downloadFile_PromiseFc(drawArrayItem.image)
};
break;
case 'custom':
break;
case 'fillrect':
break;
case 'strokeRect':
break;
case 'roundStrokeRect':
break;
case 'roundFillRect':
break;
default:
_app.log('未识别的类型');
break;
}
if (newData && _app.isObject(newData)) {
drawArray[i] = { ...drawArrayItem,
...newData
}
};
}
if (hasAllInfoCallback) {
_app.log('----------------hasAllInfoCallback----------------');
const drawArray_copy = [...drawArray];
drawArray_copy.sort((a, b) => {
const a_serialNum = !_app.isUndef(a.serialNum) && !_app.isNull(a.serialNum) ? Number(a.serialNum) : Number.NEGATIVE_INFINITY;
const b_serialNum = !_app.isUndef(b.serialNum) && !_app.isNull(b.serialNum) ? Number(b.serialNum) : Number.NEGATIVE_INFINITY;
return a_serialNum - b_serialNum;
})
_app.log('开始for循环');
for (let i = 0; i < drawArray_copy.length; i++) {
const item = { ...drawArray_copy[i]
};
if (_app.isFn(item.allInfoCallback)) {
let newData = item.allInfoCallback({
drawArray
});
if (_app.isPromise(newData)) newData = await newData;
const item_idKey = item[idKey];
if (!_app.isUndef(item_idKey)) {
drawArray[item[idKey]] = { ...item,
...newData
};
} else {
console.log('程序错误 找不到idKey!!! ...这不应该啊');
}
}
}
_app.log('for循环结束');
}
}
}
console.log('params:' + JSON.stringify(params))
if (setCanvasWH && typeof(setCanvasWH) == 'function') {
await new Promise((resolve, reject)=>{
setCanvasWH(params);
setTimeout(()=>{
resolve();
}, 50)
})
}
const poster = await drawShareImage({
Context,
type,
posterCanvasId,
reserve,
drawArray,
textArray,
imagesArray,
bgObj,
qrCodeArray,
setCanvasToTempFilePath,
setDraw,
bgScale,
_this,
delayTimeScale,
drawDelayTime
});
_app.hideLoading();
rs({
bgObj,
poster,
type
});
} catch (e) {
//TODO handle the exception
rj(e);
}
});
}
function drawShareImage(obj) { //绘制海报方法
let {
Context,
type,
posterCanvasId,
reserve,
bgObj,
drawArray,
textArray,
qrCodeArray,
imagesArray,
setCanvasToTempFilePath,
setDraw,
bgScale,
_this,
delayTimeScale,
drawDelayTime
} = obj;
const params = {
Context,
bgObj,
type,
bgScale
};
delayTimeScale = delayTimeScale !== undefined ? delayTimeScale : 15;
drawDelayTime = drawDelayTime !== undefined ? drawDelayTime : 100;
return new Promise((rs, rj) => {
try {
_app.showLoading('正在绘制海报');
_app.log('背景对象:' + JSON.stringify(bgObj));
if (bgObj && bgObj.path) {
_app.log('背景有图片路径');
Context.drawImage(bgObj.path, 0, 0, bgObj.width, bgObj.height);
} else {
_app.log('背景没有图片路径');
if (bgObj.backgroundColor) {
_app.log('背景有背景颜色:' + bgObj.backgroundColor);
Context.setFillStyle(bgObj.backgroundColor);
Context.fillRect(0, 0, bgObj.width, bgObj.height);
} else {
_app.log('背景没有背景颜色');
}
}
_app.showLoading('绘制图片');
if (imagesArray && imagesArray.length > 0)
drawImage(Context, imagesArray);
_app.showLoading('绘制自定义内容');
if (setDraw && typeof(setDraw) == 'function') setDraw(params);
_app.showLoading('绘制文本');
if (textArray && textArray.length > 0)
drawText(Context, textArray, bgObj);
_app.showLoading('绘制二维码');
if (qrCodeArray && qrCodeArray.length > 0) {
for (let i = 0; i < qrCodeArray.length; i++) {
drawQrCode(Context, qrCodeArray[i]);
}
}
_app.showLoading('绘制可控层级序列');
if (drawArray && drawArray.length > 0) {
for (let i = 0; i < drawArray.length; i++) {
const drawArrayItem = drawArray[i];
_app.log('绘制可控层级序列, drawArrayItem:' + JSON.stringify(drawArrayItem));
switch (drawArrayItem.type) {
case 'image':
_app.log('绘制可控层级序列, 绘制图片');
drawImage(Context, drawArrayItem);
break;
case 'text':
_app.log('绘制可控层级序列, 绘制文本');
drawText(Context, drawArrayItem, bgObj);
break;
case 'qrcode':
_app.log('绘制可控层级序列, 绘制二维码');
drawQrCode(Context, drawArrayItem);
break;
case 'custom':
_app.log('绘制可控层级序列, 绘制自定义内容');
if (drawArrayItem.setDraw && typeof drawArrayItem.setDraw === 'function')
drawArrayItem.setDraw(Context);
break;drawRoundStrokeRect, drawStrokeRect
case 'fillRect':
_app.log('绘制可控层级序列, 绘制填充直角矩形');
drawFillRect(Context, drawArrayItem);
break;
case 'strokeRect':
_app.log('绘制可控层级序列, 绘制线条直角矩形');
drawStrokeRect(Context, drawArrayItem);
break;
case 'roundStrokeRect':
_app.log('绘制可控层级序列, 绘制线条圆角矩形');
drawRoundStrokeRect(Context, drawArrayItem);
break;
case 'roundFillRect':
_app.log('绘制可控层级序列, 绘制填充圆角矩形');
drawRoundFillRect(Context, drawArrayItem);
break;
default:
_app.log('未识别的类型');
break;
}
}
}
_app.showLoading('绘制中')
setTimeout(() => {
_app.log('准备执行draw方法')
_app.log('Context:' + Context);
const fn = function(){
_app.showLoading('正在输出图片');
let setObj = setCanvasToTempFilePath || {};
if (setObj && typeof(setObj) == 'function')
setObj = setCanvasToTempFilePath(bgObj, type);
let canvasToTempFilePathFn;
const data = {
x: 0,
y: 0,
width: Number(bgObj.width),
height: Number(bgObj.height),
destWidth: Number(bgObj.width), // 若H5使用这里请不要乘以二
destHeight: Number(bgObj.height), // 若H5使用这里请不要乘以二
quality: .8,
fileType: 'jpg',
...setObj
};
console.log('canvasToTempFilePath的data对象:' + JSON.stringify(data));
canvasToTempFilePathFn = function() {
const toTempFilePathObj = { //输出为图片
...data,
canvasId: posterCanvasId,
success(res) {
_app.hideLoading();
rs(res);
},
fail(err) {
_app.hideLoading();
console.log('输出图片失败');
_app.log('输出图片失败:' + JSON.stringify(err));
rj('输出图片失败:' + JSON.stringify(err))
}
}
uni.canvasToTempFilePath(toTempFilePathObj, _this || null);
}
let delayTime = 0;
if (qrCodeArray) {
qrCodeArray.forEach(item => {
if (item.text) {
delayTime += Number(item.text.length);
}
})
}
if (imagesArray) {
imagesArray.forEach(() => {
delayTime += delayTimeScale;
})
}
if (textArray) {
textArray.forEach(() => {
delayTime += delayTimeScale;
})
}
if (drawArray) {
drawArray.forEach(item => {
switch (item.type) {
case 'text':
if (item.text) {
delayTime += item.text.length;
}
break;
default:
delayTime += delayTimeScale;
break;
}
})
}
_app.log('延时系数:' + delayTimeScale);
_app.log('总计延时:' + delayTime);
setTimeout(canvasToTempFilePathFn, delayTime);
}
Context.draw((typeof(reserve) == 'boolean' ? reserve : false), fn);
}, drawDelayTime);
} catch (e) {
//TODO handle the exception
_app.hideLoading();
rj(e);
}
});
}
// export
function drawFillRect(Context, drawArrayItem = {}) { //填充矩形
_app.log('进入绘制填充直角矩形方法, drawArrayItem:' + JSON.stringify(drawArrayItem));
Context.setFillStyle(drawArrayItem.backgroundColor || 'black');
Context.setGlobalAlpha(drawArrayItem.alpha || 1);
Context.fillRect(drawArrayItem.dx || 0, drawArrayItem.dy || 0, drawArrayItem.width || 0, drawArrayItem.height || 0);
Context.setGlobalAlpha(1);
}
// export
function drawStrokeRect(Context, drawArrayItem = {}) { //线条矩形
Context.setStrokeStyle(drawArrayItem.color||'black');
Context.setLineWidth(drawArrayItem.lineWidth || 1);
Context.strokeRect(drawArrayItem.dx, drawArrayItem.dy, drawArrayItem.width, drawArrayItem.height);
}
// export
function drawRoundStrokeRect(Context, drawArrayItem = {}) {
let { dx, dy, width, height, r, lineWidth, color } = drawArrayItem;
r = r || width * .1;
if (width < 2 * r) {
r = width / 2;
}
if (width < 2 * r) {
r = width / 2;
}
Context.beginPath();
Context.moveTo(dx + r, dy);
Context.arcTo(dx + width, dy, dx + width, dy + height, r);
Context.arcTo(dx + width, dy + height, dx, dy + height, r);
Context.arcTo(dx, dy + height, dx, dy, r);
Context.arcTo(dx, dy, dx + width, dy, r);
Context.closePath();
Context.setLineWidth(lineWidth || 1);
Context.setStrokeStyle(color || 'black');
Context.stroke();
}
// export
function drawRoundFillRect(Context, drawArrayItem = {}) {
let { dx, dy, width, height, r, backgroundColor } = drawArrayItem;
r = r || width * .1;
if (width < 2 * r) {
r = width / 2;
}
if (width < 2 * r) {
r = width / 2;
}
Context.beginPath();
Context.moveTo(dx + r, dy);
Context.arcTo(dx + width, dy, dx + width, dy + height, r);
Context.arcTo(dx + width, dy + height, dx, dy + height, r);
Context.arcTo(dx, dy + height, dx, dy, r);
Context.arcTo(dx, dy, dx + width, dy, r);
Context.closePath();
Context.setFillStyle(backgroundColor);
Context.fill();
}
// export
function setText(Context, texts) { // 设置文本数据
_app.log('进入设置文字方法, texts:' + JSON.stringify(texts));
if (texts && _app.isArray(texts)) {
_app.log('texts是数组');
if (texts.length > 0) {
for (let i = 0; i < texts.length; i++) {
_app.log('字符串信息-初始化之前:' + JSON.stringify(texts[i]));
texts[i] = setTextFn(Context, texts[i]);
}
}
} else {
_app.log('texts是对象');
texts = setTextFn(Context, texts);
}
_app.log('返回texts:' + JSON.stringify(texts));
return texts;
}
function setTextFn(Context, textItem) {
_app.log('进入设置文字方法, textItem:' + JSON.stringify(textItem));
if (_app.isNotNull_string(textItem.text)) {
textItem.text = String(textItem.text);
textItem.alpha = textItem.alpha !== undefined ? Number(textItem.alpha) : 1;
textItem.color = textItem.color || 'black';
textItem.size = textItem.size !== undefined ? Number(textItem.size) : 10;
textItem.textAlign = textItem.textAlign || 'left';
textItem.textBaseline = textItem.textBaseline || 'middle';
textItem.dx = Number(textItem.dx) || 0;
textItem.dy = Number(textItem.dy) || 0;
textItem.size = Math.ceil(Number(textItem.size));
_app.log('字符串信息-初始化默认值后:' + JSON.stringify(textItem));
const textLength = countTextLength(Context, {
text: textItem.text,
size: textItem.size
});
_app.log('字符串信息-初始化时的文本长度:' + textLength);
let infoCallBackObj = {};
if (textItem.infoCallBack && typeof(textItem.infoCallBack) === 'function') {
infoCallBackObj = textItem.infoCallBack(textLength);
}
textItem = {
...textItem,
textLength,
...infoCallBackObj
}
_app.log('字符串信息-infoCallBack后:' + JSON.stringify(textItem));
}
return textItem;
}
function countTextLength(Context, obj) {
_app.log('计算文字长度, obj:' + JSON.stringify(obj));
const {
text,
size
} = obj;
Context.setFontSize(size);
let textLength;
try{
textLength = Context.measureText(text); // 官方文档说 App端自定义组件编译模式暂时不可用measureText方法
}catch(e){
//TODO handle the exception
textLength = {};
}
textLength = {};
_app.log('measureText计算文字长度, textLength:' + JSON.stringify(textLength));
textLength = textLength && textLength.width ? textLength.width : 0;
if (!textLength) {
let l = 0;
for (let j = 0; j < text.length; j++) {
let t = text.substr(j, 1);
const countL = countStrLength(t);
_app.log('计算文字宽度系数:' + countL);
l += countL;
}
_app.log('文字宽度总系数:' + l);
textLength = l * size;
}
return textLength;
}
//计算字符长度系数
function countStrLength(t) {
let l;
if (/a/.test(t)) {
l = 0.552734375
} else if (/b/.test(t)) {
l = 0.638671875
} else if (/c/.test(t)) {
l = 0.50146484375
} else if (/d/.test(t)) {
l = 0.6396484375
} else if (/e/.test(t)) {
l = 0.5673828125
} else if (/f/.test(t)) {
l = 0.3466796875
} else if (/g/.test(t)) {
l = 0.6396484375
} else if (/h/.test(t)) {
l = 0.61572265625
} else if (/i/.test(t)) {
l = 0.26611328125
} else if (/j/.test(t)) {
l = 0.26708984375
} else if (/k/.test(t)) {
l = 0.54443359375
} else if (/l/.test(t)) {
l = 0.26611328125
} else if (/m/.test(t)) {
l = 0.93701171875
} else if (/n/.test(t)) {
l = 0.6162109375
} else if (/o/.test(t)) {
l = 0.6357421875
} else if (/p/.test(t)) {
l = 0.638671875
} else if (/q/.test(t)) {
l = 0.6396484375
} else if (/r/.test(t)) {
l = 0.3818359375
} else if (/s/.test(t)) {
l = 0.462890625
} else if (/t/.test(t)) {
l = 0.37255859375
} else if (/u/.test(t)) {
l = 0.6162109375
} else if (/v/.test(t)) {
l = 0.52490234375
} else if (/w/.test(t)) {
l = 0.78955078125
} else if (/x/.test(t)) {
l = 0.5068359375
} else if (/y/.test(t)) {
l = 0.529296875
} else if (/z/.test(t)) {
l = 0.49169921875
} else if (/A/.test(t)) {
l = 0.70361328125
} else if (/B/.test(t)) {
l = 0.62744140625
} else if (/C/.test(t)) {
l = 0.6689453125
} else if (/D/.test(t)) {
l = 0.76171875
} else if (/E/.test(t)) {
l = 0.5498046875
} else if (/F/.test(t)) {
l = 0.53125
} else if (/G/.test(t)) {
l = 0.74365234375
} else if (/H/.test(t)) {
l = 0.7734375
} else if (/I/.test(t)) {
l = 0.2939453125
} else if (/J/.test(t)) {
l = 0.39599609375
} else if (/K/.test(t)) {
l = 0.634765625
} else if (/L/.test(t)) {
l = 0.51318359375
} else if (/M/.test(t)) {
l = 0.97705078125
} else if (/N/.test(t)) {
l = 0.81298828125
} else if (/O/.test(t)) {
l = 0.81494140625
} else if (/P/.test(t)) {
l = 0.61181640625
} else if (/Q/.test(t)) {
l = 0.81494140625
} else if (/R/.test(t)) {
l = 0.65283203125
} else if (/S/.test(t)) {
l = 0.5771484375
} else if (/T/.test(t)) {
l = 0.5732421875
} else if (/U/.test(t)) {
l = 0.74658203125
} else if (/V/.test(t)) {
l = 0.67626953125
} else if (/W/.test(t)) {
l = 1.017578125
} else if (/X/.test(t)) {
l = 0.64501953125
} else if (/Y/.test(t)) {
l = 0.603515625
} else if (/Z/.test(t)) {
l = 0.6201171875
} else if (/[0-9]/.test(t)) {
l = 0.58642578125
} else if (/[\u4e00-\u9fa5]/.test(t)) {
l = 1
} else if (/ /.test(t)) {
l = 0.2958984375
} else if (/\`/.test(t)) {
l = 0.294921875
} else if (/\~/.test(t)) {
l = 0.74169921875
} else if (/\!/.test(t)) {
l = 0.3125
} else if (/\@/.test(t)) {
l = 1.03125
} else if (/\#/.test(t)) {
l = 0.63818359375
} else if (/\$/.test(t)) {
l = 0.58642578125
} else if (/\%/.test(t)) {
l = 0.8896484375
} else if (/\^/.test(t)) {
l = 0.74169921875
} else if (/\&/.test(t)) {
l = 0.8701171875
} else if (/\*/.test(t)) {
l = 0.455078125
} else if (/\(/.test(t)) {
l = 0.333984375
} else if (/\)/.test(t)) {
l = 0.333984375
} else if (/\_/.test(t)) {
l = 0.4482421875
} else if (/\-/.test(t)) {
l = 0.4326171875
} else if (/\+/.test(t)) {
l = 0.74169921875
} else if (/\=/.test(t)) {
l = 0.74169921875
} else if (/\|/.test(t)) {
l = 0.26904296875
} else if (/\\/.test(t)) {
l = 0.416015625
} else if (/\[/.test(t)) {
l = 0.333984375
} else if (/\]/.test(t)) {
l = 0.333984375
} else if (/\;/.test(t)) {
l = 0.24072265625
} else if (/\'/.test(t)) {
l = 0.25634765625
} else if (/\,/.test(t)) {
l = 0.24072265625
} else if (/\./.test(t)) {
l = 0.24072265625
} else if (/\//.test(t)) {
l = 0.42724609375
} else if (/\{/.test(t)) {
l = 0.333984375
} else if (/\}/.test(t)) {
l = 0.333984375
} else if (/\:/.test(t)) {
l = 0.24072265625
} else if (/\"/.test(t)) {
l = 0.435546875
} else if (/\</.test(t)) {
l = 0.74169921875
} else if (/\>/.test(t)) {
l = 0.74169921875
} else if (/\?/.test(t)) {
l = 0.48291015625
} else {
l = 1
}
return l;
}
// export
function setImage(images) { // 设置图片数据
_app.log('进入设置图片数据方法');
return new Promise(async (resolve, rejcet) => {
try {
if (images && _app.isArray(images)) {
_app.log('images是一个数组');
for (let i = 0; i < images.length; i++) {
_app.log('设置图片数据循环中:' + i);
images[i] = await setImageFn(images[i]);
}
} else {
_app.log('images是一个对象');
images = await setImageFn(images);
}
resolve(images);
} catch (e) {
//TODO handle the exception
rejcet(e);
}
})
}
function base64ToPathFn(path) {
var reg = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i;
if(!reg.test(path)){
return Promise.resolve(path);
}
return base64ToPath(path);
}
function setImageFn(image) {
return new Promise(async (resolve, reject) => {
if (image.url) {
image.url = (await base64ToPathFn(image.url));
let imgUrl = image.url;
imgUrl = await _app.downloadFile_PromiseFc(imgUrl);
image.url = imgUrl;
const hasinfoCallBack = image.infoCallBack && typeof(image.infoCallBack) === 'function';
let imageInfo = {};
imageInfo = await _app.getImageInfo_PromiseFc(imgUrl);
if (hasinfoCallBack) {
image = {
...image,
...image.infoCallBack(imageInfo)
};
}
image.dx = Number(image.dx) || 0;
image.dy = Number(image.dy) || 0;
image.dWidth = Number(image.dWidth || imageInfo.width);
image.dHeight = Number(image.dHeight || imageInfo.height);
image = {
...image,
imageInfo
}
}
resolve(image);
})
}
// export
function drawText(Context, textArray, bgObj) { // 先遍历换行再绘制
if (!_app.isArray(textArray)) {
_app.log('遍历文本方法, 不是数组');
textArray = [textArray];
} else {
_app.log('遍历文本方法, 是数组');
}
_app.log('遍历文本方法, textArray:' + JSON.stringify(textArray));
const newArr = [];
if (textArray && textArray.length > 0) {
for (let j = 0; j < textArray.length; j++) {
const textItem = textArray[j];
if (textItem.text && textItem.lineFeed) {
let lineNum = -1,
maxWidth = bgObj.width,
lineHeight = textItem.size,
dx = textItem.dx;
if (_app.isObject(textItem.lineFeed)) {
const lineFeed = textItem.lineFeed;
lineNum = (lineFeed.lineNum !== undefined && typeof(lineFeed.lineNum) === 'number') && lineFeed.lineNum >= 0 ?
lineFeed.lineNum : lineNum;
maxWidth = (lineFeed.maxWidth !== undefined && typeof(lineFeed.maxWidth) === 'number') ? lineFeed.maxWidth :
maxWidth;
lineHeight = (lineFeed.lineHeight !== undefined && typeof(lineFeed.lineHeight) === 'number') ? lineFeed.lineHeight :
lineHeight;
dx = (lineFeed.dx !== undefined && typeof(lineFeed.dx) === 'number') ? lineFeed.dx : dx;
}
const chr = (textItem.text).split("");
let temp = "";
const row = [];
//循环出几行文字组成数组
for (let a = 0, len = chr.length; a < len; a++) {
if (countTextLength(Context, {
text: temp,
size: textItem.size
}) <= maxWidth && countTextLength(Context, {
text: (temp + chr[a]),
size: textItem.size
}) <= maxWidth) {
temp += chr[a];
if (a == (chr.length - 1)) {
row.push(temp);
}
} else {
row.push(temp);
temp = chr[a];
}
}
_app.log('循环出的文本数组:' + JSON.stringify(row));
//只显示几行 变量间距lineHeight 变量行数lineNum
let allNum = (lineNum >= 0 && lineNum < row.length) ? lineNum : row.length;
for (let i = 0; i < allNum; i++) {
let str = row[i];
if (i == (allNum - 1) && allNum < row.length) {
str = str.substring(0, str.length - 1) + '...';
}
const obj = { ...textItem,
text: str,
dx: i === 0 ? textItem.dx : (dx >= 0 ? dx : textItem.dx),
dy: textItem.dy + (i * lineHeight),
textLength: countTextLength(Context, {
text: str,
size: textItem.size
})
};
_app.log('重新组成的文本对象:' + JSON.stringify(obj));
newArr.push(obj);
}
} else {
newArr.push(textItem);
}
}
}
_app.log('绘制文本新数组:' + JSON.stringify(newArr));
drawTexts(Context, newArr);
}
function setFont(textItem = {}) {
if (textItem.font && typeof(textItem.font) === 'string') {
_app.log(textItem.font)
return textItem.font;
} else {
let fontStyle = 'normal';
let fontVariant = 'normal';
let fontWeight = 'normal';
let fontSize = textItem.size || 10;
let fontFamily = 'sans-serif';
fontSize = Math.ceil(Number(fontSize));
if (textItem.fontStyle && typeof(textItem.fontStyle) === 'string')
fontStyle = textItem.fontStyle.trim();
if (textItem.fontVariant && typeof(textItem.fontVariant) === 'string')
fontVariant = textItem.fontVariant.trim();
if (textItem.fontWeight && (typeof(textItem.fontWeight) === 'string' || typeof(textItem.fontWeight) === 'number'))
fontWeight = textItem.fontWeight.trim();
if (textItem.fontFamily && typeof(textItem.fontFamily) === 'string')
fontFamily = textItem.fontFamily.trim();
return fontStyle + ' ' +
fontVariant + ' ' +
fontWeight + ' ' +
fontSize + 'px' + ' ' +
fontFamily;
}
}
function drawTexts(Context, texts) { // 绘制文本
_app.log('准备绘制文本方法, texts:' + JSON.stringify(texts));
if (texts && _app.isArray(texts)) {
_app.log('准备绘制文本方法, 是数组');
if (texts.length > 0) {
for (let i = 0; i < texts.length; i++) {
drawTextFn(Context, texts[i]);
}
}
} else {
_app.log('准备绘制文本方法, 不是数组');
drawTextFn(Context, texts);
}
}
function drawTextFn(Context, textItem) {
_app.log('进入绘制文本方法, textItem:' + JSON.stringify(textItem));
if (textItem && _app.isObject(textItem) && textItem.text) {
Context.font = setFont(textItem);
Context.setFillStyle(textItem.color);
Context.setGlobalAlpha(textItem.alpha);
Context.setTextAlign(textItem.textAlign);
Context.setTextBaseline(textItem.textBaseline);
Context.fillText(textItem.text, textItem.dx, textItem.dy);
if (textItem.lineThrough && _app.isObject(textItem.lineThrough)) {
_app.log('有删除线');
let lineThrough = textItem.lineThrough;
lineThrough.alpha = lineThrough.alpha !== undefined ? lineThrough.alpha : textItem.alpha;
lineThrough.style = lineThrough.style || textItem.color;
lineThrough.width = lineThrough.width !== undefined ? lineThrough.width : textItem.size / 10;
lineThrough.cap = lineThrough.cap !== undefined ? lineThrough.cap : 'butt';
_app.log('删除线对象:' + JSON.stringify(lineThrough));
Context.setGlobalAlpha(lineThrough.alpha);
Context.setStrokeStyle(lineThrough.style);
Context.setLineWidth(lineThrough.width);
Context.setLineCap(lineThrough.cap);
let mx, my;
switch (textItem.textAlign) {
case 'left':
mx = textItem.dx;
break;
case 'center':
mx = textItem.dx - (textItem.textLength) / 2;
break;
default:
mx = textItem.dx - (textItem.textLength);
break;
}
switch (textItem.textBaseline) {
case 'top':
my = textItem.dy + (textItem.size * .5);
break;
case 'middle':
my = textItem.dy;
break;
default:
my = textItem.dy - (textItem.size * .5);
break;
}
Context.beginPath();
Context.moveTo(mx, my);
Context.lineTo(mx + textItem.textLength, my);
Context.stroke();
Context.closePath();
_app.log('删除线完毕');
}
Context.setGlobalAlpha(1);
Context.font = '10px sans-serif';
}
}
// export
function drawImage(Context, images) { // 绘制图片
_app.log('判断图片数据类型:' + JSON.stringify(images))
if (images && _app.isArray(images)) {
if (images.length > 0) {
for (let i = 0; i < images.length; i++) {
readyDrawImageFn(Context, images[i]);
}
}
} else {
readyDrawImageFn(Context, images);
}
}
function readyDrawImageFn(Context, img) {
_app.log('判断绘制图片形状, img:' + JSON.stringify(img));
if (img.url) {
if (img.circleSet) {
drawCircleImage(Context, img);
} else if (img.roundRectSet) {
drawRoundRectImage(Context, img);
} else {
drawImageFn(Context, img);
}
}
}
function drawImageFn(Context, img) {
_app.log('进入绘制默认图片方法, img:' + JSON.stringify(img));
if (img.url) {
const hasAlpha = !_app.isUndef(img.alpha);
img.alpha = Number(!_app.isUndef(img.alpha) ? img.alpha : 1);
Context.setGlobalAlpha(img.alpha);
_app.log('绘制默认图片方法, 有url');
if (img.dWidth && img.dHeight && img.sx && img.sy && img.sWidth && img.sHeight) {
_app.log('绘制默认图片方法, 绘制第一种方案');
Context.drawImage(img.url,
Number(img.sx) || false, Number(img.sy) || false,
Number(img.sWidth) || false, Number(img.sHeight) || false,
Number(img.dx || 0), Number(img.dy || 0),
Number(img.dWidth) || false, Number(img.dHeight) || false,);
} else if (img.dWidth && img.dHeight) {
_app.log('绘制默认图片方法, 绘制第二种方案');
Context.drawImage(img.url, Number(img.dx || 0), Number(img.dy || 0),
Number(img.dWidth) || false, Number(img.dHeight) || false);
} else {
_app.log('绘制默认图片方法, 绘制第三种方案');
Context.drawImage(img.url, Number(img.dx || 0), Number(img.dy || 0));
}
if (hasAlpha) {
Context.setGlobalAlpha(1);
}
}
_app.log('绘制默认图片方法, 绘制完毕');
}
function drawCircleImage(Context, obj) {
_app.log('进入绘制圆形图片方法, obj:' + JSON.stringify(obj));
let {
dx,
dy,
dWidth,
dHeight,
circleSet,
imageInfo
} = obj;
let x, y, r;
if (typeof circleSet === 'object') {
x = circleSet.x;
y = circleSet.y;
r = circleSet.r;
}
if (!r) {
let d;
d = dWidth > dHeight ? dHeight : dWidth;
r = d / 2;
}
x = x ? dx + x : (dx || 0) + r;
y = y ? dy + y : (dy || 0) + r;
Context.save();
Context.beginPath();
Context.arc(x, y, r, 0, 2 * Math.PI, false);
Context.closePath();
Context.setGlobalAlpha(0);
Context.fillStyle = '#FFFFFF';
Context.fill();
Context.setGlobalAlpha(1);
Context.clip();
drawImageFn(Context, obj);
_app.log('默认图片绘制完毕');
Context.restore();
}
function drawRoundRectImage(Context, obj) { // 绘制矩形
_app.log('进入绘制矩形图片方法, obj:' + JSON.stringify(obj));
Context.save();
let {
dx,
dy,
dWidth,
dHeight,
roundRectSet,
imageInfo
} = obj;
let r;
if (typeof roundRectSet === 'object') {
r = roundRectSet.r;
}
r = r || dWidth * .1;
if (dWidth < 2 * r) {
r = dWidth / 2;
}
if (dHeight < 2 * r) {
r = dHeight / 2;
}
Context.beginPath();
Context.moveTo(dx + r, dy);
Context.arcTo(dx + dWidth, dy, dx + dWidth, dy + dHeight, r);
Context.arcTo(dx + dWidth, dy + dHeight, dx, dy + dHeight, r);
Context.arcTo(dx, dy + dHeight, dx, dy, r);
Context.arcTo(dx, dy, dx + dWidth, dy, r);
Context.closePath();
Context.setGlobalAlpha(0);
Context.fillStyle = '#FFFFFF';
Context.fill();
Context.setGlobalAlpha(1);
Context.clip();
drawImageFn(Context, obj);
Context.restore();
_app.log('进入绘制矩形图片方法, 绘制完毕');
}
// export
function drawQrCode(Context, qrCodeObj) { //生成二维码方法, 参考了 诗小柒 的二维码生成器代码
_app.log('进入绘制二维码方法')
_app.showLoading('正在生成二维码');
let qrcodeAlgObjCache = [];
let options = {
text: String(qrCodeObj.text || '') || '', // 生成内容
size: Number(qrCodeObj.size || 0) || 200, // 二维码大小
background: String(qrCodeObj.background || '') || '#ffffff', // 背景色
foreground: String(qrCodeObj.foreground || '') || '#000000', // 前景色
pdground: String(qrCodeObj.pdground || '') || '#000000', // 定位角点颜色
correctLevel: Number(qrCodeObj.correctLevel || 0) || 3, // 容错级别
image: String(qrCodeObj.image || '') || '', // 二维码图标
imageSize: Number(qrCodeObj.imageSize || 0) || 40, // 二维码图标大小
dx: Number(qrCodeObj.dx || 0) || 0, // x轴距离
dy: Number(qrCodeObj.dy || 0) || 0 // y轴距离
}
let qrCodeAlg = null;
let d = 0;
for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
d = i;
if (qrcodeAlgObjCache[i].text == options.text && qrcodeAlgObjCache[i].text.correctLevel == options.correctLevel) {
qrCodeAlg = qrcodeAlgObjCache[i].obj;
break;
}
}
if (d == l) {
qrCodeAlg = new QRCodeAlg(options.text, options.correctLevel);
qrcodeAlgObjCache.push({
text: options.text,
correctLevel: options.correctLevel,
obj: qrCodeAlg
});
}
let getForeGround = function(config) {
let options = config.options;
if (options.pdground && (
(config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
(config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
(config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
)) {
return options.pdground;
}
return options.foreground;
}
let count = qrCodeAlg.getModuleCount();
let ratioSize = options.size;
let ratioImgSize = options.imageSize;
//计算每个点的长宽
let tileW = (ratioSize / count).toPrecision(4);
let tileH = (ratioSize / count).toPrecision(4);
//绘制
for (let row = 0; row < count; row++) {
for (let col = 0; col < count; col++) {
let w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
let h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW));
let foreground = getForeGround({
row: row,
col: col,
count: count,
options: options
});
Context.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background);
Context.fillRect(options.dx + Math.round(col * tileW), options.dy + Math.round(row * tileH), w, h);
}
}
if (options.image) {
let x = options.dx + Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
let y = options.dy + Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
drawRoundedRect(Context, x, y, ratioImgSize, ratioImgSize, 2, 6, true, true)
Context.drawImage(options.image, x, y, ratioImgSize, ratioImgSize);
// 画圆角矩形
function drawRoundedRect(ctxi, x, y, width, height, r, lineWidth, fill, stroke) {
ctxi.setLineWidth(lineWidth);
ctxi.setFillStyle(options.background);
ctxi.setStrokeStyle(options.background);
ctxi.beginPath(); // draw top and top right corner
ctxi.moveTo(x + r, y);
ctxi.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner
ctxi.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner
ctxi.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner
ctxi.arcTo(x, y, x + r, y, r);
ctxi.closePath();
if (fill) {
ctxi.fill();
}
if (stroke) {
ctxi.stroke();
}
}
}
_app.log('进入绘制二维码方法完毕')
_app.hideLoading();
}
function getShreUserPosterBackground(objs) { //检查背景图是否存在于本地, 若存在直接返回, 否则调用getShreUserPosterBackgroundFc方法
let {
backgroundImage,
type
} = objs;
return new Promise(async (resolve, reject) => {
try {
_app.showLoading('正在获取海报背景图');
const savedFilePath = await getShreUserPosterBackgroundFc(objs)
_app.hideLoading();
resolve(savedFilePath);
} catch (e) {
_app.hideLoading();
_app.showToast('获取分享用户背景图失败:' + JSON.stringify(e));
_app.log(JSON.stringify(e));
reject(e);
}
})
}
function getPosterStorage(type) {
return _app.getStorageSync(getStorageKey(type));
}
function removePosterStorage(type) {
const ShreUserPosterBackgroundKey = getStorageKey(type);
const pbg = _app.getStorageSync(ShreUserPosterBackgroundKey);
if (pbg && pbg.path) {
_app.removeStorageSync(ShreUserPosterBackgroundKey);
}
}
function setPosterStorage(type, data) {
_app.setStorage(getStorageKey(type), data);
}
function getStorageKey(type) {
return ShreUserPosterBackgroundKey + (type || 'default');
}
function getShreUserPosterBackgroundFc(objs, upimage) { //下载并保存背景图方法
let {
backgroundImage,
type
} = objs;
_app.log('获取分享背景图, 尝试清空本地数据');
return new Promise(async (resolve, reject) => {
try {
_app.showLoading('正在下载海报背景图');
_app.log('没有从后端获取的背景图片路径, 尝试从后端获取背景图片路径');
let image = backgroundImage?backgroundImage:(await _app.getPosterUrl(objs));
image = (await base64ToPathFn(image));
_app.log('尝试下载并保存背景图:' + image);
const savedFilePath = await _app.downLoadAndSaveFile_PromiseFc(image);
if (savedFilePath) {
_app.log('下载并保存背景图成功:' + savedFilePath);
const imageObj = await _app.getImageInfo_PromiseFc(savedFilePath);
_app.log('获取图片信息成功');
const returnObj = {
path: savedFilePath,
width: imageObj.width,
height: imageObj.height,
name: _app.fileNameInPath(image)
}
_app.log('拼接背景图信息对象成功:' + JSON.stringify(returnObj));
// #ifndef H5
setPosterStorage(type, { ...returnObj
});
// #endif
_app.hideLoading();
_app.log('返回背景图信息对象');
resolve({ ...returnObj
});
} else {
_app.hideLoading();
reject('not find savedFilePath');
}
} catch (e) {
//TODO handle the exception
reject(e);
}
});
}
module.exports = {
getSharePoster,
setText,
setImage,
drawText,
drawImage,
drawQrCode,
drawFillRect,
drawStrokeRect,
drawRoundStrokeRect,
drawRoundFillRect
}