mrr.sj.front/components/ShareCanvas/ShareCanvas.vue

459 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="ShareCanvas">
<view class="canvas">
<canvas canvas-id="shareCanvas" />
</view>
</view>
</template>
<script>
export default {
name: 'ShareCanvas',
methods: {
setRadius(ctx, r, x, y, width, height) {
ctx.beginPath();
// 依次绘制四个圆角的弧
ctx.moveTo(x + r, y);
ctx.arcTo(x + width, y, x + width, y + height, r);
ctx.arcTo(x + width, y + height, x, y + height, r);
ctx.arcTo(x, y + height, x, y, r);
ctx.arcTo(x, y, x + width, y, r);
ctx.closePath();
},
// 订单分享
setOrderCanvas(info) {
return new Promise(async (resolve, reject) => {
console.log('订单分享-info', info);
try {
const ctx = uni.createCanvasContext('shareCanvas', this)
// 绘制背景图
// ctx.setFillStyle('#19C161')
// ctx.fillRect(0, 0, 211, 170) // 保证宽高比是 5:4
const bgImageUrl = "../../static/images/shop/pintuan/shareBj.png";
try {
// 2. 异步获取图片信息利用已封装的getImge方法确保图片加载完成
const { path: bgImagePath } = await this.getImge(bgImageUrl);
// 3. 绘制背景图参数依次为「图片路径、x坐标、y坐标、宽度、高度」
// 宽度211、高度170 与Canvas尺寸一致确保背景铺满
ctx.drawImage(bgImagePath, 0, 0, 211, 170);
} catch (bgError) {
console.error("背景图加载/绘制失败:", bgError);
// 可选:背景图失败时降级用原纯色填充,避免画布空白
ctx.setFillStyle('#19C161');
ctx.fillRect(0, 0, 211, 170);
}
// 绘制文本信息
// ctx.setFontSize(21);
// ctx.setTextAlign('left')
// ctx.setFillStyle('#FFFFFF')
// ctx.fillText('我买好啦!', 9, 32)
// 浅绿色背景
// this.setRadius(ctx, 10, 106, 11, 97, 27) // 加圆角
// ctx.setFillStyle('#EFF9F1')
// ctx.fill()
// ctx.fillRect(106, 11, 97, 27) // x, y, width, height
// ctx.setFontSize(14);
// ctx.setTextAlign('center')
// ctx.setFillStyle('#19C161')
// ctx.fillText('跟团号:' + info.followNum, 155, 30)
this.setRadius(ctx, 3, 9, 49, 194, 106)
ctx.setFillStyle('#FFFFFF')
ctx.fill()
// ctx.fillRect(9, 49, 194, 106) // 不设置圆角的时候这么画有背景色的矩形
// 画商品图
ctx.save();
this.setRadius(ctx, 5, 17, 59, 85, 85)
ctx.clip();//画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.orderCartInfos[0].productImg)
ctx.drawImage(path, 17, 59, 85, 85)
} catch (error) {
console.error(error);
}
ctx.restore();
if (info.teamLeaderUser && info.teamLeaderUser.avatar) {
// 团长头像
// ctx.save();
// this.setRadius(ctx, 5, 110, 60, 25, 25)
// ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
// try {
// const { path } = await this.getImge(info.teamLeaderUser.avatar)
// ctx.drawImage(path, 110, 60, 25, 25)
// } catch (error) {
// console.error(error);
// }
// ctx.restore();
// // ctx.setFillStyle(grd)
// // // ctx.fillRect(230/2, 218/2, 149/2, 53/2) // x, y, width, height
// // ctx.fill()
// ctx.setFontSize(12);
// ctx.setTextAlign('center')
// ctx.setFillStyle('#FFFFFF')
// ctx.fillText('立即成团 >', 152, 135)
}
if (info.teamLeaderUser && info.teamLeaderUser.nickname) {
// 团长昵称
ctx.setFontSize(12);
ctx.setTextAlign('left')
ctx.setFillStyle('rgba(253, 218, 226, 1)')
ctx.fillText(info.teamLeaderUser.nickname.length > 4 ? info.teamLeaderUser.nickname.slice(0, 4) + '...' : info.teamLeaderUser.nickname, 140, 76)
}
ctx.setFontSize(14);
ctx.setTextAlign('center')
ctx.setFillStyle('#FB7415')
ctx.fillText(`¥${info.orderCartInfos[0].unitPrice}`, 152, 105)
this.setRadius(ctx, 10, 115, 118, 75, 26)
const grd = ctx.createLinearGradient(115, 118, 115, 144)
grd.addColorStop(0, '#FDAC2F')
grd.addColorStop(0.5, '#FDA72C')
grd.addColorStop(1, '#FB5615')
// 橙色按钮背景
ctx.setFillStyle(grd)
// ctx.fillRect(230/2, 218/2, 149/2, 53/2) // x, y, width, height
ctx.fill()
ctx.setFontSize(12);
ctx.setTextAlign('center')
ctx.setFillStyle('#FFFFFF')
ctx.fillText('立即成团 >', 152, 135)
ctx.draw(false, (() => {
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: 'shareCanvas',
success: (res) => {
return resolve(res.tempFilePath)
},
fail: function (error) {
console.log('fail----fail', error);
//TODO
return reject(error)
}
}, this)
}, 500);
}))
} catch (error) {
console.log('画图失败error', error);
return reject(error)
}
})
},
// 商品分享
setGoodsShareCanvas(info) {
console.log('商品分享--info', info);
return new Promise(async (resolve, reject) => {
try {
const ctx = uni.createCanvasContext('shareCanvas', this)
// 绘制背景图
ctx.setFillStyle('#FFFFFF')
ctx.fillRect(0, 0, 211, 170)
// 团长头像
ctx.save();
this.setRadius(ctx, 5, 0, 0, 30, 30)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.avatar)
ctx.drawImage(path, 0, 0, 30, 30)
} catch (error) {
console.error(error);
}
ctx.restore();
// 团长昵称
ctx.setFontSize(12);
ctx.setTextAlign('left')
ctx.setFillStyle('#96999B')
ctx.fillText(info.nickname.length > 11 ? info.nickname.slice(0, 11) + `${info.pinkId ? '...的团' : '...'}` : info.nickname + '的团', 35, 18)
// 商品1图
ctx.save();
this.setRadius(ctx, 3, 0, 35, 211, 211)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.image)
ctx.drawImage(path, 0, 35, 211, 211)
} catch (error) {
console.error(error);
}
ctx.restore();
// 绿色背景
ctx.setFillStyle('#19C161')
ctx.fillRect(0, 130, 131, 40) // x, y, width, height
ctx.setFontSize(16);
ctx.setTextAlign('center')
ctx.setFillStyle('#FFFFFF')
ctx.fillText(`¥${info.pinkId ? info.pinkPrice : info.price}`, 65, 130 + 18)
ctx.setFontSize(12);
ctx.setTextAlign('center')
ctx.setFillStyle('#FFFFFF')
ctx.fillText(`¥${info.otPrice}`, 65, 130 + 34)
// 划线
ctx.beginPath()
ctx.setLineWidth(1);
ctx.setStrokeStyle('#FFFFFF')
ctx.moveTo(40, 130 + 30)
ctx.lineTo(90, 130 + 30)
ctx.stroke()
// 深绿色背景
ctx.setFillStyle('#19AF5C')
ctx.fillRect(131, 130, 211 - 131, 40)
if (info.pinkId) {
// 立即跟团按钮
ctx.setFontSize(16);
ctx.setTextAlign('center')
ctx.setFillStyle('#FFFFFF')
ctx.fillText(`立即跟团`, 131 + (211 - 131) / 2, 130 + 26)
} else {
// 已团数量
ctx.setFontSize(12);
ctx.setTextAlign('center')
ctx.setFillStyle('#FFFFFF')
ctx.fillText(`已团 ${this.getSales(info.sales)}`, 131 + (211 - 131) / 2, 130 + 25)
}
ctx.draw(false, (() => {
uni.canvasToTempFilePath({
canvasId: 'shareCanvas',
success: (res) => {
return resolve(res.tempFilePath)
},
fail: function (error) {
console.log('fail----fail', error);
//TODO
return reject(error)
}
}, this)
}))
} catch (error) {
uni.hideLoading()
console.log('画图失败error', error);
return reject(error)
}
})
},
// 团分享
setGroupShareCanvas(info) {
console.log('团分享-info', info);
return new Promise(async (resolve, reject) => {
try {
const ctx = uni.createCanvasContext('shareCanvas', this)
// 绘制背景图
ctx.setFillStyle('#FFFFFF')
ctx.fillRect(0, 0, 211, 170)
if (info.productImageList.length == 1) {
// 团长头像
ctx.save();
this.setRadius(ctx, 5, 0, 0, 30, 30)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.avatar)
ctx.drawImage(path, 0, 0, 30, 30)
} catch (error) {
console.error(error);
}
ctx.restore();
// 团长昵称
ctx.setFontSize(12);
ctx.setTextAlign('left')
ctx.setFillStyle('#96999B')
ctx.fillText((info.nickname.length > 11 ? info.nickname.slice(0, 11) + '...' : info.nickname) + '的团', 35, 18)
// 商品1图
ctx.save();
this.setRadius(ctx, 3, 0, 35, 211, 211)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.productImageList[0])
ctx.drawImage(path, 0, 35, 211, 211)
} catch (error) {
console.error(error);
}
ctx.restore();
}
if (info.productImageList.length >= 2) {
// 团长头像
ctx.save();
this.setRadius(ctx, 5, 0, 0, 42, 42)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.avatar)
ctx.drawImage(path, 0, 0, 42, 42)
} catch (error) {
console.error(error);
}
ctx.restore();
// 团长昵称
ctx.setFontSize(12);
ctx.setTextAlign('left')
ctx.setFillStyle('#96999B')
ctx.fillText((info.nickname.length > 10 ? info.nickname.slice(0, 10) + '...' : info.nickname) + '的团', 47, 25)
// 商品1图
ctx.save();
this.setRadius(ctx, 3, 0, 51, 69, 69)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.productImageList[0])
ctx.drawImage(path, 0, 51, 69, 69)
} catch (error) {
console.error(error);
}
ctx.restore();
// 商品2图
ctx.save();
this.setRadius(ctx, 3, 69 + 2, 51, 69, 69)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.productImageList[1])
ctx.drawImage(path, 69 + 2, 51, 69, 69)
} catch (error) {
console.error(error);
}
ctx.restore();
if (info.productImageList.length >= 3) {
// 商品3图
ctx.save();
this.setRadius(ctx, 3, 69 * 2 + 4, 51, 69, 69)
ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
try {
const { path } = await this.getImge(info.productImageList[2])
ctx.drawImage(path, 69 * 2 + 4, 51, 69, 69)
} catch (error) {
console.error(error);
}
ctx.restore();
}
}
// 绿色背景
ctx.setFillStyle('#19C161')
ctx.fillRect(0, 128, 211, 42) // x, y, width, height
ctx.setFontSize(16);
ctx.setTextAlign('center')
ctx.setFillStyle('#FFFFFF')
ctx.fillText(`立即跟团`, 211 / 2, 128 + 26)
ctx.draw(false, (() => {
uni.canvasToTempFilePath({
canvasId: 'shareCanvas',
success: (res) => {
return resolve(res.tempFilePath)
},
fail: function (error) {
console.log('fail----fail', error);
//TODO
return reject(error)
}
}, this)
}))
} catch (error) {
console.log('画图失败error', error);
return reject(error)
}
})
},
/**
* 设置圆角矩形
*
* @param ctx 绘图上下文
* @param cornerRadius 圆角半径
* @param width 矩形宽度
* @param height 矩形高度
* @param x 矩形左上角的 x 坐标
* @param y 矩形左上角的 y 坐标
* @returns 无返回值
*/
setRadius(ctx, cornerRadius, x, y, width, height) {
// 开始绘制路径
ctx.beginPath();
// 绘制最左侧的圆角
ctx.arc(x + cornerRadius, y + cornerRadius, cornerRadius, Math.PI, Math.PI * 1.5);
// 绘制顶部边缘
ctx.moveTo(x + cornerRadius, y);
ctx.lineTo(x + width - cornerRadius, y);
ctx.lineTo(x + width, y + cornerRadius);
// 绘制最右侧的圆角
ctx.arc(x + width - cornerRadius, y + cornerRadius, cornerRadius, Math.PI * 1.5, Math.PI * 2);
// 绘制右侧边缘
ctx.lineTo(x + width, y + height - cornerRadius);
ctx.lineTo(x + width - cornerRadius, y + height);
// 绘制最下侧的圆角
ctx.arc(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0, Math.PI * 0.5);
// 绘制底部边缘
ctx.lineTo(x + cornerRadius, y + height);
ctx.lineTo(x, y + height - cornerRadius);
// 绘制最左侧的圆角
ctx.arc(x + cornerRadius, y + height - cornerRadius, cornerRadius, Math.PI * 0.5, Math.PI);
// 绘制左侧边缘
ctx.lineTo(x, y + cornerRadius);
ctx.lineTo(x + cornerRadius, y);
// 闭合路径
ctx.closePath();
},
// 获取图片地址
getImge(path) {
// 利用promise异步转同步否则可能显示不了
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: path,
success: function (res) {
if (res && res.path) {
resolve(res)
} else {
reject(false)
}
},
fail: function (res) {
reject(res)
}
})
})
},
getSales(sales) {
return sales >= 10000 ? sales / 10000 + 'w+' : sales
},
}
}
</script>
<style lang="scss" scoped>
// 隐藏画布
.ShareCanvas {
position: absolute;
top: -200px;
z-index: -1;
opacity: 0;
.canvas canvas {
width: 211px;
height: 170px; // +16
}
}
</style>