feat: 英语字母描红修改

This commit is contained in:
R524809
2026-04-14 17:34:05 +08:00
parent e74fd3709c
commit ec5bc44741
11 changed files with 546 additions and 342 deletions
+21 -17
View File
@@ -258,8 +258,8 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
sheet(
{
id: 'letter-tracing-single',
title: '字母学习页',
desc: '单字母配图、阅读句与四线三格描红',
title: '看图描红',
desc: '单字母配图、例句与六行四线三格描红',
category: 'english',
page: 'letterTracing',
subpackage: 'englishPages',
@@ -280,7 +280,7 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
instructionText: '看一看,读一读,再描一描',
},
generatorConfig: {
mode: 'single-letter',
mode: 'picture-tracing',
letterCase: 'upper',
selectedLetter: 'A',
repetitions: 5,
@@ -291,8 +291,8 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
sheet(
{
id: 'letter-tracing-full',
title: '26 字母描红',
desc: '整页四线三格逐字母描红',
title: '分栏对照',
desc: '半组字母左大写右小写,每行四个',
category: 'english',
page: 'letterTracing',
subpackage: 'englishPages',
@@ -308,11 +308,15 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
template: 'tracing-writing',
generator: 'letter-tracing',
sortOrder: 43,
layoutConfig: { ...L.trace, instructionText: '沿虚线描写字母' },
layoutConfig: {
...L.trace,
instructionText: '左栏大写、右栏小写,逐行对应描红',
},
generatorConfig: {
mode: 'full-alphabet',
letterCase: 'upper',
repetitions: 8,
mode: 'column-compare',
letterCase: 'both',
alphabetHalf: 'A-M',
repetitions: 4,
fadePattern: 'gradient',
},
},
@@ -320,8 +324,8 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
sheet(
{
id: 'letter-tracing-upper-lower',
title: '大小写字母对照',
desc: '上半部大写、下半部小写总览',
title: '字母总览',
desc: 'Uppercase / Lowercase 分区,9+9+8 行',
category: 'english',
page: 'letterTracing',
subpackage: 'englishPages',
@@ -340,10 +344,10 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
layoutConfig: {
...L.trace,
columns: 13,
instructionText: '认大写小写字母',
instructionText: '认读全部大写小写字母',
},
generatorConfig: {
mode: 'upper-lower-split',
mode: 'overview',
letterCase: 'both',
repetitions: 1,
fadePattern: 'first-only',
@@ -353,8 +357,8 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
sheet(
{
id: 'letter-tracing-two-column',
title: '字母描红(两列)',
desc: 'AM / NZ 两列练习',
title: '大小写配对',
desc: 'AM、右 NZ,每行四组大小写',
category: 'english',
page: 'letterTracing',
subpackage: 'englishPages',
@@ -376,8 +380,8 @@ export const ALL_WORKSHEETS: WorksheetDefinition[] = [
instructionText: '沿虚线描写字母',
},
generatorConfig: {
mode: 'two-column',
letterCase: 'upper',
mode: 'case-pairing',
letterCase: 'both',
repetitions: 4,
fadePattern: 'gradient',
},
+1 -1
View File
@@ -14,7 +14,7 @@ import drawHeader from './drawHeader';
* - 打印配置管理
*/
const LINE_COLOR = 'rgba(50, 46, 37, 0.4)';
const LINE_WIDTH = 3;
const LINE_WIDTH = 2;
export class BaseDrawService {
canvas: WechatMiniprogram.Canvas;
+1 -1
View File
@@ -24,7 +24,7 @@ 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 LINE_WIDTH = 2;
const META_COLOR = '#7C766A';
const META_ROW_MARGIN_X = 130;
@@ -1,172 +0,0 @@
import {
ALPHABET_BY_LETTER,
LETTERS_UPPER,
} from '../data/alphabet';
export type LetterTracingMode =
| 'single-letter'
| 'full-alphabet'
| 'upper-lower-split'
| 'two-column';
export type LetterCaseMode = 'upper' | 'lower' | 'both';
export interface LetterTracingGeneratorConfig {
mode: LetterTracingMode;
letterCase: LetterCaseMode;
selectedLetter?: string;
repetitions: number;
fadePattern: 'gradient' | 'first-only';
}
export interface TracingCell {
char: string;
opacity: number;
isGuide: boolean;
}
export interface TracingRow {
displayChar: string;
cells: TracingCell[];
}
export interface TeachingBlock {
upper: string;
lower: string;
word: string;
sentence: string;
emoji: string;
}
export type LetterTracingData =
| {
mode: 'single-letter';
teaching: TeachingBlock;
rows: TracingRow[];
}
| {
mode: 'full-alphabet';
rows: TracingRow[];
}
| {
mode: 'upper-lower-split';
upperGlyphs: string[];
lowerGlyphs: string[];
}
| {
mode: 'two-column';
leftRows: TracingRow[];
rightRows: TracingRow[];
};
function normalizeLetter(c: string): string {
const u = c.trim().toUpperCase();
return u.length ? u[0] : 'A';
}
function buildCells(
char: string,
count: number,
fadePattern: 'gradient' | 'first-only',
): TracingCell[] {
const n = Math.max(1, Math.min(12, count));
const cells: TracingCell[] = [];
for (let i = 0; i < n; i++) {
let opacity: number;
if (fadePattern === 'first-only') {
opacity = i === 0 ? 1 : 0;
} else if (n <= 1) {
opacity = 1;
} else {
opacity = Math.max(0.12, 1 - i / (n - 1));
}
cells.push({ char, opacity, isGuide: i === 0 });
}
return cells;
}
function rowForChar(
ch: string,
repetitions: number,
fadePattern: 'gradient' | 'first-only',
): TracingRow {
return {
displayChar: ch,
cells: buildCells(ch, repetitions, fadePattern),
};
}
export function generateLetterTracing(
config: LetterTracingGeneratorConfig,
): LetterTracingData {
const { mode, letterCase, repetitions, fadePattern } = config;
if (mode === 'single-letter') {
const key = normalizeLetter(config.selectedLetter ?? 'A');
const meta = ALPHABET_BY_LETTER[key] ?? ALPHABET_BY_LETTER.A;
const upper = key;
const lower = key.toLowerCase();
const teaching: TeachingBlock = {
upper,
lower,
word: meta.word,
sentence: `${upper} is for ${meta.word}`,
emoji: meta.emoji,
};
const rows: TracingRow[] = [
rowForChar(upper, repetitions, fadePattern),
rowForChar(lower, repetitions, fadePattern),
];
return { mode: 'single-letter', teaching, rows };
}
if (mode === 'full-alphabet') {
const rows = LETTERS_UPPER.map((L) => {
const ch = letterCase === 'lower' ? L.toLowerCase() : L;
return rowForChar(ch, repetitions, fadePattern);
});
return { mode: 'full-alphabet', rows };
}
if (mode === 'upper-lower-split') {
return {
mode: 'upper-lower-split',
upperGlyphs: [...LETTERS_UPPER],
lowerGlyphs: LETTERS_UPPER.map((L) => L.toLowerCase()),
};
}
// two-column
const left = LETTERS_UPPER.slice(0, 13);
const right = LETTERS_UPPER.slice(13);
const mapChar = (L: string) =>
letterCase === 'lower' ? L.toLowerCase() : L;
const leftRows = left.map((L) =>
rowForChar(mapChar(L), repetitions, fadePattern),
);
const rightRows = right.map((L) =>
rowForChar(mapChar(L), repetitions, fadePattern),
);
return { mode: 'two-column', leftRows, rightRows };
}
/** 与 WorksheetDefinition.generatorConfig 合并后的运行时配置 */
export function mergeLetterTracingConfig(
base: Partial<LetterTracingGeneratorConfig>,
overrides: Partial<LetterTracingGeneratorConfig>,
): LetterTracingGeneratorConfig {
return {
mode: (overrides.mode ?? base.mode ?? 'single-letter') as LetterTracingMode,
letterCase: (overrides.letterCase ??
base.letterCase ??
'upper') as LetterCaseMode,
selectedLetter: overrides.selectedLetter ?? base.selectedLetter,
repetitions: Math.max(
1,
(overrides.repetitions ?? base.repetitions ?? 5) as number,
),
fadePattern: (overrides.fadePattern ??
base.fadePattern ??
'gradient') as 'gradient' | 'first-only',
};
}