style(fe): 身份卡按参考图收拾 — 去标题/正反面标签/疫苗,卡高按内容自适应
- 去掉顶部「宠物身份卡」大标题和「· 正面 ·/· 背面 ·」灰字,只留两张卡 - 正面卡高度按性格/介绍有无动态算,消掉中间大块空白 - 性格标签为空时整段不画 - 照护卡去掉「疫苗情况」行 - 导出只裁到实际内容底部,卡片下面不再拖空白 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,7 @@ Page({
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
dpr = 2;
|
dpr = 2;
|
||||||
}
|
}
|
||||||
|
this._dpr = dpr;
|
||||||
node.width = W * dpr;
|
node.width = W * dpr;
|
||||||
node.height = H * dpr;
|
node.height = H * dpr;
|
||||||
const ctx = node.getContext('2d');
|
const ctx = node.getContext('2d');
|
||||||
@@ -93,18 +94,14 @@ Page({
|
|||||||
const cardW = W - pad * 2;
|
const cardW = W - pad * 2;
|
||||||
|
|
||||||
const fx = pad, cardW2 = cardW;
|
const fx = pad, cardW2 = cardW;
|
||||||
|
const tags = (c.personality || '').split(',').filter(Boolean);
|
||||||
// 顶部标题
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillStyle = '#8A5A18';
|
|
||||||
ctx.font = '700 40px sans-serif';
|
|
||||||
ctx.fillText('🐾 宠物身份卡 🐾', W / 2, 66);
|
|
||||||
|
|
||||||
// ══════════ 正面卡(暖黄)══════════
|
// ══════════ 正面卡(暖黄)══════════
|
||||||
ctx.fillStyle = '#C7A36A';
|
// 高度按内容算,避免留一大块空白
|
||||||
ctx.font = '22px sans-serif';
|
let fH = 420;
|
||||||
ctx.fillText('· 正面 ·', W / 2, 116);
|
if (tags.length) fH += 90;
|
||||||
const fY = 140, fH = 700;
|
if (c.intro) fH += 90;
|
||||||
|
const fY = pad;
|
||||||
const gf = ctx.createLinearGradient(fx, fY, fx, fY + fH);
|
const gf = ctx.createLinearGradient(fx, fY, fx, fY + fH);
|
||||||
gf.addColorStop(0, '#FFF7E0');
|
gf.addColorStop(0, '#FFF7E0');
|
||||||
gf.addColorStop(1, '#FFE7AE');
|
gf.addColorStop(1, '#FFE7AE');
|
||||||
@@ -186,56 +183,53 @@ Page({
|
|||||||
kvF('毛色', c.color, avY + 168);
|
kvF('毛色', c.color, avY + 168);
|
||||||
kvF('年龄', c.age, avY + 206);
|
kvF('年龄', c.age, avY + 206);
|
||||||
|
|
||||||
// 性格标签
|
// 性格标签(没有就不画,避免留空行)
|
||||||
let ty = avY + avD + 34;
|
let ty = fY + 320;
|
||||||
ctx.textAlign = 'left';
|
if (tags.length) {
|
||||||
ctx.fillStyle = '#8A5A18';
|
|
||||||
ctx.font = '600 24px sans-serif';
|
|
||||||
ctx.fillText('🐾 性格标签', fx + 34, ty);
|
|
||||||
const tags = (c.personality || '').split(',').filter(Boolean);
|
|
||||||
const tagColors = [['#FFE7A8', '#8A5A18'], ['#D6E9FF', '#2E6DA8'], ['#FFD9DE', '#C24A63']];
|
|
||||||
let tx = fx + 34;
|
|
||||||
ty += 42;
|
|
||||||
tags.forEach((t, i) => {
|
|
||||||
ctx.font = '23px sans-serif';
|
|
||||||
const tw = ctx.measureText(t).width + 32;
|
|
||||||
const col = tagColors[i % tagColors.length];
|
|
||||||
ctx.fillStyle = col[0];
|
|
||||||
this.roundRect(ctx, tx, ty - 28, tw, 40, 20);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.fillStyle = col[1];
|
|
||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
ctx.fillText(t, tx + 16, ty);
|
ctx.fillStyle = '#8A5A18';
|
||||||
tx += tw + 14;
|
ctx.font = '600 24px sans-serif';
|
||||||
});
|
ctx.fillText('🐾 性格标签', fx + 34, ty);
|
||||||
|
ty += 44;
|
||||||
|
const tagColors = [['#FFE7A8', '#8A5A18'], ['#D6E9FF', '#2E6DA8'], ['#FFD9DE', '#C24A63']];
|
||||||
|
let tx = fx + 34;
|
||||||
|
tags.forEach((t, i) => {
|
||||||
|
ctx.font = '23px sans-serif';
|
||||||
|
const tw = ctx.measureText(t).width + 32;
|
||||||
|
const col = tagColors[i % tagColors.length];
|
||||||
|
ctx.fillStyle = col[0];
|
||||||
|
this.roundRect(ctx, tx, ty - 28, tw, 40, 20);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.fillStyle = col[1];
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.fillText(t, tx + 16, ty);
|
||||||
|
tx += tw + 14;
|
||||||
|
});
|
||||||
|
ty += 46;
|
||||||
|
}
|
||||||
|
|
||||||
// 介绍 quote
|
// 介绍 quote
|
||||||
if (c.intro) {
|
if (c.intro) {
|
||||||
const qy = ty + 30;
|
|
||||||
ctx.fillStyle = 'rgba(255,255,255,.6)';
|
ctx.fillStyle = 'rgba(255,255,255,.6)';
|
||||||
this.roundRect(ctx, fx + 34, qy, cardW2 - 68, 60, 16);
|
this.roundRect(ctx, fx + 34, ty, cardW2 - 68, 60, 16);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.fillStyle = '#7A5A2E';
|
ctx.fillStyle = '#7A5A2E';
|
||||||
ctx.font = '25px sans-serif';
|
ctx.font = '25px sans-serif';
|
||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
ctx.fillText('“ ' + c.intro + ' ”', fx + 54, qy + 40);
|
ctx.fillText('“ ' + c.intro + ' ”', fx + 54, ty + 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 底部:身份ID + 一起生活天数
|
// 底部:身份ID + 一起生活天数(贴卡片底边)
|
||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
ctx.fillStyle = '#9C7A3E';
|
ctx.fillStyle = '#9C7A3E';
|
||||||
ctx.font = '22px sans-serif';
|
ctx.font = '22px sans-serif';
|
||||||
ctx.fillText('物ID ' + (c.id_no || ''), fx + 34, fY + fH - 60);
|
ctx.fillText('物ID ' + (c.id_no || ''), fx + 34, fY + fH - 56);
|
||||||
if (c.days > 0) {
|
if (c.days > 0) {
|
||||||
ctx.fillText('和主人一起生活的第 ' + c.days + ' 天', fx + 34, fY + fH - 26);
|
ctx.fillText('和主人一起生活的第 ' + c.days + ' 天', fx + 34, fY + fH - 22);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════ 背面卡(白 + 蓝)══════════
|
// ══════════ 照护卡(白 + 蓝)══════════
|
||||||
const bY = fY + fH + 46, bH = 620;
|
const bY = fY + fH + 40, bH = 566;
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillStyle = '#C7A36A';
|
|
||||||
ctx.font = '22px sans-serif';
|
|
||||||
ctx.fillText('· 背面 ·', W / 2, bY - 16);
|
|
||||||
this.roundRect(ctx, fx, bY, cardW2, bH, 30);
|
this.roundRect(ctx, fx, bY, cardW2, bH, 30);
|
||||||
ctx.fillStyle = '#FFFFFF';
|
ctx.fillStyle = '#FFFFFF';
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
@@ -267,12 +261,11 @@ Page({
|
|||||||
ctx.fillText(value || '—', rowX + 170, yy);
|
ctx.fillText(value || '—', rowX + 170, yy);
|
||||||
};
|
};
|
||||||
let ry = bY + 140;
|
let ry = bY + 140;
|
||||||
kvB('👤', '主人', c.owner, ry); ry += 46;
|
kvB('👤', '主人', c.owner, ry); ry += 48;
|
||||||
kvB('📞', '联系电话', c.phone || '未验证', ry); ry += 46;
|
kvB('📞', '联系电话', c.phone || '未验证', ry); ry += 48;
|
||||||
kvB('💉', '疫苗情况', c.vaccine_state, ry); ry += 46;
|
kvB('📝', '备注', c.notes || '无', ry); ry += 48;
|
||||||
kvB('📝', '备注', c.notes || '无', ry); ry += 46;
|
kvB('🏛', '发证机构', c.org || '肉垫计划', ry); ry += 48;
|
||||||
kvB('🏛', '发证机构', c.org || '肉垫计划', ry); ry += 46;
|
kvB('📅', '有效期', c.validity || '永久有效', ry); ry += 42;
|
||||||
kvB('📅', '有效期', c.validity || '永久有效', ry); ry += 40;
|
|
||||||
|
|
||||||
// 走失联系框 + 小程序码
|
// 走失联系框 + 小程序码
|
||||||
const lostY = ry + 16, lostH = bY + bH - lostY - 30;
|
const lostY = ry + 16, lostH = bY + bH - lostY - 30;
|
||||||
@@ -302,9 +295,18 @@ Page({
|
|||||||
ctx.fillText('待发布', qrX + qrD / 2, qrY + qrD / 2 + 24);
|
ctx.fillText('待发布', qrX + qrD / 2, qrY + qrD / 2 + 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 只导出到实际内容底部,避免卡片下面拖一条空白
|
||||||
|
const dpr = this._dpr || 2;
|
||||||
|
const contentH = bY + bH + pad;
|
||||||
wx.canvasToTempFilePath(
|
wx.canvasToTempFilePath(
|
||||||
{
|
{
|
||||||
canvas: node,
|
canvas: node,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: W,
|
||||||
|
height: contentH,
|
||||||
|
destWidth: W * dpr,
|
||||||
|
destHeight: contentH * dpr,
|
||||||
fileType: 'png',
|
fileType: 'png',
|
||||||
success: (r) => this.setData({ cardImg: r.tempFilePath }),
|
success: (r) => this.setData({ cardImg: r.tempFilePath }),
|
||||||
fail: () => wx.showToast({ title: '生成失败,稍后再试', icon: 'none' }),
|
fail: () => wx.showToast({ title: '生成失败,稍后再试', icon: 'none' }),
|
||||||
|
|||||||
Reference in New Issue
Block a user