processImage: async function(imgPath)
{
let tempFilePath = '';
if (imgPath.startsWith('http'))
{
const downloadRes = await uni.downloadFile({ url: imgPath });
if (downloadRes.statusCode !== 200) throw new Error(`图片下载失败:${downloadRes.statusCode}`);
tempFilePath = downloadRes.tempFilePath;
}
else
{
tempFilePath = imgPath;
}
// 1. 获取图片信息(网络图片先转临时路径)
const { width: imgW, height: imgH } = await uni.getImageInfo({ src: tempFilePath });
// 2. 获取画布节点并初始化
const dpr = await uni.getWindowInfo().pixelRatio || 1;
const canvas = await new Promise((resolve, reject) =>
{
const timer = setTimeout(() => reject(new Error('获取画布节点超时')), 3000); // 增加超时处理,避免发布版获取失败
uni.createSelectorQuery().in(this).select('#shareCanvas').fields({ node: true, size: true }).exec(res => resolve(res[0].node));
})
const ctx = canvas.getContext('2d');
canvas.width = this.canvasWidth * dpr;
canvas.height = this.canvasHeight * dpr;
ctx.scale(dpr, dpr);
if (!canvas || !ctx) throw new Error('获取画布失败');
// 绘制背景(整个画布先填充白色)
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
this.roundRect(ctx, 0, 0, this.canvasWidth, this.canvasHeight, this.canvasRadius);
ctx.clip(); // 设置裁剪,后续所有绘制都限制在这个圆角区域内
// 3. 计算缩放比例和裁剪偏移(核心修复:先缩放图片,再计算居中裁剪)
// 计算等比例缩放比例(保证图片覆盖画布)
const scale = Math.max(this.canvasWidth / imgW, this.canvasHeight / imgH); // 取较大比例保证覆盖画布
// 缩放后的图片尺寸
const scaledW = imgW * scale;
const scaledH = imgH * scale;
// 居中裁剪偏移量
const offsetX = (scaledW - this.canvasWidth) / 2;
const offsetY = (scaledH - this.canvasHeight) / 2;
// 4. 绘制图片到画布
await new Promise((resolve, reject) =>
{
const image = canvas.createImage();
image.crossOrigin = 'anonymous';
image.src = tempFilePath;
image.onload = () =>
{
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
ctx.drawImage(
image, // 图片对象
0, 0, // 原图上的裁剪起始坐标
imgW, imgH, // 原图上裁剪的宽度/高度
-offsetX, -offsetY, // 画布上的绘制起始坐标
scaledW, scaledH // 画布上的绘制宽度/高度
);
resolve();
};
image.onerror = (err) => reject(new Error('图片加载失败' + err.message));
});
// 绘制底部白色背景条
ctx.fillStyle = '#fff';
ctx.fillRect(0, this.canvasHeight - this.bottomBarHeight, this.canvasWidth, this.bottomBarHeight);
// 标题(灰色小字)
ctx.fillStyle = '#666666';
ctx.font = '14px sans-serif';
ctx.fillText(this.info.name, 12, this.canvasHeight - this.bottomBarHeight + 46);
// 价格(红色)
ctx.fillStyle = '#ff4444';
ctx.font = 'bold 20px sans-serif';
ctx.fillText(`¥${this.info.online_price}`, 12, this.canvasHeight - this.bottomBarHeight + 26);
// “立即购买”按钮
const btnWidth = 100;
const btnHeight = 36;
const btnX = this.canvasWidth - btnWidth - 12;
const btnY = this.canvasHeight - this.bottomBarHeight + (this.bottomBarHeight - btnHeight) / 2;
ctx.fillStyle = '#ff6b35';
this.roundRect(ctx, btnX, btnY, btnWidth, btnHeight, 18);
ctx.fill();
// 按钮文字(白色)
ctx.fillStyle = '#ffffff';
ctx.font = '14px sans-serif';
const btnText = '立即购买';
const textMetrics = ctx.measureText(btnText);
const textX = btnX + (btnWidth - textMetrics.width) / 2;
const textY = btnY + btnHeight / 2 + 5;
ctx.fillText(btnText, textX, textY);
// 绘制边框(核心新增逻辑)
const borderWidth = 5; // 边框粗细(px)
const borderColor = '#ff6b35'; // 边框颜色(浅灰色)
ctx.strokeStyle = borderColor; // 边框颜色
ctx.lineWidth = borderWidth; // 边框粗细
ctx.lineJoin = 'round'; // 边框拐角圆角过渡
// 绘制圆角边框(位置和背景对齐)
this.roundRect(ctx, borderWidth / 2, borderWidth / 2, this.canvasWidth - borderWidth, this.canvasHeight - borderWidth, 0);
ctx.stroke();
// 导出画布为图片
const canvasRes = await new Promise((resolve, reject) =>
{
uni.canvasToTempFilePath(
{
canvas: canvas,
x: 0,
y: 0,
width: this.canvasWidth,
height: this.canvasHeight,
destWidth: this.canvasWidth * dpr,
destHeight: this.canvasHeight * dpr,
quality: 1.0,
success: res => resolve(res),
fail: err => reject(err)
}, this);
})
uni.getImageInfo(
{
src: canvasRes.tempFilePath,
success: (res) =>
{
console.log('导出的图片信息', res)
}
})
return canvasRes.tempFilePath;
},
