|
@@ -52,26 +52,26 @@ export class CanvasUtils {
|
|
|
drawPoster(data) {
|
|
|
return new Promise(async (resolve, reject) => {
|
|
|
try {
|
|
|
- // 基础参数
|
|
|
- const w = uni.upx2px(690)
|
|
|
- const top = uni.upx2px(0)
|
|
|
- const r = uni.upx2px(24)
|
|
|
+ // 基础参数 - 使用2倍分辨率提高清晰度
|
|
|
+ const w = uni.upx2px(690) * 2
|
|
|
+ const top = uni.upx2px(0) * 2
|
|
|
+ const r = uni.upx2px(24) * 2
|
|
|
|
|
|
- // 计算内容布局参数
|
|
|
+ // 计算内容布局参数 - 使用2倍分辨率
|
|
|
const watermarkArr = data.treeObj.watermarkArr || [];
|
|
|
- const lineHeight = uni.upx2px(40); // 水印文字行间距
|
|
|
- const startY = uni.upx2px(870); // 水印文字起始Y坐标
|
|
|
+ const lineHeight = uni.upx2px(40) * 2; // 水印文字行间距
|
|
|
+ const startY = uni.upx2px(870) * 2; // 水印文字起始Y坐标
|
|
|
const lastWatermarkY = startY + ((watermarkArr.length - 1) * lineHeight);
|
|
|
|
|
|
- const logoHeight = uni.upx2px(80);
|
|
|
- const logoSpacing = uni.upx2px(26);
|
|
|
+ const logoHeight = uni.upx2px(80) * 2;
|
|
|
+ const logoSpacing = uni.upx2px(26) * 2;
|
|
|
const logoY = lastWatermarkY - logoHeight - logoSpacing;
|
|
|
const brandTextY = logoY + logoHeight + logoSpacing;
|
|
|
|
|
|
// 使用固定高度,确保有足够空间显示所有内容
|
|
|
// 计算canvas高度(根据内容自适应)
|
|
|
- const bottomMargin = uni.upx2px(20); // 底部边距
|
|
|
- const h = brandTextY + uni.upx2px(30) + bottomMargin; // 30rpx是文字高度估算
|
|
|
+ const bottomMargin = uni.upx2px(20) * 2; // 底部边距
|
|
|
+ const h = brandTextY + uni.upx2px(30) * 2 + bottomMargin; // 30rpx是文字高度估算
|
|
|
|
|
|
|
|
|
// 下载所有图片到本地
|
|
@@ -138,54 +138,48 @@ export class CanvasUtils {
|
|
|
ctx.setFillStyle('#000000')
|
|
|
ctx.setTextAlign('left') // 设置文字对齐方式
|
|
|
ctx.setTextBaseline('top') // 设置文字基线
|
|
|
- ctx.font = `bold ${uni.upx2px(36)}px "SweiSpringCJKtc", Arial, sans-serif` // 设置字体样式:粗体 + 自定义字体
|
|
|
- ctx.fillText(data.treeObj.year, uni.upx2px(26), uni.upx2px(34))
|
|
|
+ ctx.font = `bold ${uni.upx2px(36) * 2}px "SweiSpringCJKtc", Arial, sans-serif` // 设置字体样式:粗体 + 自定义字体
|
|
|
+ ctx.fillText(data.treeObj.year, uni.upx2px(26) * 2, uni.upx2px(34) * 2)
|
|
|
|
|
|
// 绘制月份
|
|
|
- ctx.font = `bold ${uni.upx2px(172)}px "SweiSpringCJKtc", Arial, sans-serif` // 设置字体样式:粗体 + 自定义字体,172rpx大小
|
|
|
- ctx.fillText(data.treeObj.monthNumber, uni.upx2px(26), uni.upx2px(76))
|
|
|
+ ctx.font = `bold ${uni.upx2px(172) * 2}px "SweiSpringCJKtc", Arial, sans-serif` // 设置字体样式:粗体 + 自定义字体,172rpx大小
|
|
|
+ ctx.fillText(data.treeObj.monthNumber, uni.upx2px(26) * 2, uni.upx2px(76) * 2)
|
|
|
|
|
|
// 绘制斜线
|
|
|
- ctx.setLineWidth(uni.upx2px(4)) // 设置线条宽度
|
|
|
+ ctx.setLineWidth(uni.upx2px(4) * 2) // 设置线条宽度
|
|
|
ctx.beginPath()
|
|
|
- ctx.moveTo(uni.upx2px(160), uni.upx2px(180)) // 斜线起点(右上角)
|
|
|
- ctx.lineTo(uni.upx2px(140), uni.upx2px(220)) // 斜线终点(左下角)
|
|
|
+ ctx.moveTo(uni.upx2px(160) * 2, uni.upx2px(180) * 2) // 斜线起点(右上角)
|
|
|
+ ctx.lineTo(uni.upx2px(140) * 2, uni.upx2px(220) * 2) // 斜线终点(左下角)
|
|
|
ctx.stroke()
|
|
|
|
|
|
// 绘制日
|
|
|
- ctx.font = `bold ${uni.upx2px(48)}px "SweiSpringCJKtc", Arial, sans-serif` // 设置字体样式:粗体 + 自定义字体,172rpx大小
|
|
|
- ctx.fillText(data.treeObj.day, uni.upx2px(162), uni.upx2px(180))
|
|
|
+ ctx.font = `bold ${uni.upx2px(48) * 2}px "SweiSpringCJKtc", Arial, sans-serif` // 设置字体样式:粗体 + 自定义字体,172rpx大小
|
|
|
+ ctx.fillText(data.treeObj.day, uni.upx2px(162) * 2, uni.upx2px(180) * 2)
|
|
|
|
|
|
// 绘制二维码图片
|
|
|
if (downloadedImages.qrCodeUrl) {
|
|
|
try {
|
|
|
- ctx.drawImage(downloadedImages.qrCodeUrl, uni.upx2px(w - -190), uni.upx2px(34), uni.upx2px(130), uni.upx2px(142));
|
|
|
+ ctx.drawImage(downloadedImages.qrCodeUrl, uni.upx2px(545) * 2, uni.upx2px(34) * 2, uni.upx2px(130) * 2, uni.upx2px(142) * 2);
|
|
|
} catch (error) {
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // 添加品种
|
|
|
- ctx.setFontSize(uni.upx2px(24))
|
|
|
- ctx.fillText(data.treeObj.pz, uni.upx2px(w - -218), uni.upx2px(190))
|
|
|
-
|
|
|
- // 绘制树龄
|
|
|
- ctx.setFontSize(uni.upx2px(24))
|
|
|
- ctx.fillText(`${data.treeObj.age}年 ${data.treeObj.age > 9 ? "老树" : "树龄"}`, uni.upx2px(w - -104), uni.upx2px(190))
|
|
|
-
|
|
|
- // 绘制气候适宜
|
|
|
- ctx.setFontSize(uni.upx2px(24))
|
|
|
- ctx.fillText(data.treeObj.phenology, uni.upx2px(w - -32), uni.upx2px(230))
|
|
|
|
|
|
- // 绘制采摘方式
|
|
|
- ctx.setFontSize(uni.upx2px(24))
|
|
|
- ctx.fillText(`气候适宜-${data.treeObj.howTxt || "果园采摘"}`, uni.upx2px(w - -114), uni.upx2px(230))
|
|
|
+ // 添加品种 绘制树龄 - 靠右对齐
|
|
|
+ ctx.setFontSize(uni.upx2px(24) * 2)
|
|
|
+ ctx.setTextAlign('right')
|
|
|
+ ctx.fillText(`${data.treeObj.pz} ${data.treeObj.age}年 ${data.treeObj.age > 9 ? "老树" : "树龄"}`, uni.upx2px(665) * 2, uni.upx2px(186) * 2)
|
|
|
+
|
|
|
+ // 绘制气候适宜 绘制采摘方式 - 靠右对齐
|
|
|
+ ctx.setFontSize(uni.upx2px(24) * 2)
|
|
|
+ ctx.setTextAlign('right')
|
|
|
+ ctx.fillText(`${data.treeObj.phenology} 气候适宜-${data.treeObj.howTxt || "果园采摘"}`, uni.upx2px(665) * 2, uni.upx2px(225) * 2)
|
|
|
|
|
|
// 绘制树图片(带圆角)
|
|
|
- const imgX = uni.upx2px(26);
|
|
|
- const imgY = uni.upx2px(280);
|
|
|
- const imgW = uni.upx2px(640);
|
|
|
- const imgH = uni.upx2px(480);
|
|
|
- const radius = uni.upx2px(10);
|
|
|
+ const imgX = uni.upx2px(26) * 2;
|
|
|
+ const imgY = uni.upx2px(280) * 2;
|
|
|
+ const imgW = uni.upx2px(640) * 2;
|
|
|
+ const imgH = uni.upx2px(480) * 2;
|
|
|
+ const radius = uni.upx2px(10) * 2;
|
|
|
|
|
|
// 创建圆角路径
|
|
|
ctx.beginPath();
|
|
@@ -217,12 +211,12 @@ export class CanvasUtils {
|
|
|
ctx.restore();
|
|
|
|
|
|
// 绘制图片文字背景和边框(圆角)
|
|
|
- const textX = uni.upx2px(w - -104);
|
|
|
- const textY = uni.upx2px(316);
|
|
|
- const textWidth = uni.upx2px(180); // 背景宽度
|
|
|
- const textHeight = uni.upx2px(18); // 背景高度
|
|
|
- const padding = uni.upx2px(16); // 内边距
|
|
|
- const textRadius = uni.upx2px(30); // 圆角半径
|
|
|
+ const textX = uni.upx2px(450) * 2;
|
|
|
+ const textY = uni.upx2px(316) * 2;
|
|
|
+ const textWidth = uni.upx2px(180) * 2; // 背景宽度
|
|
|
+ const textHeight = uni.upx2px(18) * 2; // 背景高度
|
|
|
+ const padding = uni.upx2px(16) * 2; // 内边距
|
|
|
+ const textRadius = uni.upx2px(25) * 2; // 圆角半径
|
|
|
|
|
|
// 绘制黑色半透明背景(圆角)
|
|
|
ctx.setFillStyle('rgba(0, 0, 0, 0.6)');
|
|
@@ -241,7 +235,7 @@ export class CanvasUtils {
|
|
|
|
|
|
// 绘制白色边框(圆角)
|
|
|
ctx.setStrokeStyle('rgba(255, 255, 255, 0.39)');
|
|
|
- ctx.setLineWidth(uni.upx2px(2));
|
|
|
+ ctx.setLineWidth(uni.upx2px(2) * 2);
|
|
|
ctx.beginPath();
|
|
|
ctx.moveTo(textX - padding + textRadius, textY - padding);
|
|
|
ctx.lineTo(textX - padding + textWidth + padding * 2 - textRadius, textY - padding);
|
|
@@ -260,13 +254,13 @@ export class CanvasUtils {
|
|
|
ctx.setTextAlign('center');
|
|
|
ctx.setTextBaseline('middle');
|
|
|
// 重置字体为默认字体
|
|
|
- ctx.font = `bold ${uni.upx2px(20)}px Arial, sans-serif`;
|
|
|
+ ctx.font = `bold ${uni.upx2px(20) * 2}px Arial, sans-serif`;
|
|
|
ctx.fillText(`${data.treeObj.pz}-${data.treeObj.countyName}`, textX + textWidth / 2, textY + textHeight / 2);
|
|
|
|
|
|
// 绘制树牌文字背景
|
|
|
if (downloadedImages.treeNameBgUrl) {
|
|
|
try {
|
|
|
- ctx.drawImage(downloadedImages.treeNameBgUrl, uni.upx2px(40), uni.upx2px(486), uni.upx2px(276), uni.upx2px(274));
|
|
|
+ ctx.drawImage(downloadedImages.treeNameBgUrl, uni.upx2px(40) * 2, uni.upx2px(486) * 2, uni.upx2px(276) * 2, uni.upx2px(274) * 2);
|
|
|
} catch (error) {
|
|
|
console.error('树牌背景图片绘制失败:', error);
|
|
|
}
|
|
@@ -274,41 +268,41 @@ export class CanvasUtils {
|
|
|
|
|
|
// 绘制树牌文字
|
|
|
ctx.setFillStyle('#ffffff');
|
|
|
- ctx.font = `bold ${uni.upx2px(36)}px "jiangxizhuokai", Arial, sans-serif`
|
|
|
- ctx.fillText(`【${data.treeName}】`, uni.upx2px(178), uni.upx2px(565))
|
|
|
+ ctx.font = `bold ${uni.upx2px(36) * 2}px "jiangxizhuokai", Arial, sans-serif`
|
|
|
+ ctx.fillText(`【${data.treeName}】`, uni.upx2px(178) * 2, uni.upx2px(565) * 2)
|
|
|
|
|
|
- ctx.font = `bold ${uni.upx2px(18)}px "jiangxizhuokai", Arial, sans-serif`
|
|
|
- ctx.fillText(`${data.userInfo.nickname || data.userInfo.name} ${data.treeObj.year}.${data.treeObj.monthNumber >= 10 ? data.treeObj.monthNumber : "0" + data.treeObj.monthNumber}.${data.treeObj.day}`, uni.upx2px(178), uni.upx2px(610))
|
|
|
+ ctx.font = `bold ${uni.upx2px(18) * 2}px "jiangxizhuokai", Arial, sans-serif`
|
|
|
+ ctx.fillText(`${data.userInfo.nickname || data.userInfo.name} ${data.treeObj.year}.${data.treeObj.monthNumber >= 10 ? data.treeObj.monthNumber : "0" + data.treeObj.monthNumber}.${data.treeObj.day}`, uni.upx2px(178) * 2, uni.upx2px(610) * 2)
|
|
|
|
|
|
// 绘制横线(在水印文字上面)
|
|
|
ctx.setStrokeStyle('#000000'); // 设置线条颜色为黑色
|
|
|
- ctx.setLineWidth(uni.upx2px(2));
|
|
|
+ ctx.setLineWidth(uni.upx2px(2) * 2);
|
|
|
|
|
|
// 计算第一行水印文字的长度
|
|
|
const firstWatermarkText = data.treeObj.watermarkArr[0] || '';
|
|
|
- ctx.font = `bold ${uni.upx2px(24)}px "SweiSpringCJKtc", Arial, sans-serif`;
|
|
|
+ ctx.font = `bold ${uni.upx2px(24) * 2}px "SweiSpringCJKtc", Arial, sans-serif`;
|
|
|
const textMetrics = ctx.measureText(firstWatermarkText);
|
|
|
const watermarkTextWidth = textMetrics.width;
|
|
|
|
|
|
ctx.beginPath();
|
|
|
- ctx.moveTo(uni.upx2px(36), uni.upx2px(820)); // 横线起点,左边距离36rpx
|
|
|
- ctx.lineTo(uni.upx2px(36) + watermarkTextWidth, uni.upx2px(820)); // 横线终点,根据文字长度
|
|
|
+ ctx.moveTo(uni.upx2px(36) * 2, uni.upx2px(820) * 2); // 横线起点,左边距离36rpx
|
|
|
+ ctx.lineTo(uni.upx2px(36) * 2 + watermarkTextWidth, uni.upx2px(820) * 2); // 横线终点,根据文字长度
|
|
|
ctx.stroke();
|
|
|
|
|
|
// 绘制水印文字(根据数组长度动态生成)
|
|
|
ctx.setFillStyle('#000000');
|
|
|
- ctx.font = `bold ${uni.upx2px(24)}px "SweiSpringCJKtc", Arial, sans-serif`
|
|
|
+ ctx.font = `bold ${uni.upx2px(24) * 2}px "SweiSpringCJKtc", Arial, sans-serif`
|
|
|
ctx.setTextAlign('left'); // 设置左对齐,确保第一个字在指定位置
|
|
|
|
|
|
watermarkArr.forEach((text, index) => {
|
|
|
const y = startY + (index * lineHeight);
|
|
|
- ctx.fillText(text, uni.upx2px(36), y);
|
|
|
+ ctx.fillText(text, uni.upx2px(36) * 2, y);
|
|
|
});
|
|
|
|
|
|
// 绘制logo(与最后一条水印文字对齐)
|
|
|
if (downloadedImages.logoUrl) {
|
|
|
try {
|
|
|
- ctx.drawImage(downloadedImages.logoUrl, uni.upx2px(w - -230), logoY, uni.upx2px(76), logoHeight);
|
|
|
+ ctx.drawImage(downloadedImages.logoUrl, uni.upx2px(590) * 2, logoY, uni.upx2px(76) * 2, logoHeight);
|
|
|
} catch (error) {
|
|
|
console.error('logo图片绘制失败:', error);
|
|
|
}
|
|
@@ -316,8 +310,8 @@ export class CanvasUtils {
|
|
|
|
|
|
// 绘制飞鸟有味(与logo保持间距)
|
|
|
ctx.setFillStyle('#000000');
|
|
|
- ctx.font = `bold ${uni.upx2px(22)}px "SweiSpringCJKtc", Arial, sans-serif`
|
|
|
- ctx.fillText(`飞鸟有味`, uni.upx2px(w - -225), brandTextY);
|
|
|
+ ctx.font = `bold ${uni.upx2px(22) * 2}px "SweiSpringCJKtc", Arial, sans-serif`
|
|
|
+ ctx.fillText(`飞鸟有味`, uni.upx2px(580) * 2, brandTextY);
|
|
|
|
|
|
ctx.draw(false, () => {
|
|
|
// 等待canvas渲染完成
|
|
@@ -353,12 +347,12 @@ export class CanvasUtils {
|
|
|
}
|
|
|
|
|
|
// 保存到相册
|
|
|
- saveToAlbum() {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- uni.showLoading({
|
|
|
- title: '正在生成图片...',
|
|
|
- mask: true
|
|
|
- });
|
|
|
+ saveToAlbum() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.showLoading({
|
|
|
+ title: '正在生成海报...',
|
|
|
+ mask: true
|
|
|
+ });
|
|
|
|
|
|
// 先检查canvas是否存在
|
|
|
const query = uni.createSelectorQuery();
|
|
@@ -377,10 +371,10 @@ export class CanvasUtils {
|
|
|
|
|
|
uni.canvasToTempFilePath({
|
|
|
canvasId: this.canvasId,
|
|
|
- width: 690,
|
|
|
- height: 1140, // 固定高度1140rpx
|
|
|
- destWidth: 690,
|
|
|
- destHeight: 1140, // 固定高度1140rpx
|
|
|
+ width: 1380, // 2倍分辨率宽度
|
|
|
+ height: 2280, // 2倍分辨率高度
|
|
|
+ destWidth: 1380, // 2倍分辨率宽度
|
|
|
+ destHeight: 2280, // 2倍分辨率高度
|
|
|
fileType: 'png',
|
|
|
success: (res) => {
|
|
|
uni.hideLoading();
|
|
@@ -390,12 +384,16 @@ export class CanvasUtils {
|
|
|
mask: true
|
|
|
});
|
|
|
|
|
|
+ // 生成带时间戳的海报文件名
|
|
|
+ const timestamp = new Date().getTime();
|
|
|
+ const posterFileName = `海报_${timestamp}.png`;
|
|
|
+
|
|
|
uni.saveImageToPhotosAlbum({
|
|
|
filePath: res.tempFilePath,
|
|
|
success: () => {
|
|
|
uni.hideLoading();
|
|
|
uni.showToast({
|
|
|
- title: '保存成功',
|
|
|
+ title: '海报保存成功',
|
|
|
icon: 'success',
|
|
|
duration: 2000
|
|
|
});
|
|
@@ -465,7 +463,7 @@ export class CanvasUtils {
|
|
|
} catch (error) {
|
|
|
console.error('海报生成和保存失败:', error);
|
|
|
uni.showToast({
|
|
|
- title: error.message || '保存失败',
|
|
|
+ title: error.message || '海报保存失败',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
return false;
|