import { getMiniCodeImage } from '../../utils/index'; /** * 统一打印纸页眉(与 UI 稿一致:左上 Logo、居中主标题、姓名/日期/得分、底部分割线) */ interface DrawBaseHeaderParams { canvas: WechatMiniprogram.Canvas; ctx: RenderingContext; canvasWidth: number; options: { /** 练习纸主标题,如「数一数,连一连」 */ title: string; }; onHeaderDrawn?: (currentY: number) => void; } const MARGIN_X = 24; const LOGO_SIZE = 68; const LOGO_X = MARGIN_X; const LOGO_Y = 14; const TITLE_FONT = 'bold 20px "Microsoft Yahei"'; const META_FONT = '14px "Microsoft Yahei"'; const META_FONT_PX = 12; const META_UNDERLINE_GAP = 3; const TITLE_COLOR = '#322E25'; const LINE_COLOR = 'rgba(50, 46, 37, 0.4)'; const LINE_WIDTH = 2; const META_COLOR = '#7C766A'; const META_ROW_MARGIN_X = 130; function drawMetaField( ctx: RenderingContext, label: string, x: number, y: number, underlineRight: number, ) { ctx.font = META_FONT; ctx.fillStyle = META_COLOR; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; ctx.fillText(label, x, y); const metrics = ctx.measureText(label); const labelW = metrics.width; // textBaseline=top 时 y 是字顶,不是垂直中心;下划线应从字底开始(约一行字高,与 META_FONT 字号一致) const lineY = y + META_FONT_PX + META_UNDERLINE_GAP; ctx.strokeStyle = '#E5DCC9'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(x + labelW + 2, lineY); ctx.lineTo(Math.max(x + labelW + 2, underlineRight), lineY); ctx.stroke(); } export default async function drawHeader({ canvas, ctx, canvasWidth, options, onHeaderDrawn, }: DrawBaseHeaderParams): Promise { const { title } = options; const image = await getMiniCodeImage(canvas); ctx.drawImage(image, LOGO_X, LOGO_Y, LOGO_SIZE, LOGO_SIZE); ctx.font = TITLE_FONT; ctx.fillStyle = TITLE_COLOR; ctx.textAlign = 'center'; // top:y 为文本顶边;勿用 alphabetic,否则 y 是基线,大字会大量画在 y 上方导致裁出纸上沿 ctx.textBaseline = 'top'; const titleY = 22; ctx.fillText(title, canvasWidth / 2, titleY); const metaRowY = 65; const innerLeft = META_ROW_MARGIN_X; const innerRight = canvasWidth - META_ROW_MARGIN_X; const innerW = innerRight - innerLeft; const colW = innerW / 3; drawMetaField(ctx, '姓名:', innerLeft + 4, metaRowY, innerLeft + colW - 8); drawMetaField( ctx, '日期:', innerLeft + colW + 4, metaRowY, innerLeft + 2 * colW - 8, ); drawMetaField( ctx, '得分:', innerLeft + 2 * colW + 4, metaRowY, innerRight - 4, ); ctx.save(); const separatorY = 96; ctx.strokeStyle = LINE_COLOR; ctx.lineWidth = LINE_WIDTH; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(MARGIN_X, separatorY); ctx.lineTo(canvasWidth - MARGIN_X, separatorY); ctx.stroke(); ctx.restore(); const contentStartY = separatorY + 14; if (onHeaderDrawn) { onHeaderDrawn(contentStartY); } }