106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
|
import type {
|
|
LetterTracingData,
|
|
TracingRow,
|
|
} from '../generators/letter-tracing-generator';
|
|
import { drawFourLineGrid } from './drawTools';
|
|
|
|
const LETTER_FACE =
|
|
'Helvetica, Arial, "Segoe UI", Roboto, sans-serif';
|
|
|
|
type ColumnCompareData = Extract<
|
|
LetterTracingData,
|
|
{ mode: 'column-compare' }
|
|
>;
|
|
|
|
export default class ColumnCompareDraw extends BaseDrawService {
|
|
async draw(data: ColumnCompareData) {
|
|
this.prepareDraw();
|
|
await this.drawHeaderAndDivider();
|
|
this.drawContent(data.leftRows, data.rightRows);
|
|
this.drawPrintFooter();
|
|
}
|
|
|
|
private drawContent(leftRows: TracingRow[], rightRows: TracingRow[]) {
|
|
const M = 24;
|
|
const y0 = this.currentY + 6;
|
|
const bottom = this.canvasHeight - M;
|
|
const maxR = Math.max(leftRows.length, rightRows.length);
|
|
const rowH = Math.max(36, (bottom - y0) / maxR - 6);
|
|
const gapCol = 16;
|
|
const colW = (this.canvasWidth - M * 2 - gapCol) / 2;
|
|
|
|
for (let i = 0; i < leftRows.length; i++) {
|
|
const y = y0 + i * (rowH + 6);
|
|
this.drawTracingRow(leftRows[i], M, y, rowH, colW);
|
|
}
|
|
const xR = M + colW + gapCol;
|
|
for (let i = 0; i < rightRows.length; i++) {
|
|
const y = y0 + i * (rowH + 6);
|
|
this.drawTracingRow(rightRows[i], xR, y, rowH, colW);
|
|
}
|
|
}
|
|
|
|
private drawTracingRow(
|
|
row: TracingRow,
|
|
startX: number,
|
|
y: number,
|
|
rowH: number,
|
|
totalWidth: number,
|
|
) {
|
|
const gap = 4;
|
|
const n = row.cells.length;
|
|
const cellW = n > 0 ? (totalWidth - gap * (n - 1)) / n : totalWidth;
|
|
for (let i = 0; i < n; i++) {
|
|
const cx = startX + i * (cellW + gap);
|
|
drawFourLineGrid(this.ctx, cx, y, cellW, rowH);
|
|
const cell = row.cells[i];
|
|
this.drawCharInCell(
|
|
cell.char,
|
|
cx,
|
|
y,
|
|
cellW,
|
|
rowH,
|
|
cell.opacity,
|
|
cell.isGuide,
|
|
cell.pairChar,
|
|
);
|
|
}
|
|
}
|
|
|
|
private drawCharInCell(
|
|
ch: string,
|
|
x: number,
|
|
y: number,
|
|
w: number,
|
|
h: number,
|
|
opacity: number,
|
|
bold: boolean,
|
|
pairChar?: string,
|
|
) {
|
|
const ctx = this.ctx;
|
|
if (opacity <= 0) return;
|
|
ctx.save();
|
|
ctx.globalAlpha = opacity;
|
|
ctx.fillStyle = bold ? '#1a1a1a' : '#4a4a4a';
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'alphabetic';
|
|
const baselineY = y + h * 0.72;
|
|
|
|
if (pairChar) {
|
|
const fs = Math.min(h * 0.5, w * 0.22);
|
|
ctx.font = `${bold ? 'bold ' : ''}${fs}px ${LETTER_FACE}`;
|
|
const gap = fs * 0.2;
|
|
const totalW = fs * 2 + gap;
|
|
const x0 = x + w / 2 - totalW / 2;
|
|
ctx.fillText(ch, x0 + fs * 0.5, baselineY);
|
|
ctx.fillText(pairChar, x0 + fs * 1.5 + gap, baselineY);
|
|
} else {
|
|
const fontSize = Math.min(h * 0.72, w * 0.85);
|
|
ctx.font = `${bold ? 'bold ' : ''}${fontSize}px ${LETTER_FACE}`;
|
|
ctx.fillText(ch, x + w / 2, baselineY);
|
|
}
|
|
ctx.restore();
|
|
}
|
|
}
|