feat: 开发完成拼音每日一练

This commit is contained in:
R524809
2026-05-20 15:33:06 +08:00
parent cab4e9c33f
commit d52339c0fa
25 changed files with 1478 additions and 428 deletions
@@ -15,6 +15,18 @@ import {
drawLetterInFourLineGrid,
loadLetterFont,
} from '../../shared/draw/drawTools';
import {
SECTION_PADDING_TOP,
SECTION_PADDING_BOTTOM,
HEADER_TITLE_GAP,
HEADER_META_GAP,
CONTENT_TOP_GAP,
FOOTER_PHRASE_GAP,
FOOTER_INDEX_GAP,
drawSectionHeader,
drawSectionFooter,
getRandomEncouragementPhrase,
} from '../../../core/draw/dailyPracticeDrawHelper';
type DailyCheckinData = Extract<
LetterTracingData,
@@ -27,22 +39,6 @@ const PAGE_MARGIN_X = 14;
const PAGE_MARGIN_Y = 14;
// 每个分区内容区左右内边距(px)
const SECTION_PADDING_X = 18;
// 每个分区内容区顶部内边距(px)
const SECTION_PADDING_TOP = 12;
// 每个分区内容区底部内边距(px)
const SECTION_PADDING_BOTTOM = 10;
// 标题与上一元素的垂直间距(px)
const HEADER_TITLE_GAP = 32;
// 标题与元信息(如日期、学号等)的间距(px)
const HEADER_META_GAP = 22;
// 正文内容距标题/元信息顶部的间距(px)
const CONTENT_TOP_GAP = 14;
// 页脚短语与内容之间的间距(px)
const FOOTER_PHRASE_GAP = 26;
// 页脚索引与短语之间的间距(px)
const FOOTER_INDEX_GAP = 22;
// 单个格子区列数
const GRID_COLS = 5;
@@ -146,70 +142,12 @@ export default class DailyCheckinDraw extends BaseDrawService {
) {
if (!section) return;
this.drawSectionHeader(rect);
this.drawSectionFooter(rect, section.pageNumber);
const phrase = getRandomEncouragementPhrase();
drawSectionHeader(this.ctx, rect, '字母每日打卡');
drawSectionFooter(this.ctx, rect, phrase, `- ${section.pageNumber} -`);
this.drawSectionGrid(rect, section.rows);
}
private drawSectionHeader(rect: {
x: number;
y: number;
w: number;
h: number;
}) {
const cx = rect.x + rect.w / 2;
const titleY = rect.y + SECTION_PADDING_TOP + 12;
const metaY = titleY + HEADER_TITLE_GAP;
const metaUnderlineY = metaY + 5;
this.ctx.save();
this.ctx.fillStyle = '#322E25';
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.font = '20px serif';
this.ctx.fillText('每日打卡', cx, titleY);
this.ctx.font = '12px serif';
this.ctx.fillText('月', rect.x + 76, metaY);
this.ctx.fillText('日', rect.x + 122, metaY);
this.ctx.fillText('姓名:', rect.x + 176, metaY);
this.ctx.strokeStyle = '#8E897E';
this.ctx.lineWidth = 1;
this.ctx.setLineDash([]);
this.ctx.beginPath();
this.ctx.moveTo(rect.x + 40, metaUnderlineY);
this.ctx.lineTo(rect.x + 68, metaUnderlineY);
this.ctx.moveTo(rect.x + 85, metaUnderlineY);
this.ctx.lineTo(rect.x + 113, metaUnderlineY);
this.ctx.moveTo(rect.x + 195, metaUnderlineY);
this.ctx.lineTo(rect.x + 248, metaUnderlineY);
this.ctx.stroke();
this.ctx.restore();
}
private drawSectionFooter(
rect: { x: number; y: number; w: number; h: number },
pageNumber: number,
) {
const cx = rect.x + rect.w / 2;
const phraseY =
rect.y + rect.h - SECTION_PADDING_BOTTOM - FOOTER_PHRASE_GAP;
const indexY = rect.y + rect.h - SECTION_PADDING_BOTTOM - 6;
this.ctx.save();
this.ctx.fillStyle = '#3E3A33';
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.font = '14px serif';
this.ctx.fillText('日拱一卒 不期而至', cx, phraseY);
this.ctx.font = '12px serif';
this.ctx.fillText(`- ${pageNumber} -`, cx, indexY);
this.ctx.restore();
}
private drawSectionGrid(
rect: { x: number; y: number; w: number; h: number },
rows: DailyCheckinRow[],