112 lines
3.1 KiB
TypeScript
112 lines
3.1 KiB
TypeScript
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<void> {
|
||
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);
|
||
}
|
||
}
|