feat: 绘制基础类重构

This commit is contained in:
R524809
2026-04-10 15:06:11 +08:00
parent a911da6181
commit e74fd3709c
17 changed files with 1091 additions and 278 deletions
+111
View File
@@ -0,0 +1,111 @@
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 = 3;
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';
// topy 为文本顶边;勿用 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);
}
}