Files
doodle-mini/miniprogram/englishPages/letterTracing/draw/casePairingDraw.ts
T
2026-04-21 15:03:38 +08:00

131 lines
3.9 KiB
TypeScript

import { BaseDrawService } from '../../../core/draw/baseDraw';
import type {
LetterTracingData,
TracingRow,
} from '../generators/letter-tracing-generator';
import { fontFamilyOf } from '../../shared/data/fontProfiles';
import {
calcLetterFontSizes,
drawFourLineGrid,
drawLetterInFourLineGrid,
FOUR_LINE_GRID_H,
type LetterFontSizes,
} from '../../shared/draw/drawTools';
const COLOR_BLACK = '#1a1a1a';
const COLOR_LIGHT_RED = '#FF8E8E';
/** 左右大写/小写两栏之间的间距(相对行宽 + 下限,避免过窄屏过小) */
const COLUMN_GUTTER_RATIO = 0.02;
const COLUMN_GUTTER_MIN = 14;
/**
* 单栏内字母排版区域占栏宽比例(<1 则同栏字母更紧凑、字间距更小)
*/
const INTRA_COLUMN_LETTER_BAND_RATIO = 1;
type CasePairingData = Extract<
LetterTracingData,
{ mode: 'letter-tracing-case-pairing' }
>;
/**
* 分栏对照绘制:
* 四线三格横跨整页宽度,字母分左右两侧书写——
* 左栏大写、右栏小写(各 repetitions 个),中间留间距;同栏内字母略收紧。
* 每侧第一个字母黑色,后续浅红色。
*/
export default class CasePairingDraw extends BaseDrawService {
private letterSizes: LetterFontSizes =
calcLetterFontSizes(FOUR_LINE_GRID_H);
async draw(data: CasePairingData) {
this.prepareDraw();
await this.drawHeaderAndDivider();
this.drawContent(data.leftRows, data.rightRows);
this.drawPrintFooter();
}
private drawContent(leftRows: TracingRow[], rightRows: TracingRow[]) {
const M = 24;
const lineW = this.canvasWidth - M * 2;
const maxR = Math.max(leftRows.length, rightRows.length);
const footerReserve = 56;
const availableH =
this.canvasHeight - this.currentY - footerReserve - 12;
const rowGap = Math.max(
4,
(availableH - maxR * FOUR_LINE_GRID_H) / (maxR + 1),
);
const y0 = this.currentY + rowGap;
const columnGutter = Math.max(
COLUMN_GUTTER_MIN,
Math.round(lineW * COLUMN_GUTTER_RATIO),
);
const halfContentW = (lineW - columnGutter) / 2;
const leftX = M;
const rightX = M + halfContentW + columnGutter;
for (let i = 0; i < maxR; i++) {
const y = y0 + i * (FOUR_LINE_GRID_H + rowGap);
drawFourLineGrid(this.ctx, M, y, lineW, FOUR_LINE_GRID_H);
if (i < leftRows.length) {
this.drawLettersInHalf(
leftRows[i],
leftX,
y,
halfContentW,
INTRA_COLUMN_LETTER_BAND_RATIO,
);
}
if (i < rightRows.length) {
this.drawLettersInHalf(
rightRows[i],
rightX,
y,
halfContentW,
INTRA_COLUMN_LETTER_BAND_RATIO,
);
}
}
}
/**
* 在四线三格的半侧区域内绘制字母。
* 第一个字母黑色,后续字母浅红色。
*/
private drawLettersInHalf(
row: TracingRow,
x: number,
y: number,
columnW: number,
letterBandRatio: number,
) {
const n = row.cells.length;
if (n === 0) return;
const bandW = columnW * letterBandRatio;
const inset = (columnW - bandW) / 2;
const slotW = bandW / n;
for (let i = 0; i < n; i++) {
const cell = row.cells[i];
const color = i === 0 ? COLOR_BLACK : COLOR_LIGHT_RED;
const cx = x + inset + slotW * i + slotW / 2;
drawLetterInFourLineGrid(
this.ctx,
cell.char,
cx,
y,
FOUR_LINE_GRID_H,
this.letterSizes,
{ fontFamily: fontFamilyOf(this.letterSizes._profile), color },
);
}
}
}