Files
doodle-mini/miniprogram/focusPages/gridDrawing/gridDrawing.ts
T
2025-12-12 17:19:36 +08:00

251 lines
7.9 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 GridDraw, { GridGroup } from '../shared/service/gridDraw';
import { createFocusPage } from '../shared/common/focusPageMixin';
import { GridDrawingData } from '../shared/service/gridDraw';
import { generateCompletePattern } from '../shared/utils/gridUtils';
import { ALL_3X3_SHAPES } from '../shared/shapes/gridShapes3x3';
import { ALL_5X5_SHAPES } from '../shared/shapes/gridShapes5x5';
import { ALL_7X7_SHAPES } from '../shared/shapes/gridShapes7x7';
import { ShapeTemplate, GridCell } from '../shared/types/gridTypes';
import { GridConfig } from '../shared/types/gridTypes';
createFocusPage({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
drawService: null as GridDraw | null,
gridData: null as GridDrawingData | null,
data: {
pageTitle: '格子仿画',
functionId: '',
hasContent: false,
showShareDialog: false,
currentMode: '3x3', // '3x3', '5x5', '7x7'
currentModeName: '3×3',
typeActions: [
{ name: '3×3', value: '3x3' },
{ name: '5×5', value: '5x5' },
{ name: '7×7', value: '7x7' },
],
},
onLoad(options: { id?: string }) {
const functionId = options.id || 'grid-drawing';
this.setData({
functionId,
});
this.initPageInfo(functionId, '格子仿画');
},
onReady() {
this.initCanvas({
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
return new GridDraw(canvas, ctx, options);
},
drawServiceOptions: {
subTitle: '在网格中填充颜色,形成各种形状',
functionId: this.data.functionId,
},
onCanvasReady: () => {
// 初始随机生成
this.onRandom();
},
});
},
/**
* 绘制Canvas内容
*/
async drawCanvas() {
if (!this.ctx || !this.drawService || !this.gridData) {
return;
}
try {
// 更新 Header 的 Title
if (this.drawService) {
this.drawService.options.title = this.data.currentModeName;
}
await this.drawService.draw(this.gridData);
this.setData({ hasContent: true });
} catch (error) {
console.error('绘制失败:', error);
this.setData({ hasContent: false });
}
},
/**
* 检查两个图案是否相同(通过比较格子的位置和颜色)
*/
arePatternsEqual(cells1: GridCell[], cells2: GridCell[]): boolean {
if (cells1.length !== cells2.length) {
return false;
}
// 将格子转换为字符串键进行比较
const pattern1 = cells1
.map((c) => `${c.x},${c.y},${c.color}`)
.sort()
.join('|');
const pattern2 = cells2
.map((c) => `${c.x},${c.y},${c.color}`)
.sort()
.join('|');
return pattern1 === pattern2;
},
/**
* 生成不重复的图案组
*/
generateUniqueGroups(
allShapes: ShapeTemplate[],
count: number,
config: GridConfig,
): GridGroup[] {
const groups: GridGroup[] = [];
const usedPatterns = new Set<string>(); // 用于存储已使用的图案签名
// 如果需要的数量超过可用形状数量,允许重复使用
const maxUniqueShapes = Math.min(count, allShapes.length);
const availableShapes = [...allShapes]; // 复制数组以便打乱
// 打乱数组顺序
for (let i = availableShapes.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[availableShapes[i], availableShapes[j]] = [
availableShapes[j],
availableShapes[i],
];
}
// 生成不重复的图案
for (let i = 0; i < count; i++) {
const shapeIndex = i % availableShapes.length;
const shape = availableShapes[shapeIndex];
const cells = generateCompletePattern(shape);
// 生成图案签名
const patternSignature = cells
.map((c) => `${c.x},${c.y},${c.color}`)
.sort()
.join('|');
// 如果图案已存在,尝试下一个形状
if (usedPatterns.has(patternSignature)) {
// 如果所有形状都用过了,允许重复
if (usedPatterns.size >= maxUniqueShapes) {
// 使用当前形状,即使重复
groups.push({
filledCells: cells,
emptyCells: [],
});
continue;
} else {
// 尝试找到未使用的形状
let found = false;
for (let j = 0; j < availableShapes.length; j++) {
const nextShape = availableShapes[j];
const nextCells = generateCompletePattern(nextShape);
const nextSignature = nextCells
.map((c) => `${c.x},${c.y},${c.color}`)
.sort()
.join('|');
if (!usedPatterns.has(nextSignature)) {
groups.push({
filledCells: nextCells,
emptyCells: [],
});
usedPatterns.add(nextSignature);
found = true;
break;
}
}
if (!found) {
// 如果找不到未使用的,使用当前形状
groups.push({
filledCells: cells,
emptyCells: [],
});
}
}
} else {
// 图案未使用,添加到结果中
groups.push({
filledCells: cells,
emptyCells: [],
});
usedPatterns.add(patternSignature);
}
}
return groups;
},
/**
* 随机生成
*/
onRandom() {
const mode = this.data.currentMode;
let allShapes: ShapeTemplate[];
let config: GridConfig;
let groupsPerRow: number;
let totalRows: number;
// 根据模式选择形状和配置
if (mode === '3x3') {
allShapes = ALL_3X3_SHAPES;
config = { rows: 3, cols: 3 };
groupsPerRow = 2; // 每行2组
totalRows = 3; // 总共3行
} else if (mode === '5x5') {
allShapes = ALL_5X5_SHAPES;
config = { rows: 5, cols: 5 };
groupsPerRow = 2;
totalRows = 2; // 5x5可以展示2行
} else {
// 7x7
allShapes = ALL_7X7_SHAPES;
config = { rows: 7, cols: 7 };
groupsPerRow = 1; // 7x7每行1组
totalRows = 2; // 总共2行
}
// 生成不重复的图案组
const totalGroups = groupsPerRow * totalRows;
const groups = this.generateUniqueGroups(
allShapes,
totalGroups,
config,
);
this.gridData = {
config,
groups,
groupsPerRow,
totalRows,
};
this.drawCanvas();
},
/** 选择类型 */
onSelectType(event: any) {
const { name, value } = event.detail;
this.setData({
currentMode: value,
currentModeName: name,
});
// 重新生成数据
this.onRandom();
},
});