feat: 开始开发专注力系列
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"pages": [
|
||||
"pages/mathIndex/mathIndex",
|
||||
"pages/focusIndex/focusIndex",
|
||||
"pages/index/index",
|
||||
"pages/shape/index",
|
||||
"pages/copyBook/copyBook",
|
||||
@@ -20,6 +21,12 @@
|
||||
"numberDecompose/numberDecompose"
|
||||
],
|
||||
"independent": false
|
||||
},
|
||||
{
|
||||
"root": "focusPages",
|
||||
"name": "focusPages",
|
||||
"pages": ["gridDrawing/gridDrawing"],
|
||||
"independent": false
|
||||
}
|
||||
],
|
||||
"preloadRule": {
|
||||
@@ -62,6 +69,12 @@
|
||||
"selectedIconPath": "assets/tabBar/icon-math-active.png",
|
||||
"text": "数感启蒙"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/focusIndex/focusIndex",
|
||||
"iconPath": "assets/tabBar/icon-word.png",
|
||||
"selectedIconPath": "assets/tabBar/icon-word-active.png",
|
||||
"text": "专注力"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"iconPath": "assets/tabBar/icon-word.png",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"toy-button": "../../../../ui/button/button"
|
||||
"toy-button": "../../ui/button/button"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"van-action-sheet": "../../miniprogram_npm/@vant/weapp/action-sheet/index"
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,12 @@ export interface FocusFunctionType {
|
||||
}
|
||||
|
||||
export const FOCUS_FUNCTION_TYPES: FocusFunctionType[] = [
|
||||
// 第一阶段:认识数字(最基础)
|
||||
// 格子仿画
|
||||
{
|
||||
id: 'number-find',
|
||||
page: 'numberFind',
|
||||
title: '找数字,涂一涂',
|
||||
desc: '找一找下面相同的数字,涂上颜色',
|
||||
icon: '🔍',
|
||||
id: 'grid-drawing',
|
||||
page: 'gridDrawing',
|
||||
title: '格子仿画',
|
||||
desc: '在网格中填充颜色,形成各种形状',
|
||||
icon: '🎨',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"navigationBarTitleText": "格子仿画",
|
||||
"navigationBarBackgroundColor": "#FFD719",
|
||||
"homeButton": true,
|
||||
"backgroundColor": "#F6F6F6",
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-type-selector": "../../components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import GridDraw from '../shared/service/gridDraw';
|
||||
import { createFocusPage } from '../shared/common/focusPageMixin';
|
||||
import { GridDrawingData } from '../shared/service/gridDraw';
|
||||
import { generateCompletePattern } from '../shared/utils/gridUtils';
|
||||
import { getRandom3x3Shape } from '../shared/shapes/gridShapes3x3';
|
||||
import { getRandom5x5Shape } from '../shared/shapes/gridShapes5x5';
|
||||
import { getRandom7x7Shape } from '../shared/shapes/gridShapes7x7';
|
||||
import { ShapeTemplate } 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 });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
const mode = this.data.currentMode;
|
||||
let shape: ShapeTemplate;
|
||||
let config: GridConfig;
|
||||
|
||||
// 根据模式选择形状和配置
|
||||
if (mode === '3x3') {
|
||||
shape = getRandom3x3Shape();
|
||||
config = { rows: 3, cols: 3 };
|
||||
} else if (mode === '5x5') {
|
||||
shape = getRandom5x5Shape();
|
||||
config = { rows: 5, cols: 5 };
|
||||
} else {
|
||||
// 7x7
|
||||
shape = getRandom7x7Shape();
|
||||
config = { rows: 7, cols: 7 };
|
||||
}
|
||||
|
||||
// 生成完整的格子数据
|
||||
const cells = generateCompletePattern(shape);
|
||||
|
||||
this.gridData = {
|
||||
config,
|
||||
cells,
|
||||
name: shape.name,
|
||||
};
|
||||
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentMode: value,
|
||||
currentModeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
@@ -3,7 +3,7 @@
|
||||
* 基于通用 pageMixin,提供专注力模块特定的配置
|
||||
*/
|
||||
|
||||
import { createPage, PageCommonMethodsConfig } from '../../base/pageMixin';
|
||||
import { createPage, PageCommonMethodsConfig } from '../../../base/pageMixin';
|
||||
|
||||
/**
|
||||
* 专注力模块的函数类型定义
|
||||
@@ -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];
|
||||
}
|
||||
@@ -68,7 +68,7 @@ import MathBottomButtons from '../components/math-bottom-buttons/math-bottom-but
|
||||
```typescript
|
||||
import AdditionDraw from '../shared/service/additionDraw';
|
||||
import { createMathPage } from '../shared/common/mathPageMixin';
|
||||
import MathBottomButtons from '../shared/components/math-bottom-buttons/math-bottom-buttons';
|
||||
import MathBottomButtons from '../../components/math-bottom-buttons/math-bottom-buttons';
|
||||
```
|
||||
|
||||
#### service 文件之间的导入
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-type-selector": "../../components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../shared/common/mathPage.less';
|
||||
@import '../../base/baseDrawPage.less';
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -7,6 +7,6 @@
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../shared/common/mathPage.less';
|
||||
@import '../../base/baseDrawPage.less';
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-type-selector": "../../components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../shared/common/mathPage.less';
|
||||
@import '../../base/baseDrawPage.less';
|
||||
@@ -7,6 +7,6 @@
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../shared/common/mathPage.less';
|
||||
@import '../../base/baseDrawPage.less';
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-type-selector": "../../components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../shared/common/mathPage.less';
|
||||
@import '../../base/baseDrawPage.less';
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons",
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector"
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
|
||||
"math-type-selector": "../../components/math-type-selector/math-type-selector"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../shared/common/mathPage.less';
|
||||
@import '../../base/baseDrawPage.less';
|
||||
@@ -7,6 +7,6 @@
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../shared/common/mathPage.less';
|
||||
@import '../../base/baseDrawPage.less';
|
||||
|
||||
/* 数字选择区域 */
|
||||
.number-selection-area {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"toy-button": "../../../../ui/button/button",
|
||||
"van-action-sheet": "../../../../miniprogram_npm/@vant/weapp/action-sheet/index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"navigationBarTitleText": "涂鸦丫-专注力",
|
||||
"navigationBarBackgroundColor": "#FFD719",
|
||||
"homeButton": true,
|
||||
"backgroundColor": "#F6F6F6",
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
background-color: #f6f6f6;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.empty {
|
||||
height: 190rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 专注力页面标题和描述 */
|
||||
.math-page-header {
|
||||
margin-bottom: 40rpx;
|
||||
text-align: center;
|
||||
padding-top: 28rpx;
|
||||
}
|
||||
|
||||
.math-page-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #141414;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.math-page-subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 专注力功能列表样式 */
|
||||
.math-functions-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.math-function-item {
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
transition: all 0.2s ease-in-out;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 24rpx;
|
||||
opacity: 0;
|
||||
background: rgba(147, 211, 51, 0.1);
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&:active {
|
||||
&::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
border-color: #93d333;
|
||||
box-shadow: 0 8rpx 24rpx rgba(147, 211, 51, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.function-item-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.function-image-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(255, 215, 25, 0.2) 0%, rgba(255, 169, 64, 0.2) 100%);
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 56rpx;
|
||||
}
|
||||
|
||||
.function-item-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.function-item-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #141414;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.function-item-desc {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.function-item-arrow {
|
||||
font-size: 36rpx;
|
||||
color: #999999;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 使用提示 */
|
||||
.usage-hint {
|
||||
background: linear-gradient(135deg, #fff7e6 0%, #fffbe6 100%);
|
||||
border: 2rpx solid #ffd719;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.hint-icon {
|
||||
font-size: 40rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hint-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hint-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #141414;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.hint-desc {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
line-height: 1.5;
|
||||
|
||||
view {
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { FOCUS_FUNCTION_TYPES } from '../../constants/focusFunctions';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
functionList: FOCUS_FUNCTION_TYPES,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 页面加载
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开专注力功能页面
|
||||
*/
|
||||
openFocusFunction(e: WechatMiniprogram.TouchEvent) {
|
||||
const functionId = e.currentTarget.dataset.function;
|
||||
const functionItem = this.data.functionList.find(
|
||||
(item) => item.id === functionId,
|
||||
);
|
||||
|
||||
if (!functionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果有page字段,跳转到对应页面
|
||||
if (functionItem.page) {
|
||||
wx.navigateTo({
|
||||
url: `/focusPages/${functionItem.page}/${functionItem.page}?id=${functionId}`,
|
||||
});
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: `功能开发中,敬请期待`,
|
||||
icon: 'none',
|
||||
duration: 2500,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
<view class="page-container">
|
||||
<!-- 页面标题和描述 -->
|
||||
<view class="math-page-header">
|
||||
<view class="math-page-title">专注力训练</view>
|
||||
<view class="math-page-subtitle">
|
||||
选择功能,生成可打印的专注力训练涂鸦卡
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能列表 -->
|
||||
<view class="math-functions-list">
|
||||
<view
|
||||
class="math-function-item"
|
||||
wx:for="{{functionList}}"
|
||||
wx:key="id"
|
||||
data-function="{{item.id}}"
|
||||
bind:tap="openFocusFunction">
|
||||
<view class="function-item-image">
|
||||
<view class="function-image-placeholder">{{item.icon}}</view>
|
||||
</view>
|
||||
<view class="function-item-content">
|
||||
<view class="function-item-title">{{item.title}}</view>
|
||||
<view class="function-item-desc">{{item.desc}}</view>
|
||||
</view>
|
||||
<view class="function-item-arrow">→</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 使用提示 -->
|
||||
<view class="usage-hint">
|
||||
<view class="hint-icon">💡</view>
|
||||
<view class="hint-text">
|
||||
<view class="hint-title">使用提示</view>
|
||||
<view class="hint-desc">
|
||||
<view>1. 点击上方功能卡片选择要生成的涂鸦卡类型</view>
|
||||
<view>2. 选择网格尺寸等参数</view>
|
||||
<view>3. 预览生成的涂鸦卡效果</view>
|
||||
<view>4. 下载A4格式PNG图片,打印后使用</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||