feat: 英文字母描红开发

This commit is contained in:
R524809
2026-04-03 15:21:04 +08:00
parent 206c44cf6d
commit 1ef6770810
37 changed files with 1812 additions and 179 deletions
@@ -0,0 +1,197 @@
import LetterTracingDraw from '../shared/service/letterTracingDraw';
import { createPage, type CanvasDataState } from '../../base/pageMixin';
import { defaultShareConfig } from '../../config/config';
import { getWorksheetById } from '../../core/data/worksheets';
import {
generateLetterTracing,
mergeLetterTracingConfig,
type LetterTracingGeneratorConfig,
type LetterTracingMode,
} from '../../core/generators/letter-tracing-generator';
import { LETTERS_UPPER } from '../../core/data/alphabet';
const PAGE_META: Record<string, { title: string; desc: string }> = {
'letter-tracing-single': {
title: '字母学习页',
desc: '单字母配图与描红',
},
'letter-tracing-full': {
title: '26 字母描红',
desc: '整页字母书写练习',
},
'letter-tracing-upper-lower': {
title: '大小写字母对照',
desc: '一页认识 A-Z 与 a-z',
},
'letter-tracing-two-column': {
title: '字母描红(两列)',
desc: 'AM / NZ 两列练习',
},
};
function pageInfoLookup(id: string) {
return PAGE_META[id];
}
const MODE_OPTIONS = [
{ id: 'letter-tracing-single', label: '单字母学习页' },
{ id: 'letter-tracing-full', label: '整页描红' },
{ id: 'letter-tracing-upper-lower', label: '大小写对照' },
{ id: 'letter-tracing-two-column', label: '两列描红' },
] as const;
type PageData = CanvasDataState & {
worksheetId: string;
traceMode: LetterTracingMode;
selectedLetter: string;
letterCaseLower: boolean;
showLetterPicker: boolean;
showCaseToggle: boolean;
showNextLetter: boolean;
letterGrid: string[];
modeOptions: ReadonlyArray<{ id: string; label: string }>;
};
createPage(
{
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
drawService: null as LetterTracingDraw | null,
data: {
pageTitle: '字母描红',
functionId: '',
hasContent: false,
showShareDialog: false,
worksheetId: 'letter-tracing-single',
traceMode: 'single-letter',
selectedLetter: 'A',
letterCaseLower: false,
showLetterPicker: true,
showCaseToggle: false,
showNextLetter: true,
letterGrid: LETTERS_UPPER,
modeOptions: [...MODE_OPTIONS],
} as unknown as PageData,
onLoad(options: { id?: string }) {
const worksheetId =
options.id && PAGE_META[options.id]
? options.id
: 'letter-tracing-single';
const def = getWorksheetById(worksheetId);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const merged = mergeLetterTracingConfig(base, {});
const traceMode = merged.mode;
const showLetterPicker = traceMode === 'single-letter';
const showCaseToggle =
traceMode === 'full-alphabet' || traceMode === 'two-column';
const showNextLetter = traceMode === 'single-letter';
this.setData({
worksheetId,
functionId: worksheetId,
traceMode,
selectedLetter: normalizePick(merged.selectedLetter),
letterCaseLower: merged.letterCase === 'lower',
showLetterPicker,
showCaseToggle,
showNextLetter,
});
this.initPageInfo(worksheetId, PAGE_META[worksheetId]?.title ?? '字母描红');
},
onSelectMode(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string | undefined;
if (!id || id === this.data.worksheetId) return;
wx.redirectTo({
url: `/englishPages/letterTracing/letterTracing?id=${id}`,
});
},
onReady() {
this.initCanvas({
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, unknown>,
) => new LetterTracingDraw(canvas, ctx, options),
drawServiceOptions: {
title: this.data.pageTitle,
subTitle: this.data.subTitle || '',
},
onCanvasReady: () => {
this.drawCanvas();
},
});
},
buildRuntimeConfig(): LetterTracingGeneratorConfig {
const def = getWorksheetById(this.data.worksheetId);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const letterCase =
this.data.traceMode === 'single-letter'
? (base.letterCase ?? 'upper')
: this.data.letterCaseLower
? 'lower'
: 'upper';
return mergeLetterTracingConfig(base, {
selectedLetter: this.data.selectedLetter,
letterCase,
});
},
async drawCanvas() {
if (!this.ctx || !this.drawService) return;
try {
const config = this.buildRuntimeConfig();
const data = generateLetterTracing(config);
await (this.drawService as LetterTracingDraw).draw(data);
this.setData({ hasContent: true });
} catch (e) {
console.error('letterTracing draw failed', e);
this.setData({ hasContent: false });
}
},
onSelectLetter(e: WechatMiniprogram.TouchEvent) {
const letter = e.currentTarget.dataset.letter as string;
if (!letter) return;
this.setData({ selectedLetter: letter });
this.drawCanvas();
},
onSelectCaseUpper() {
this.setData({ letterCaseLower: false });
this.drawCanvas();
},
onSelectCaseLower() {
this.setData({ letterCaseLower: true });
this.drawCanvas();
},
onNextLetter() {
const list = LETTERS_UPPER;
const i = list.indexOf(this.data.selectedLetter);
const next = list[(i + 1) % list.length];
this.setData({ selectedLetter: next });
this.drawCanvas();
},
},
{
shareConfig: defaultShareConfig,
pageInfoLookup,
},
);
function normalizePick(c?: string): string {
if (!c || c.length === 0) return 'A';
return c.trim().toUpperCase().charAt(0);
}