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

198 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}