feat(diy-recommend-code): 海报颜色布局改为读取 diyData 动态适配
之前 generateSharePoster 颜色和布局都是 hardcode(米黄区、
黄橙渐变、FFC107 等)。后端改 stylePreset 后分享图不会跟随变化。
修改: 抽出 palette 对象,所有视觉参数都从 diyData.value 读:
- bgColor / bgColorStart / bgColorEnd / borderColor
- borderRadius / padding
- titleColor / titleFontSize
- codeColor / codeFontSize
- accentColor / textColor
新增方法:
- _drawPosterBg(ctx, W, H, palette)
优先用 createLinearGradient 画 bgColorStart→bgColorEnd 渐变,
旧 canvas 不支持则 fallback 到 bgColor 单色
- _drawPosterCard(ctx, W, H, palette, stylePreset)
按 stylePreset 区分装饰:minimal/card/horizontal 右上角加 🎁 emoji
gradient 不加装饰
后续如需更多差异,只在这两个方法里扩展即可,不影响主流程。
This commit is contained in:
@@ -1289,66 +1289,88 @@ onShareClick() {
|
|||||||
const v = this.nc;
|
const v = this.nc;
|
||||||
const code = this.code || v.placeholderCode || 'ABCD1234';
|
const code = this.code || v.placeholderCode || 'ABCD1234';
|
||||||
const title = v.title || '我的推荐码';
|
const title = v.title || '我的推荐码';
|
||||||
|
const sp = this.stylePreset;
|
||||||
|
// ★ 所有视觉参数都从 diyData.value 读取,后端可改
|
||||||
|
const palette = {
|
||||||
|
bg: v.bgColor || '#FFFFFF',
|
||||||
|
bgStart: v.bgColorStart, // 渐变起始(可选)
|
||||||
|
bgEnd: v.bgColorEnd, // 渐变结束(可选)
|
||||||
|
border: v.borderColor || '#E5E7EB',
|
||||||
|
borderRadius: Number(v.borderRadius) || 12,
|
||||||
|
padding: Number(v.padding) || 16,
|
||||||
|
titleColor: v.titleColor || '#1A1A1A',
|
||||||
|
titleFontSize: Number(v.titleFontSize) || 18,
|
||||||
|
codeColor: v.codeColor || '#DC143C',
|
||||||
|
codeFontSize: Number(v.codeFontSize) || 36,
|
||||||
|
accent: v.accentColor || '#FFD700',
|
||||||
|
text: v.textColor || '#303133',
|
||||||
|
subtitle: '#999999',
|
||||||
|
codeBlockBg: '#FFF8F0', // 推荐码区背景(米黄)
|
||||||
|
codeBlockBorder: '#FFB74D'
|
||||||
|
};
|
||||||
const W = 375;
|
const W = 375;
|
||||||
const H = 480;
|
const H = 480;
|
||||||
console.log('[推荐码分享] generateSharePoster 进入');
|
console.log('[推荐码分享] generateSharePoster 进入,stylePreset=', sp);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
console.log('[推荐码分享] 准备 createCanvasContext');
|
|
||||||
const ctx = uni.createCanvasContext('sharePosterCanvas', this);
|
const ctx = uni.createCanvasContext('sharePosterCanvas', this);
|
||||||
console.log('[推荐码分享] createCanvasContext 返回:', !!ctx);
|
if (!ctx) return reject(new Error('createCanvasContext 失败'));
|
||||||
if (!ctx) {
|
console.log('[推荐码分享] createCanvasContext 返回');
|
||||||
console.error('[推荐码分享] createCanvasContext 失败');
|
// ====================== 绘制海报 ======================
|
||||||
return reject(new Error('createCanvasContext 失败'));
|
// 1. 整张背景(根据后端 bgColorStart/End 渐变,否则用 bgColor 单色)
|
||||||
}
|
this._drawPosterBg(ctx, W, H, palette);
|
||||||
// 1. 白底
|
// 2. 卡片(根据 stylePreset 选择布局)
|
||||||
ctx.setFillStyle('#FFFFFF');
|
this._drawPosterCard(ctx, W, H, palette, sp);
|
||||||
ctx.fillRect(0, 0, W, H);
|
// 3. 标题(读 titleColor/titleFontSize)
|
||||||
// 2. 顶部标题
|
ctx.setFontSize(palette.titleFontSize);
|
||||||
ctx.setFontSize(18);
|
ctx.setFillStyle(palette.titleColor);
|
||||||
ctx.setFillStyle('#1A1A1A');
|
|
||||||
ctx.setTextAlign('left');
|
ctx.setTextAlign('left');
|
||||||
ctx.fillText(title, 24, 44);
|
ctx.fillText(title, 24, 44);
|
||||||
// 3. 提示文字
|
// 4. 副标题
|
||||||
ctx.setFontSize(12);
|
ctx.setFontSize(12);
|
||||||
ctx.setFillStyle('#999999');
|
ctx.setFillStyle(palette.subtitle);
|
||||||
ctx.fillText('分享给好友,邀请入驻获得奖励', 24, 70);
|
ctx.fillText('分享给好友,邀请入驻获得奖励', 24, 70);
|
||||||
// 4. 米黄背景区
|
// 5. 推荐码区域(读 codeColor/codeFontSize/borderRadius)
|
||||||
ctx.setFillStyle('#FFF8F0');
|
|
||||||
const blockY = 90, blockH = 120, blockX = 24, blockW = W - 48;
|
const blockY = 90, blockH = 120, blockX = 24, blockW = W - 48;
|
||||||
this._roundRect(ctx, blockX, blockY, blockW, blockH, 12);
|
const codeBlockRadius = Math.min(palette.borderRadius, 16);
|
||||||
|
// 推荐码区背景
|
||||||
|
ctx.setFillStyle(palette.codeBlockBg);
|
||||||
|
this._roundRect(ctx, blockX, blockY, blockW, blockH, codeBlockRadius);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
// 5. 推荐码(居中)
|
// 推荐码区边框(浅橙)
|
||||||
ctx.setFontSize(36);
|
ctx.setStrokeStyle(palette.codeBlockBorder);
|
||||||
ctx.setFillStyle(v.codeColor || '#DC143C');
|
ctx.setLineWidth(1);
|
||||||
|
this._roundRect(ctx, blockX, blockY, blockW, blockH, codeBlockRadius);
|
||||||
|
ctx.stroke();
|
||||||
|
// 推荐码大字(居中)
|
||||||
|
ctx.setFontSize(palette.codeFontSize);
|
||||||
|
ctx.setFillStyle(palette.codeColor);
|
||||||
ctx.setTextAlign('center');
|
ctx.setTextAlign('center');
|
||||||
// setFontWeight 是新版 canvas 2d API,旧版不支持;用 font 字符串兜底
|
|
||||||
if (typeof ctx.setFontWeight === 'function') {
|
if (typeof ctx.setFontWeight === 'function') {
|
||||||
ctx.setFontWeight('bold');
|
ctx.setFontWeight('bold');
|
||||||
} else {
|
} else {
|
||||||
try { ctx.font = 'bold 36px sans-serif'; } catch(e) {}
|
try { ctx.font = 'bold ' + palette.codeFontSize + 'px sans-serif'; } catch(e) {}
|
||||||
}
|
}
|
||||||
ctx.fillText(code, W / 2, blockY + blockH / 2 + 12);
|
ctx.fillText(code, W / 2, blockY + blockH / 2 + 12);
|
||||||
// 6. 二维码(如果有 url)
|
// 6. 二维码(如果有)
|
||||||
const qr = this.qrcodeRenderUrl;
|
const qr = this.qrcodeRenderUrl;
|
||||||
console.log('[推荐码分享] qrcodeRenderUrl=', qr);
|
|
||||||
const drawQrcodeAndExport = (qrLocalPath) => {
|
const drawQrcodeAndExport = (qrLocalPath) => {
|
||||||
console.log('[推荐码分享] drawQrcodeAndExport 进入, qrLocalPath=', qrLocalPath);
|
|
||||||
if (qrLocalPath) {
|
if (qrLocalPath) {
|
||||||
const qrSize = 180;
|
const qrSize = 180;
|
||||||
const qrX = (W - qrSize) / 2;
|
const qrX = (W - qrSize) / 2;
|
||||||
const qrY = blockY + blockH + 24;
|
const qrY = blockY + blockH + 24;
|
||||||
|
// 白底框
|
||||||
ctx.setFillStyle('#FFFFFF');
|
ctx.setFillStyle('#FFFFFF');
|
||||||
this._roundRect(ctx, qrX - 6, qrY - 6, qrSize + 12, qrSize + 12, 12);
|
this._roundRect(ctx, qrX - 6, qrY - 6, qrSize + 12, qrSize + 12, codeBlockRadius);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.drawImage(qrLocalPath, qrX, qrY, qrSize, qrSize);
|
ctx.drawImage(qrLocalPath, qrX, qrY, qrSize, qrSize);
|
||||||
|
// 底部提示
|
||||||
ctx.setFontSize(11);
|
ctx.setFontSize(11);
|
||||||
ctx.setFillStyle('#999999');
|
ctx.setFillStyle(palette.subtitle);
|
||||||
ctx.setTextAlign('center');
|
ctx.setTextAlign('center');
|
||||||
ctx.fillText(v.posterQrTip || '扫码进入并填写入驻信息', W / 2, qrY + qrSize + 24);
|
ctx.fillText(v.posterQrTip || '扫码进入并填写入驻信息', W / 2, qrY + qrSize + 24);
|
||||||
}
|
}
|
||||||
console.log('[推荐码分享] 准备 ctx.draw(false, cb)');
|
console.log('[推荐码分享] 准备 ctx.draw(false, cb)');
|
||||||
// 关键: ctx.draw(false, cb) 是异步绘制,必须在 cb 里导出
|
|
||||||
ctx.draw(false, () => {
|
ctx.draw(false, () => {
|
||||||
console.log('[推荐码分享] ctx.draw callback 触发,准备 _exportCanvas');
|
console.log('[推荐码分享] ctx.draw callback 触发,准备 _exportCanvas');
|
||||||
this._exportCanvas(W, H).then(resolve).catch(reject);
|
this._exportCanvas(W, H).then(resolve).catch(reject);
|
||||||
@@ -1365,6 +1387,56 @@ onShareClick() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 海报背景:根据 diyData.bgColorStart/End 画渐变,否则用 bgColor 单色
|
||||||
|
*/
|
||||||
|
_drawPosterBg(ctx, W, H, palette) {
|
||||||
|
try {
|
||||||
|
// 尝试线性渐变(可能旧 canvas 不支持,需 try/catch)
|
||||||
|
if (palette.bgStart && palette.bgEnd && typeof ctx.createLinearGradient === 'function') {
|
||||||
|
const grad = ctx.createLinearGradient(0, 0, W, H);
|
||||||
|
grad.addColorStop(0, palette.bgStart);
|
||||||
|
grad.addColorStop(1, palette.bgEnd);
|
||||||
|
ctx.setFillStyle(grad);
|
||||||
|
} else {
|
||||||
|
ctx.setFillStyle(palette.bg);
|
||||||
|
}
|
||||||
|
ctx.fillRect(0, 0, W, H);
|
||||||
|
} catch (e) {
|
||||||
|
ctx.setFillStyle(palette.bg);
|
||||||
|
ctx.fillRect(0, 0, W, H);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 卡片绘制:根据 stylePreset 选不同视觉布局
|
||||||
|
* (虽然现在 canvas 限制很多,但保留 hook 以便后续扩展)
|
||||||
|
*/
|
||||||
|
_drawPosterCard(ctx, W, H, palette, sp) {
|
||||||
|
// 海报本身就是"卡片",这里可以做不同预设的装饰差异
|
||||||
|
// 当前:所有预设统一用单色/渐变背景 + 米黄推荐码区
|
||||||
|
// 后续如需 minimal 加 🎁 装饰 emoji 等,在这里按 sp 区分
|
||||||
|
try {
|
||||||
|
if (sp === 'minimal') {
|
||||||
|
// minimal 风格:右上角加 🎁 emoji 装饰(模仿组件)
|
||||||
|
ctx.setFontSize(40);
|
||||||
|
ctx.setFillStyle('rgba(255,107,107,0.25)');
|
||||||
|
ctx.setTextAlign('right');
|
||||||
|
ctx.fillText('🎁', W - 24, 60);
|
||||||
|
} else if (sp === 'card') {
|
||||||
|
// card 风格:右上角加 🎁
|
||||||
|
ctx.setFontSize(36);
|
||||||
|
ctx.setFillStyle('rgba(218,165,32,0.35)');
|
||||||
|
ctx.setTextAlign('right');
|
||||||
|
ctx.fillText('🎁', W - 24, 60);
|
||||||
|
} else if (sp === 'horizontal') {
|
||||||
|
// horizontal 风格:右上角加 🎁
|
||||||
|
ctx.setFontSize(36);
|
||||||
|
ctx.setFillStyle('rgba(184,160,138,0.35)');
|
||||||
|
ctx.setTextAlign('right');
|
||||||
|
ctx.fillText('🎁', W - 24, 60);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* canvas 转图片(uni.canvasToTempFilePath Promise 化)
|
* canvas 转图片(uni.canvasToTempFilePath Promise 化)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user