style(fe): 照护卡改证件版式 — 大字「X星人地球移民/居民身份卡」左对齐
- 去掉蓝色标题条,照护卡整体换淡紫底 - 标题两行左对齐:小字带字距「汪星人/喵星人/宠星人地球移民」+ 大字「居民身份卡」 - 按物种动态取字:狗→汪星人、猫→喵星人、其它→宠星人 - 发证机构→签发机构、有效期→有效期限;小程序码落到右下角淡紫底上 - 新增 spacedText 逐字字距工具 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -213,72 +213,67 @@ Page({
|
||||
// 正面印章(右上角)
|
||||
this.drawStamp(ctx, fx + cardW2 - 78, fY + 82);
|
||||
|
||||
// ══════════ 照护卡(白 + 蓝)══════════
|
||||
const bY = fY + fH + 40, bH = 566;
|
||||
// ══════════ 照护卡(淡紫,仿证件版式)══════════
|
||||
const bY = fY + fH + 40, bH = 630;
|
||||
const gbc = ctx.createLinearGradient(fx, bY, fx, bY + bH);
|
||||
gbc.addColorStop(0, '#E6E7FB');
|
||||
gbc.addColorStop(1, '#F2F0FA');
|
||||
this.roundRect(ctx, fx, bY, cardW2, bH, 30);
|
||||
ctx.fillStyle = '#FFFFFF';
|
||||
ctx.fillStyle = gbc;
|
||||
ctx.fill();
|
||||
|
||||
// 蓝色标题条(两行:「X星地球移民 / 居民身份卡」)
|
||||
// 大标题(两行左对齐):小字「X星人地球移民」+ 大字「居民身份卡」
|
||||
// 汪星人=狗、喵星人=猫,其它兜底宠星人
|
||||
const star = c.species === '狗狗' ? '汪' : c.species === '猫猫' ? '喵' : '宠';
|
||||
const hb = 92;
|
||||
const gb = ctx.createLinearGradient(fx, bY, fx + cardW2, bY);
|
||||
gb.addColorStop(0, '#5AA0E8');
|
||||
gb.addColorStop(1, '#7FB6F0');
|
||||
this.roundRect(ctx, fx + 24, bY + 24, cardW2 - 48, hb, 18);
|
||||
ctx.fillStyle = gb;
|
||||
ctx.fill();
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.font = '700 30px sans-serif';
|
||||
ctx.fillText(star + '星地球移民', W / 2, bY + 24 + 40);
|
||||
ctx.font = '700 26px sans-serif';
|
||||
ctx.fillText('居民身份卡', W / 2, bY + 24 + 76);
|
||||
ctx.textAlign = 'left';
|
||||
ctx.fillStyle = '#8A90AE';
|
||||
ctx.font = '600 27px sans-serif';
|
||||
this.spacedText(ctx, star + '星人地球移民', fx + 44, bY + 82, 8);
|
||||
ctx.fillStyle = '#3E4A63';
|
||||
ctx.font = '800 66px sans-serif';
|
||||
ctx.fillText('居民身份卡', fx + 44, bY + 168);
|
||||
|
||||
// 照护行(标签 + 值,不放图标)
|
||||
// 资料行(标签 + 值)
|
||||
const rowX = fx + 44;
|
||||
const kvB = (label, value, yy) => {
|
||||
ctx.textAlign = 'left';
|
||||
ctx.font = '24px sans-serif';
|
||||
ctx.fillStyle = '#8D8277';
|
||||
ctx.font = '25px sans-serif';
|
||||
ctx.fillStyle = '#9A9FB5';
|
||||
ctx.fillText(label, rowX, yy);
|
||||
ctx.font = '600 25px sans-serif';
|
||||
ctx.fillStyle = '#2D2925';
|
||||
ctx.fillText(value || '—', rowX + 150, yy);
|
||||
ctx.font = '600 26px sans-serif';
|
||||
ctx.fillStyle = '#3E4A63';
|
||||
ctx.fillText(value || '—', rowX + 160, yy);
|
||||
};
|
||||
let ry = bY + 140;
|
||||
kvB('主人', c.owner, ry); ry += 48;
|
||||
kvB('联系电话', c.phone || '未验证', ry); ry += 48;
|
||||
kvB('备注', c.notes || '无', ry); ry += 48;
|
||||
kvB('发证机构', c.org || '肉垫计划', ry); ry += 48;
|
||||
kvB('有效期', c.validity || '永久有效', ry); ry += 42;
|
||||
let ry = bY + 244;
|
||||
kvB('主人', c.owner, ry); ry += 52;
|
||||
kvB('联系电话', c.phone || '未验证', ry); ry += 52;
|
||||
kvB('备注', c.notes || '无', ry); ry += 52;
|
||||
kvB('签发机构', c.org || '肉垫计划', ry); ry += 52;
|
||||
kvB('有效期限', c.validity || '永久有效', ry);
|
||||
|
||||
// 走失联系框 + 小程序码
|
||||
const lostY = ry + 16, lostH = bY + bH - lostY - 30;
|
||||
this.roundRect(ctx, fx + 24, lostY, cardW2 - 48, lostH, 18);
|
||||
ctx.fillStyle = '#EAF3FE';
|
||||
ctx.fill();
|
||||
ctx.textAlign = 'left';
|
||||
ctx.fillStyle = '#5B7A9C';
|
||||
ctx.font = '22px sans-serif';
|
||||
ctx.fillText('微信扫一扫,查看' + (c.name || '') + '的身份信息', fx + 44, lostY + lostH / 2 + 8);
|
||||
// 小程序码
|
||||
const qrD = 118, qrX = fx + cardW2 - 24 - 20 - qrD, qrY = lostY + (lostH - qrD) / 2;
|
||||
// 小程序码(右下角,直接落在淡紫底上)
|
||||
const qrD = 128, qrX = fx + cardW2 - 44 - qrD, qrY = bY + bH - 40 - qrD;
|
||||
ctx.fillStyle = '#fff';
|
||||
this.roundRect(ctx, qrX, qrY, qrD, qrD, 12);
|
||||
this.roundRect(ctx, qrX, qrY, qrD, qrD, 14);
|
||||
ctx.fill();
|
||||
if (qr) {
|
||||
ctx.drawImage(qr, qrX + 8, qrY + 8, qrD - 16, qrD - 16);
|
||||
ctx.drawImage(qr, qrX + 9, qrY + 9, qrD - 18, qrD - 18);
|
||||
} else {
|
||||
ctx.fillStyle = '#9CB4CC';
|
||||
ctx.fillStyle = '#A7ABC4';
|
||||
ctx.font = '18px sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('小程序码', qrX + qrD / 2, qrY + qrD / 2 - 2);
|
||||
ctx.fillText('待发布', qrX + qrD / 2, qrY + qrD / 2 + 24);
|
||||
}
|
||||
// 扫码说明(左侧,和小程序码同高)
|
||||
ctx.textAlign = 'left';
|
||||
ctx.fillStyle = '#8A90AE';
|
||||
ctx.font = '22px sans-serif';
|
||||
ctx.fillText('微信扫一扫', fx + 44, qrY + qrD / 2 - 6);
|
||||
ctx.fillText('查看' + (c.name || '') + '的身份信息', fx + 44, qrY + qrD / 2 + 28);
|
||||
|
||||
// 背面印章(右上角,压在蓝条右侧)
|
||||
this.drawStamp(ctx, fx + cardW2 - 78, bY + 70);
|
||||
// 背面印章(右上角)
|
||||
this.drawStamp(ctx, fx + cardW2 - 84, bY + 92);
|
||||
|
||||
// 只导出到实际内容底部,避免卡片下面拖一条空白
|
||||
const dpr = this._dpr || 2;
|
||||
@@ -310,6 +305,15 @@ Page({
|
||||
ctx.closePath();
|
||||
},
|
||||
|
||||
// 逐字加字距地画一行(canvas 的 letterSpacing 各端支持不一,手动排最稳)
|
||||
spacedText(ctx, text, x, y, gap) {
|
||||
let cx = x;
|
||||
for (const ch of text) {
|
||||
ctx.fillText(ch, cx, y);
|
||||
cx += ctx.measureText(ch).width + gap;
|
||||
}
|
||||
},
|
||||
|
||||
// 五角星路径(调用方负责 fill/stroke)
|
||||
star(ctx, cx, cy, rO, rI, n) {
|
||||
ctx.beginPath();
|
||||
|
||||
Reference in New Issue
Block a user