179 lines
5.6 KiB
TypeScript
179 lines
5.6 KiB
TypeScript
import NumberObjectMatchDraw from '../shared/service/numberObjectMatchDraw';
|
||
import {
|
||
createMathPage,
|
||
CanvasDataState,
|
||
} from '../shared/common/mathPageMixin';
|
||
import { NumberObjectMatchData } from '../shared/service/numberObjectMatchDraw';
|
||
|
||
createMathPage({
|
||
canvas: null as Canvas | null,
|
||
ctx: null as RenderingContext | null,
|
||
boxHeight: 0,
|
||
boxWidth: 0,
|
||
drawService: null as NumberObjectMatchDraw | null,
|
||
matchData: null as NumberObjectMatchData | null,
|
||
|
||
data: {
|
||
pageTitle: '数物对应',
|
||
functionId: '',
|
||
hasContent: false,
|
||
showShareDialog: false,
|
||
showTypeSelector: true, // 控制是否显示类型选择器
|
||
currentMode: 'match', // 'match' 连线 或 'fill' 填写
|
||
currentModeName: '连线',
|
||
typeActions: [
|
||
{ name: '连线', value: 'match' },
|
||
{ name: '填写', value: 'fill' },
|
||
],
|
||
} as CanvasDataState & {
|
||
showTypeSelector: boolean;
|
||
currentMode: string;
|
||
currentModeName: string;
|
||
typeActions: Array<{ name: string; value: string }>;
|
||
},
|
||
|
||
onLoad(options: { id?: string; mode?: string }) {
|
||
const functionId = options.id || 'number-object-match';
|
||
this.initPageInfo(functionId, '数物对应');
|
||
const isFill = functionId === 'number-object-fill';
|
||
const currentMode = options.mode || (isFill ? 'fill' : 'match');
|
||
this.setData({
|
||
currentMode,
|
||
currentModeName: currentMode === 'fill' ? '填写' : '连线',
|
||
typeActions: [
|
||
{ name: '连线', value: 'match' },
|
||
{ name: '填写', value: 'fill' },
|
||
],
|
||
});
|
||
},
|
||
|
||
onReady() {
|
||
this.initCanvas({
|
||
createDrawService: (
|
||
canvas: Canvas,
|
||
ctx: RenderingContext,
|
||
options?: Record<string, any>,
|
||
) => {
|
||
return new NumberObjectMatchDraw(canvas, ctx, options);
|
||
},
|
||
drawServiceOptions: {},
|
||
onCanvasReady: () => {
|
||
// 初始随机生成
|
||
this.onRandom();
|
||
},
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 绘制Canvas内容
|
||
*/
|
||
async drawCanvas() {
|
||
if (!this.ctx || !this.drawService || !this.matchData) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await this.drawService.draw(this.matchData, this.data.currentMode);
|
||
this.setData({ hasContent: true });
|
||
} catch (error) {
|
||
console.error('绘制失败:', error);
|
||
this.setData({ hasContent: false });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 随机生成
|
||
*/
|
||
onRandom() {
|
||
// 从两个目录中随机选择4张不重复的图片
|
||
// fruits: 1-22, twelve-animals: 1-12
|
||
const fruitsIndices = Array.from({ length: 22 }, (_, i) => i + 1);
|
||
const animalsIndices = Array.from({ length: 12 }, (_, i) => i + 1);
|
||
|
||
// 合并所有可用的图片索引
|
||
const allImages: Array<{
|
||
imageIndex: number;
|
||
folder: 'fruits' | 'twelve-animals';
|
||
}> = [
|
||
...fruitsIndices.map((idx) => ({
|
||
imageIndex: idx,
|
||
folder: 'fruits' as const,
|
||
})),
|
||
...animalsIndices.map((idx) => ({
|
||
imageIndex: idx,
|
||
folder: 'twelve-animals' as const,
|
||
})),
|
||
];
|
||
|
||
// 随机打乱并选择4张不重复的图片
|
||
const shuffled = [...allImages].sort(() => Math.random() - 0.5);
|
||
const selectedImages = shuffled.slice(0, 4);
|
||
|
||
// 生成上面区域的16张图片(4行4列)
|
||
// 每张图片随机出现,并添加随机偏移
|
||
const gridImages: Array<{
|
||
imageIndex: number;
|
||
folder: 'fruits' | 'twelve-animals';
|
||
offsetX: number;
|
||
offsetY: number;
|
||
}> = [];
|
||
|
||
const maxOffset = 15; // 最大偏移量
|
||
for (let i = 0; i < 20; i++) {
|
||
// 从选中的4张图片中随机选择一张
|
||
const randomImage =
|
||
selectedImages[
|
||
Math.floor(Math.random() * selectedImages.length)
|
||
];
|
||
|
||
// 生成随机偏移(-maxOffset 到 maxOffset)
|
||
const offsetX = (Math.random() - 0.5) * 2 * maxOffset;
|
||
const offsetY = (Math.random() - 0.5) * 2 * maxOffset;
|
||
|
||
gridImages.push({
|
||
...randomImage,
|
||
offsetX,
|
||
offsetY,
|
||
});
|
||
}
|
||
|
||
// 下面区域第一行的4张图片(使用选中的4张图片)
|
||
const bottomImages = [...selectedImages];
|
||
|
||
// 计算每个图片在上面区域出现的次数
|
||
const imageCountMap = new Map<string, number>();
|
||
gridImages.forEach((item) => {
|
||
const key = `${item.folder}-${item.imageIndex}`;
|
||
imageCountMap.set(key, (imageCountMap.get(key) || 0) + 1);
|
||
});
|
||
|
||
// 获取每个图片的数量(按 selectedImages 的顺序)
|
||
const imageCounts = selectedImages.map((img) => {
|
||
const key = `${img.folder}-${img.imageIndex}`;
|
||
return imageCountMap.get(key) || 0;
|
||
});
|
||
|
||
// 将数量乱序排列
|
||
const bottomNumbers = [...imageCounts].sort(() => Math.random() - 0.5);
|
||
|
||
this.matchData = {
|
||
gridImages,
|
||
bottomImages,
|
||
bottomNumbers,
|
||
};
|
||
|
||
this.drawCanvas();
|
||
},
|
||
|
||
/** 选择类型 */
|
||
onSelectType(event: any) {
|
||
const { name, value } = event.detail;
|
||
this.setData({
|
||
currentMode: value,
|
||
currentModeName: name,
|
||
});
|
||
// 重新生成数据
|
||
this.onRandom();
|
||
},
|
||
});
|