feat: 开始开发专注力系列

This commit is contained in:
R524809
2025-12-11 17:47:22 +08:00
parent 3c0e2af10d
commit 3648ccc494
61 changed files with 1348 additions and 34 deletions
@@ -0,0 +1,76 @@
/**
* 专注力页面专用的 Mixin
* 基于通用 pageMixin,提供专注力模块特定的配置
*/
import { createPage, PageCommonMethodsConfig } from '../../../base/pageMixin';
/**
* 专注力模块的函数类型定义
*/
export interface FocusFunctionType {
id: string;
title: string;
desc: string;
}
/**
* 专注力模块的函数列表
* TODO: 根据实际需求补充完整的功能列表
*/
export const FOCUS_FUNCTION_TYPES: FocusFunctionType[] = [
{
id: 'grid-drawing',
title: '格子仿画',
desc: '在格子中绘制颜色,形成各种形状',
},
// 可以在这里添加更多专注力相关的功能
// {
// id: 'pattern-memory',
// title: '图案记忆',
// desc: '记住并重现图案',
// },
];
/**
* 专注力模块的分享配置
*/
const focusShareConfig = {
title: '涂鸦丫-专注力训练',
imageUrl: 'https://cdn.joeyone.cn/doodle/share-img/focus-share.png',
};
/**
* 专注力模块的页面信息查找函数
*/
function focusPageInfoLookup(functionId: string) {
const functionItem = FOCUS_FUNCTION_TYPES.find(
(item) => item.id === functionId,
);
if (functionItem) {
return {
title: functionItem.title,
desc: functionItem.desc,
};
}
return undefined;
}
/**
* 专注力模块的配置
*/
const focusConfig: PageCommonMethodsConfig = {
shareConfig: focusShareConfig,
pageInfoLookup: focusPageInfoLookup,
};
/**
* 创建专注力页面的便捷函数
* 自动应用公共方法并注册为页面
* @param pageOptions 页面选项
*/
export function createFocusPage(pageOptions: any) {
createPage(pageOptions, focusConfig);
}
@@ -0,0 +1,125 @@
/**
* 格子仿画绘制服务
* 合并了基础绘制和内容绘制逻辑
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { GridCell, GridConfig } from '../types/gridTypes';
/**
* 格子仿画数据
*/
export interface GridDrawingData {
config: GridConfig;
cells: GridCell[];
name?: string;
}
/**
* 格子仿画绘制服务
*/
class GridDraw extends BaseDrawService {
gridData: GridDrawingData | null = null;
constructor(
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) {
super(canvas, ctx, options);
}
/**
* 绘制格子仿画内容
*/
async draw(gridData: GridDrawingData) {
if (!gridData || !gridData.cells) {
return;
}
this.setPrintConfig();
this.gridData = gridData;
this.clear();
this.setPaper();
// 绘制Header
if (this.headerType !== 'minimal') {
await this.drawHeader();
} else {
this.drawMiniHeader();
}
// 绘制内容区域(网格和填充)
this.drawDivider();
await this.drawGridContent({
canvas: this.canvas,
ctx: this.ctx,
gridData: this.gridData,
canvasWidth: this.canvasWidth,
startY: this.currentY,
});
}
/**
* 绘制网格内容
*/
private async drawGridContent(params: {
canvas: WechatMiniprogram.Canvas;
ctx: RenderingContext;
gridData: GridDrawingData;
canvasWidth: number;
startY: number;
}): Promise<void> {
const { ctx, gridData, canvasWidth, startY } = params;
const { config, cells } = gridData;
const { rows, cols } = config;
// 计算网格尺寸
const margin = 40; // 左右边距
const availableWidth = canvasWidth - margin * 2;
const cellSize = Math.floor(availableWidth / cols);
const gridWidth = cellSize * cols;
const gridHeight = cellSize * rows;
// 计算网格起始位置(居中)
const gridStartX = margin + (availableWidth - gridWidth) / 2;
const gridStartY = startY + 30; // 顶部间距
// 绘制网格线
ctx.strokeStyle = config.gridLineColor || '#000000';
ctx.lineWidth = config.gridLineWidth || 1;
ctx.setLineDash([]); // 实线
// 绘制垂直线
for (let i = 0; i <= cols; i++) {
const x = gridStartX + i * cellSize;
ctx.beginPath();
ctx.moveTo(x, gridStartY);
ctx.lineTo(x, gridStartY + gridHeight);
ctx.stroke();
}
// 绘制水平线
for (let i = 0; i <= rows; i++) {
const y = gridStartY + i * cellSize;
ctx.beginPath();
ctx.moveTo(gridStartX, y);
ctx.lineTo(gridStartX + gridWidth, y);
ctx.stroke();
}
// 绘制填充的格子
for (const cell of cells) {
if (cell.x >= 0 && cell.x < cols && cell.y >= 0 && cell.y < rows) {
const x = gridStartX + cell.x * cellSize;
const y = gridStartY + cell.y * cellSize;
// 填充颜色
ctx.fillStyle = cell.color;
ctx.fillRect(x + 1, y + 1, cellSize - 2, cellSize - 2);
}
}
}
}
export default GridDraw;
@@ -0,0 +1,117 @@
/**
* 格子仿画 - 3x3 网格形状数据
* 适用于入门级,低龄儿童
*/
import { GridCell, ShapeTemplate } from '../types/gridTypes';
// ==================== 3x3 形状模板 ====================
/** 简单十字形 */
export const CROSS_3X3: ShapeTemplate = {
id: 'cross-3x3-001',
name: '十字形',
recommendedSize: { rows: 3, cols: 3 },
category: 'geometric',
difficulty: 1,
supportSymmetry: true,
symmetryType: 'both',
isComplete: true,
cells: [
{ x: 1, y: 0, color: '#FFFF00' }, // 顶部
{ x: 0, y: 1, color: '#FFFF00' }, // 左侧
{ x: 1, y: 1, color: '#FFFF00' }, // 中心
{ x: 2, y: 1, color: '#FFFF00' }, // 右侧
{ x: 1, y: 2, color: '#FFFF00' }, // 底部
],
};
/** L形 */
export const L_SHAPE_3X3: ShapeTemplate = {
id: 'l-3x3-001',
name: 'L形',
recommendedSize: { rows: 3, cols: 3 },
category: 'geometric',
difficulty: 1,
supportSymmetry: false,
isComplete: true,
cells: [
{ x: 0, y: 0, color: '#0000FF' },
{ x: 0, y: 1, color: '#0000FF' },
{ x: 0, y: 2, color: '#0000FF' },
{ x: 1, y: 2, color: '#0000FF' },
{ x: 2, y: 2, color: '#0000FF' },
],
};
/** 对角线 */
export const DIAGONAL_3X3: ShapeTemplate = {
id: 'diagonal-3x3-001',
name: '对角线',
recommendedSize: { rows: 3, cols: 3 },
category: 'geometric',
difficulty: 1,
supportSymmetry: true,
symmetryType: 'both',
isComplete: true,
cells: [
{ x: 0, y: 0, color: '#FF0000' },
{ x: 1, y: 1, color: '#FF0000' },
{ x: 2, y: 2, color: '#FF0000' },
],
};
/** 小房子 */
export const HOUSE_3X3: ShapeTemplate = {
id: 'house-3x3-001',
name: '小房子',
recommendedSize: { rows: 3, cols: 3 },
category: 'object',
difficulty: 1,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: true,
cells: [
{ x: 1, y: 0, color: '#FF0000' }, // 屋顶
{ x: 0, y: 1, color: '#FFFF00' }, // 左侧墙
{ x: 1, y: 1, color: '#FFFF00' }, // 中间墙
{ x: 2, y: 1, color: '#FFFF00' }, // 右侧墙
{ x: 1, y: 2, color: '#8B4513' }, // 门
],
};
/** 小星星 */
export const STAR_3X3: ShapeTemplate = {
id: 'star-3x3-001',
name: '小星星',
recommendedSize: { rows: 3, cols: 3 },
category: 'geometric',
difficulty: 1,
supportSymmetry: true,
symmetryType: 'both',
isComplete: true,
cells: [
{ x: 1, y: 0, color: '#FFD700' }, // 顶部
{ x: 0, y: 1, color: '#FFD700' },
{ x: 1, y: 1, color: '#FFD700' }, // 中心
{ x: 2, y: 1, color: '#FFD700' },
{ x: 1, y: 2, color: '#FFD700' }, // 底部
],
};
/** 所有 3x3 形状模板 */
export const ALL_3X3_SHAPES: ShapeTemplate[] = [
CROSS_3X3,
L_SHAPE_3X3,
DIAGONAL_3X3,
HOUSE_3X3,
STAR_3X3,
];
/**
* 随机获取一个 3x3 形状
*/
export function getRandom3x3Shape(): ShapeTemplate {
const randomIndex = Math.floor(Math.random() * ALL_3X3_SHAPES.length);
return ALL_3X3_SHAPES[randomIndex];
}
@@ -0,0 +1,218 @@
/**
* 格子仿画 - 5x5 网格形状数据
* 适用于常用尺寸,适中难度
*/
import { GridCell, ShapeTemplate } from '../types/gridTypes';
// ==================== 5x5 形状模板 ====================
/** 十字形 */
export const CROSS_5X5: ShapeTemplate = {
id: 'cross-5x5-001',
name: '十字形',
recommendedSize: { rows: 5, cols: 5 },
category: 'geometric',
difficulty: 1,
supportSymmetry: true,
symmetryType: 'both',
isComplete: true,
cells: [
{ x: 2, y: 0, color: '#FFFF00' }, // 顶部
{ x: 2, y: 1, color: '#FFFF00' },
{ x: 0, y: 2, color: '#FFFF00' }, // 左侧
{ x: 1, y: 2, color: '#FFFF00' },
{ x: 2, y: 2, color: '#FFFF00' }, // 中心
{ x: 3, y: 2, color: '#FFFF00' },
{ x: 4, y: 2, color: '#FFFF00' }, // 右侧
{ x: 2, y: 3, color: '#FFFF00' },
{ x: 2, y: 4, color: '#FFFF00' }, // 底部
],
};
/** X形 */
export const X_SHAPE_5X5: ShapeTemplate = {
id: 'x-5x5-001',
name: 'X形',
recommendedSize: { rows: 5, cols: 5 },
category: 'geometric',
difficulty: 2,
supportSymmetry: true,
symmetryType: 'both',
isComplete: true,
cells: [
{ x: 0, y: 0, color: '#0000FF' },
{ x: 1, y: 1, color: '#0000FF' },
{ x: 2, y: 2, color: '#0000FF' },
{ x: 3, y: 3, color: '#0000FF' },
{ x: 4, y: 4, color: '#0000FF' },
{ x: 4, y: 0, color: '#0000FF' },
{ x: 3, y: 1, color: '#0000FF' },
{ x: 1, y: 3, color: '#0000FF' },
{ x: 0, y: 4, color: '#0000FF' },
],
};
/** 星星 */
export const STAR_5X5: ShapeTemplate = {
id: 'star-5x5-001',
name: '星星',
recommendedSize: { rows: 5, cols: 5 },
category: 'geometric',
difficulty: 3,
supportSymmetry: true,
symmetryType: 'both',
isComplete: true,
cells: [
{ x: 2, y: 0, color: '#FFD700' }, // 顶部
{ x: 1, y: 1, color: '#FFD700' },
{ x: 3, y: 1, color: '#FFD700' },
{ x: 0, y: 2, color: '#FFD700' },
{ x: 2, y: 2, color: '#FFD700' }, // 中心
{ x: 4, y: 2, color: '#FFD700' },
{ x: 1, y: 3, color: '#FFD700' },
{ x: 3, y: 3, color: '#FFD700' },
{ x: 2, y: 4, color: '#FFD700' }, // 底部
],
};
/** Z字形 */
export const ZIGZAG_5X5: ShapeTemplate = {
id: 'zigzag-5x5-001',
name: 'Z字形',
recommendedSize: { rows: 5, cols: 5 },
category: 'geometric',
difficulty: 2,
supportSymmetry: false,
isComplete: true,
cells: [
{ x: 0, y: 0, color: '#800080' },
{ x: 1, y: 0, color: '#800080' },
{ x: 2, y: 0, color: '#800080' },
{ x: 2, y: 1, color: '#800080' },
{ x: 1, y: 2, color: '#800080' },
{ x: 0, y: 2, color: '#800080' },
{ x: 0, y: 3, color: '#800080' },
{ x: 1, y: 3, color: '#800080' },
{ x: 2, y: 4, color: '#800080' },
{ x: 3, y: 4, color: '#800080' },
{ x: 4, y: 4, color: '#800080' },
],
};
/** V字形 */
export const V_SHAPE_5X5: ShapeTemplate = {
id: 'v-5x5-001',
name: 'V字形',
recommendedSize: { rows: 5, cols: 5 },
category: 'geometric',
difficulty: 2,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: true,
cells: [
{ x: 0, y: 0, color: '#00FF00' },
{ x: 4, y: 0, color: '#00FF00' },
{ x: 0, y: 1, color: '#00FF00' },
{ x: 4, y: 1, color: '#00FF00' },
{ x: 1, y: 2, color: '#00FF00' },
{ x: 3, y: 2, color: '#00FF00' },
{ x: 2, y: 3, color: '#00FF00' },
{ x: 2, y: 4, color: '#00FF00' },
],
};
/** I字形 */
export const I_SHAPE_5X5: ShapeTemplate = {
id: 'i-5x5-001',
name: 'I字形',
recommendedSize: { rows: 5, cols: 5 },
category: 'geometric',
difficulty: 1,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: true,
cells: [
{ x: 1, y: 0, color: '#FF7F00' },
{ x: 2, y: 0, color: '#FF7F00' },
{ x: 3, y: 0, color: '#FF7F00' },
{ x: 2, y: 1, color: '#FF7F00' },
{ x: 2, y: 2, color: '#FF7F00' },
{ x: 2, y: 3, color: '#FF7F00' },
{ x: 2, y: 4, color: '#FF7F00' },
{ x: 1, y: 4, color: '#FF7F00' },
{ x: 3, y: 4, color: '#FF7F00' },
],
};
/** 棋盘格 */
export const CHECKERBOARD_5X5: ShapeTemplate = {
id: 'checkerboard-5x5-001',
name: '棋盘格',
recommendedSize: { rows: 5, cols: 5 },
category: 'geometric',
difficulty: 3,
supportSymmetry: true,
symmetryType: 'both',
isComplete: true,
cells: [
{ x: 0, y: 0, color: '#FFFF00' },
{ x: 2, y: 0, color: '#FFFF00' },
{ x: 4, y: 0, color: '#FFFF00' },
{ x: 1, y: 1, color: '#FFFF00' },
{ x: 3, y: 1, color: '#FFFF00' },
{ x: 0, y: 2, color: '#FFFF00' },
{ x: 2, y: 2, color: '#FFFF00' },
{ x: 4, y: 2, color: '#FFFF00' },
{ x: 1, y: 3, color: '#FFFF00' },
{ x: 3, y: 3, color: '#FFFF00' },
{ x: 0, y: 4, color: '#FFFF00' },
{ x: 2, y: 4, color: '#FFFF00' },
{ x: 4, y: 4, color: '#FFFF00' },
],
};
/** 心形(简化版) */
export const HEART_5X5: ShapeTemplate = {
id: 'heart-5x5-001',
name: '心形',
recommendedSize: { rows: 5, cols: 5 },
category: 'object',
difficulty: 2,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: true,
cells: [
{ x: 1, y: 0, color: '#FF69B4' },
{ x: 2, y: 0, color: '#FF69B4' },
{ x: 3, y: 0, color: '#FF69B4' },
{ x: 0, y: 1, color: '#FF69B4' },
{ x: 2, y: 1, color: '#FF69B4' },
{ x: 4, y: 1, color: '#FF69B4' },
{ x: 1, y: 2, color: '#FF69B4' },
{ x: 2, y: 2, color: '#FF69B4' },
{ x: 3, y: 2, color: '#FF69B4' },
{ x: 2, y: 3, color: '#FF69B4' },
{ x: 2, y: 4, color: '#FF69B4' },
],
};
/** 所有 5x5 形状模板 */
export const ALL_5X5_SHAPES: ShapeTemplate[] = [
CROSS_5X5,
X_SHAPE_5X5,
STAR_5X5,
ZIGZAG_5X5,
V_SHAPE_5X5,
I_SHAPE_5X5,
CHECKERBOARD_5X5,
HEART_5X5,
];
/**
* 随机获取一个 5x5 形状
*/
export function getRandom5x5Shape(): ShapeTemplate {
const randomIndex = Math.floor(Math.random() * ALL_5X5_SHAPES.length);
return ALL_5X5_SHAPES[randomIndex];
}
@@ -0,0 +1,247 @@
/**
* 格子仿画 - 7x7 网格形状数据
* 适用于中等难度,复杂图案
*/
import { GridCell, ShapeTemplate } from '../types/gridTypes';
// ==================== 7x7 形状模板 ====================
/** 南瓜头 - 对称涂色(仅存储左半部分) */
export const PUMPKIN_HEAD_7X7: ShapeTemplate = {
id: 'pumpkin-7x7-001',
name: '南瓜头',
recommendedSize: { rows: 7, cols: 7 },
category: 'character',
difficulty: 4,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: false, // 只存储了左半部分
colorSchemeId: 'halloween',
cells: [
// 茎(在对称轴上)
{ x: 3, y: 0, color: '#FF7F00' },
// 脸部轮廓(浅绿色)- 左半部分
{ x: 0, y: 1, color: '#90EE90' },
{ x: 1, y: 1, color: '#90EE90' },
{ x: 0, y: 2, color: '#90EE90' },
{ x: 1, y: 2, color: '#90EE90' },
{ x: 0, y: 3, color: '#90EE90' },
{ x: 1, y: 3, color: '#90EE90' },
{ x: 0, y: 4, color: '#90EE90' },
{ x: 1, y: 4, color: '#90EE90' },
{ x: 0, y: 5, color: '#90EE90' },
{ x: 1, y: 5, color: '#90EE90' },
{ x: 0, y: 6, color: '#90EE90' },
{ x: 1, y: 6, color: '#90EE90' },
// 脸部(黄色)
{ x: 2, y: 1, color: '#FFFF00' },
{ x: 3, y: 1, color: '#FFFF00' }, // 对称轴上的点
{ x: 2, y: 2, color: '#FFFF00' },
{ x: 2, y: 3, color: '#FFFF00' },
{ x: 3, y: 3, color: '#FFFF00' },
{ x: 2, y: 4, color: '#FFFF00' },
{ x: 2, y: 5, color: '#FFFF00' },
{ x: 3, y: 5, color: '#FFFF00' },
// 眼睛(黑色)- 左眼
{ x: 2, y: 2, color: '#000000' },
],
};
/** 卡通恐龙 - 对称涂色(仅存储左半部分) */
export const DINOSAUR_7X7: ShapeTemplate = {
id: 'dinosaur-7x7-001',
name: '卡通恐龙',
recommendedSize: { rows: 7, cols: 7 },
category: 'animal',
difficulty: 4,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: false, // 只存储了左半部分
cells: [
// 身体(浅绿色)
{ x: 1, y: 1, color: '#90EE90' },
{ x: 2, y: 1, color: '#90EE90' },
{ x: 1, y: 2, color: '#90EE90' },
{ x: 2, y: 2, color: '#90EE90' },
{ x: 0, y: 3, color: '#90EE90' },
{ x: 1, y: 3, color: '#90EE90' },
{ x: 2, y: 3, color: '#90EE90' },
{ x: 0, y: 4, color: '#90EE90' },
{ x: 1, y: 4, color: '#90EE90' },
{ x: 2, y: 4, color: '#90EE90' },
{ x: 0, y: 5, color: '#90EE90' },
{ x: 1, y: 5, color: '#90EE90' },
{ x: 2, y: 5, color: '#90EE90' },
{ x: 0, y: 6, color: '#90EE90' },
{ x: 1, y: 6, color: '#90EE90' },
// 眼睛(黑色)
{ x: 2, y: 1, color: '#000000' },
// 嘴巴(红色)
{ x: 2, y: 2, color: '#FF0000' },
{ x: 0, y: 3, color: '#FF0000' },
// 脚(黄色)
{ x: 2, y: 5, color: '#FFFF00' },
{ x: 0, y: 6, color: '#FFFF00' },
{ x: 1, y: 6, color: '#FFFF00' },
],
};
/** 心形 - 对称涂色(仅存储左半部分,对称轴在x=3) */
export const HEART_7X7: ShapeTemplate = {
id: 'heart-7x7-001',
name: '心形',
recommendedSize: { rows: 7, cols: 7 },
category: 'object',
difficulty: 3,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: false,
colorSchemeId: 'warm',
cells: [
// 左上半部分
{ x: 1, y: 0, color: '#FF69B4' },
{ x: 2, y: 0, color: '#FF69B4' },
{ x: 0, y: 1, color: '#FF69B4' },
{ x: 3, y: 1, color: '#FF69B4' }, // 对称轴上的点
{ x: 0, y: 2, color: '#FF69B4' },
{ x: 1, y: 3, color: '#FF69B4' },
{ x: 2, y: 4, color: '#FF69B4' },
{ x: 3, y: 5, color: '#FF69B4' }, // 对称轴上的点
{ x: 3, y: 6, color: '#FF69B4' }, // 对称轴上的点(底部尖端)
],
};
/** 房子 */
export const HOUSE_7X7: ShapeTemplate = {
id: 'house-7x7-001',
name: '小房子',
recommendedSize: { rows: 7, cols: 7 },
category: 'object',
difficulty: 3,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: false,
cells: [
// 屋顶(三角形)- 左半部分
{ x: 2, y: 0, color: '#FF0000' },
{ x: 1, y: 1, color: '#FF0000' },
{ x: 2, y: 1, color: '#FF0000' },
{ x: 3, y: 1, color: '#FF0000' }, // 对称轴上的点
{ x: 0, y: 2, color: '#FF0000' },
{ x: 1, y: 2, color: '#FF0000' },
{ x: 2, y: 2, color: '#FF0000' },
{ x: 3, y: 2, color: '#FF0000' },
// 墙体(黄色)- 左半部分
{ x: 1, y: 3, color: '#FFFF00' },
{ x: 2, y: 3, color: '#FFFF00' },
{ x: 3, y: 3, color: '#FFFF00' },
{ x: 1, y: 4, color: '#FFFF00' },
{ x: 2, y: 4, color: '#FFFF00' },
{ x: 3, y: 4, color: '#FFFF00' },
{ x: 1, y: 5, color: '#FFFF00' },
{ x: 2, y: 5, color: '#FFFF00' },
{ x: 3, y: 5, color: '#FFFF00' },
// 门(棕色)- 在对称轴上
{ x: 3, y: 4, color: '#8B4513' },
{ x: 3, y: 5, color: '#8B4513' },
// 窗户(蓝色)
{ x: 1, y: 3, color: '#0000FF' },
],
};
/** 蝴蝶 */
export const BUTTERFLY_7X7: ShapeTemplate = {
id: 'butterfly-7x7-001',
name: '蝴蝶',
recommendedSize: { rows: 7, cols: 7 },
category: 'animal',
difficulty: 4,
supportSymmetry: true,
symmetryType: 'vertical',
isComplete: false,
cells: [
// 左翅膀
{ x: 0, y: 1, color: '#FF69B4' },
{ x: 1, y: 1, color: '#FF69B4' },
{ x: 0, y: 2, color: '#FF69B4' },
{ x: 1, y: 2, color: '#FF69B4' },
{ x: 2, y: 2, color: '#FF69B4' },
{ x: 0, y: 3, color: '#FF69B4' },
{ x: 1, y: 3, color: '#FF69B4' },
{ x: 2, y: 3, color: '#FF69B4' },
// 身体(在对称轴上)
{ x: 3, y: 1, color: '#000000' },
{ x: 3, y: 2, color: '#000000' },
{ x: 3, y: 3, color: '#000000' },
{ x: 3, y: 4, color: '#000000' },
{ x: 3, y: 5, color: '#000000' },
// 左下半翅膀
{ x: 1, y: 4, color: '#FF69B4' },
{ x: 2, y: 4, color: '#FF69B4' },
{ x: 0, y: 5, color: '#FF69B4' },
{ x: 1, y: 5, color: '#FF69B4' },
{ x: 2, y: 5, color: '#FF69B4' },
],
};
/** 花朵 */
export const FLOWER_7X7: ShapeTemplate = {
id: 'flower-7x7-001',
name: '花朵',
recommendedSize: { rows: 7, cols: 7 },
category: 'nature',
difficulty: 3,
supportSymmetry: true,
symmetryType: 'both',
isComplete: false,
cells: [
// 花瓣(左上半部分)
{ x: 1, y: 0, color: '#FF69B4' },
{ x: 2, y: 0, color: '#FF69B4' },
{ x: 0, y: 1, color: '#FF69B4' },
{ x: 1, y: 1, color: '#FF69B4' },
{ x: 2, y: 1, color: '#FF69B4' },
{ x: 3, y: 1, color: '#FF69B4' }, // 对称轴上的点
// 花心(中心)
{ x: 2, y: 2, color: '#FFFF00' },
{ x: 3, y: 2, color: '#FFFF00' }, // 对称轴上的点
{ x: 2, y: 3, color: '#FFFF00' },
{ x: 3, y: 3, color: '#FFFF00' }, // 对称轴上的点
// 茎(在对称轴上)
{ x: 3, y: 4, color: '#00FF00' },
{ x: 3, y: 5, color: '#00FF00' },
{ x: 3, y: 6, color: '#00FF00' },
],
};
/** 所有 7x7 形状模板 */
export const ALL_7X7_SHAPES: ShapeTemplate[] = [
PUMPKIN_HEAD_7X7,
DINOSAUR_7X7,
HEART_7X7,
HOUSE_7X7,
BUTTERFLY_7X7,
FLOWER_7X7,
];
/**
* 随机获取一个 7x7 形状
*/
export function getRandom7x7Shape(): ShapeTemplate {
const randomIndex = Math.floor(Math.random() * ALL_7X7_SHAPES.length);
return ALL_7X7_SHAPES[randomIndex];
}
@@ -0,0 +1,59 @@
/**
* 格子仿画类型定义
*/
/** 单个格子数据 */
export interface GridCell {
/** X轴坐标,从0开始,表示第几列 */
x: number;
/** Y轴坐标,从0开始,表示第几行 */
y: number;
/** 颜色值,十六进制格式 */
color: string;
}
/** 网格配置 */
export interface GridConfig {
/** 网格行数 */
rows: number;
/** 网格列数 */
cols: number;
/** 网格线颜色,默认为黑色 */
gridLineColor?: string;
/** 网格线宽度,默认为1 */
gridLineWidth?: number;
}
/** 形状模板 */
export interface ShapeTemplate {
/** 模板ID */
id: string;
/** 形状名称 */
name: string;
/** 推荐网格尺寸 */
recommendedSize: { rows: number; cols: number };
/** 形状数据(如果是对称形状,这里存储的是左半部分或上半部分) */
cells: GridCell[];
/** 颜色方案ID */
colorSchemeId?: string;
/** 分类 */
category: 'animal' | 'object' | 'geometric' | 'character' | 'nature';
/** 难度等级 1-5 */
difficulty: number;
/** 是否支持对称模式 */
supportSymmetry: boolean;
/** 对称轴类型 */
symmetryType?: 'vertical' | 'horizontal' | 'both';
/** 是否已经是对称完整的图案(false表示只存储了半边) */
isComplete?: boolean;
}
/** 颜色方案 */
export const COLOR_SCHEMES: Record<string, string[]> = {
basic: ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF'],
warm: ['#FF0000', '#FF7F00', '#FFFF00', '#FF69B4', '#FF4500'],
cool: ['#00FFFF', '#0000FF', '#8A2BE2', '#00FF00', '#87CEEB'],
halloween: ['#FF7F00', '#FFFF00', '#000000', '#90EE90', '#FF0000'],
nature: ['#00FF00', '#90EE90', '#006400', '#FFFF00', '#8B4513'],
rainbow: ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#8A2BE2'],
};
@@ -0,0 +1,108 @@
/**
* 格子仿画工具函数
* 提供对称图案生成等功能
*/
import { GridCell, GridConfig, ShapeTemplate } from '../types/gridTypes';
/**
* 移除重复的格子(保留最后一个)
*/
function removeDuplicateCells(cells: GridCell[]): GridCell[] {
const cellMap = new Map<string, GridCell>();
// 从后往前遍历,保留最后出现的格子
for (let i = cells.length - 1; i >= 0; i--) {
const cell = cells[i];
const key = `${cell.x},${cell.y}`;
if (!cellMap.has(key)) {
cellMap.set(key, cell);
}
}
return Array.from(cellMap.values());
}
/**
* 根据对称轴类型,将半部分图案补全为完整图案
* @param halfCells 半部分格子的数据(通常是左半部分或上半部分)
* @param config 网格配置
* @param symmetryType 对称类型
* @returns 完整的格子数据
*/
export function generateSymmetricalPattern(
halfCells: GridCell[],
config: GridConfig,
symmetryType: 'vertical' | 'horizontal' | 'both',
): GridCell[] {
const result: GridCell[] = [...halfCells];
const { rows, cols } = config;
if (symmetryType === 'vertical' || symmetryType === 'both') {
// 垂直对称(关于中间列对称)
const verticalAxis = Math.floor(cols / 2);
const cellsToAdd: GridCell[] = [];
for (const cell of halfCells) {
// 如果格子不在对称轴上,生成对称的格子
if (cell.x < verticalAxis) {
const symmetricX = cols - 1 - cell.x;
cellsToAdd.push({
x: symmetricX,
y: cell.y,
color: cell.color,
});
}
}
result.push(...cellsToAdd);
}
if (symmetryType === 'horizontal' || symmetryType === 'both') {
// 水平对称(关于中间行对称)
const horizontalAxis = Math.floor(rows / 2);
const cellsToAdd: GridCell[] = [];
for (const cell of result) {
// 如果格子不在对称轴上,生成对称的格子
if (cell.y < horizontalAxis) {
const symmetricY = rows - 1 - cell.y;
cellsToAdd.push({
x: cell.x,
y: symmetricY,
color: cell.color,
});
}
}
result.push(...cellsToAdd);
}
// 去重(避免对称轴上的格子重复)
return removeDuplicateCells(result);
}
/**
* 从形状模板生成完整的格子数据
* @param template 形状模板
* @returns 完整的格子数据
*/
export function generateCompletePattern(template: ShapeTemplate): GridCell[] {
if (template.isComplete) {
// 如果已经是完整图案,直接返回
return [...template.cells];
}
if (template.supportSymmetry && template.symmetryType) {
// 使用对称生成完整图案
return generateSymmetricalPattern(
template.cells,
{
rows: template.recommendedSize.rows,
cols: template.recommendedSize.cols,
},
template.symmetryType,
);
}
// 如果不支持对称,返回原数据
return [...template.cells];
}