From d1a41602a7f59308b4d7d05e497006bfd9f2f4dd Mon Sep 17 00:00:00 2001 From: R524809 Date: Tue, 16 Dec 2025 11:01:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:2.5.1=E5=A2=9E=E5=8A=A0=E6=96=B9=E4=BD=8D?= =?UTF-8?q?=E6=B6=82=E6=B6=82=E4=B9=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- miniprogram/app.json | 6 +- miniprogram/app.ts | 2 + miniprogram/config/config.ts | 3 +- miniprogram/constants/focusFunctions.ts | 8 + .../positionColoring/positionColoring.json | 12 + .../positionColoring/positionColoring.ts | 112 ++++++++ .../positionColoring/positionColoring.wxml | 37 +++ .../positionColoring/positionColoring.wxss | 1 + .../shared/service/positionColoringDraw.ts | 253 ++++++++++++++++++ project.private.config.json | 7 + 10 files changed, 439 insertions(+), 2 deletions(-) create mode 100644 miniprogram/focusPages/positionColoring/positionColoring.json create mode 100644 miniprogram/focusPages/positionColoring/positionColoring.ts create mode 100644 miniprogram/focusPages/positionColoring/positionColoring.wxml create mode 100644 miniprogram/focusPages/positionColoring/positionColoring.wxss create mode 100644 miniprogram/focusPages/shared/service/positionColoringDraw.ts diff --git a/miniprogram/app.json b/miniprogram/app.json index ac75aa0..084aecb 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -24,7 +24,11 @@ { "root": "focusPages", "name": "focusPages", - "pages": ["gridDrawing/gridDrawing", "shape/shape"], + "pages": [ + "gridDrawing/gridDrawing", + "shape/shape", + "positionColoring/positionColoring" + ], "independent": false } ], diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 818bc87..1cb3727 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -25,6 +25,8 @@ App({ if (env !== 'release') { const printConfig = wx.getStorageSync('printConfig') || defaultPrintConfig; + // 开发环境,设置为 LogoImage + printConfig.header = 'LogoImage'; this.globalData.printConfig = printConfig; } }, diff --git a/miniprogram/config/config.ts b/miniprogram/config/config.ts index 130b922..5061672 100644 --- a/miniprogram/config/config.ts +++ b/miniprogram/config/config.ts @@ -7,7 +7,8 @@ // }; export const defaultPrintConfig: PrintConfig = { - header: 'LogoImage', + // header: 'LogoImage', + header: 'wechat', appName: '涂鸦丫小程序', appHint: '数学|专注|练字|涂鸦', }; diff --git a/miniprogram/constants/focusFunctions.ts b/miniprogram/constants/focusFunctions.ts index 953a074..bb4145b 100644 --- a/miniprogram/constants/focusFunctions.ts +++ b/miniprogram/constants/focusFunctions.ts @@ -16,6 +16,14 @@ export const FOCUS_FUNCTION_TYPES: FocusFunctionType[] = [ desc: '识别形状,涂一涂', icon: '🔍', }, + // 方位涂涂乐 + { + id: 'position-coloring', + page: 'positionColoring', + title: '方位涂涂乐', + desc: '观察卡片位置,在对应的方格中涂上颜色', + icon: '📍', + }, // 格子仿画 { id: 'grid-drawing-3x3', diff --git a/miniprogram/focusPages/positionColoring/positionColoring.json b/miniprogram/focusPages/positionColoring/positionColoring.json new file mode 100644 index 0000000..94b8061 --- /dev/null +++ b/miniprogram/focusPages/positionColoring/positionColoring.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "方位涂涂乐", + "navigationBarBackgroundColor": "#FFD719", + "homeButton": true, + "backgroundColor": "#F6F6F6", + "enablePullDownRefresh": false, + "usingComponents": { + "toy-button": "../../ui/button/button", + "share-guide-popup": "../../components/share-guide-popup/share-guide-popup", + "math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons" + } +} diff --git a/miniprogram/focusPages/positionColoring/positionColoring.ts b/miniprogram/focusPages/positionColoring/positionColoring.ts new file mode 100644 index 0000000..ce63348 --- /dev/null +++ b/miniprogram/focusPages/positionColoring/positionColoring.ts @@ -0,0 +1,112 @@ +import PositionColoringDraw from '../shared/service/positionColoringDraw'; +import { createFocusPage } from '../shared/common/focusPageMixin'; +import { PositionColoringData } from '../shared/service/positionColoringDraw'; + +createFocusPage({ + canvas: null as Canvas | null, + ctx: null as RenderingContext | null, + boxHeight: 0, + boxWidth: 0, + drawService: null as PositionColoringDraw | null, + gridData: null as PositionColoringData | null, + + data: { + pageTitle: '方位涂涂乐', + functionId: '', + hasContent: false, + showShareDialog: false, + }, + + onLoad(options: { id?: string }) { + const functionId = options.id || 'position-coloring'; + this.setData({ + functionId, + }); + this.initPageInfo(functionId, '方位涂涂乐'); + }, + + onReady() { + this.initCanvas({ + createDrawService: ( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) => { + return new PositionColoringDraw(canvas, ctx, options); + }, + drawServiceOptions: { + title: this.data.pageTitle, + subTitle: '观察卡片位置,在对应的方格中涂上颜色', + functionId: this.data.functionId, + }, + onCanvasReady: () => { + // 初始随机生成 + this.onRandom(); + }, + }); + }, + + /** + * 绘制Canvas内容 + */ + async drawCanvas() { + if (!this.ctx || !this.drawService || !this.gridData) { + return; + } + + try { + await this.drawService.draw(this.gridData); + this.setData({ hasContent: true }); + } catch (error) { + console.error('绘制失败:', error); + this.setData({ hasContent: false }); + } + }, + + /** + * 随机生成 + */ + onRandom() { + // 生成9张不重复的图片索引(1-33),33 是图片总数 + const allImageIndices = Array.from({ length: 33 }, (_, i) => i + 1); + const shuffled = [...allImageIndices].sort(() => Math.random() - 0.5); + const selectedImages = shuffled.slice(0, 9); + + // 创建3x3网格的图片位置映射 + const referenceGrid: number[] = []; + for (let i = 0; i < 9; i++) { + referenceGrid[i] = selectedImages[i]; + } + + // 创建任务组:9个任务,每个任务包含一个图片和对应的位置 + // 第一行需要标记位置(涂色),其他行不标记 + const tasks: Array<{ + imageIndex: number; + position: { x: number; y: number }; + }> = []; + + // 打乱顺序选择图片用于任务 + const taskImageIndices = [...selectedImages].sort( + () => Math.random() - 0.5, + ); + + for (let i = 0; i < 9; i++) { + const imageIndex = taskImageIndices[i]; + // 找到这个图片在参考网格中的位置 + const gridIndex = referenceGrid.indexOf(imageIndex); + const x = gridIndex % 3; + const y = Math.floor(gridIndex / 3); + tasks.push({ + imageIndex, + position: { x, y }, + }); + } + + this.gridData = { + referenceGrid, + tasks, + }; + + this.drawCanvas(); + }, +}); diff --git a/miniprogram/focusPages/positionColoring/positionColoring.wxml b/miniprogram/focusPages/positionColoring/positionColoring.wxml new file mode 100644 index 0000000..5d3cbdb --- /dev/null +++ b/miniprogram/focusPages/positionColoring/positionColoring.wxml @@ -0,0 +1,37 @@ + + + 预览打印效果 + + + + + + + + + + 随机生成 + + + + + + + diff --git a/miniprogram/focusPages/positionColoring/positionColoring.wxss b/miniprogram/focusPages/positionColoring/positionColoring.wxss new file mode 100644 index 0000000..ddc41cc --- /dev/null +++ b/miniprogram/focusPages/positionColoring/positionColoring.wxss @@ -0,0 +1 @@ +@import '../../base/baseDrawPage.wxss'; diff --git a/miniprogram/focusPages/shared/service/positionColoringDraw.ts b/miniprogram/focusPages/shared/service/positionColoringDraw.ts new file mode 100644 index 0000000..4f54b8f --- /dev/null +++ b/miniprogram/focusPages/shared/service/positionColoringDraw.ts @@ -0,0 +1,253 @@ +/** + * 方位涂涂乐绘制服务 + */ + +import { BaseDrawService } from '../../../service/baseDraw'; +import { getImage } from '../../../utils/index'; + +/** + * 方位涂涂乐数据 + */ +export interface PositionColoringData { + /** 参考网格(3x3),存储图片索引数组,按行优先顺序 */ + referenceGrid: number[]; + /** 任务数组,每个任务包含图片索引和位置 */ + tasks: Array<{ + imageIndex: number; + position: { x: number; y: number }; + }>; +} + +/** + * 方位涂涂乐绘制服务 + */ +class PositionColoringDraw extends BaseDrawService { + gridData: PositionColoringData | null = null; + + constructor( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) { + super(canvas, ctx, options); + } + + /** + * 绘制方位涂涂乐内容 + */ + async draw(gridData: PositionColoringData) { + if (!gridData || !gridData.referenceGrid || !gridData.tasks) { + 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.drawContent({ + canvas: this.canvas, + ctx: this.ctx, + gridData: this.gridData, + canvasWidth: this.canvasWidth, + startY: this.currentY, + }); + } + + /** + * 绘制内容区域 + */ + private async drawContent(params: { + canvas: WechatMiniprogram.Canvas; + ctx: RenderingContext; + gridData: PositionColoringData; + canvasWidth: number; + startY: number; + }): Promise { + const { ctx, gridData, canvasWidth } = params; + let { startY } = params; + + startY = startY + 25; + + const margin = 40; // 左右边距 + const gridSize = 3; // 3x3网格 + const cellSize = 65; // 每个格子的大小 + const gridWidth = cellSize * gridSize; + const gridHeight = cellSize * gridSize; + + // 绘制参考网格(上面的九宫格) + const referenceGridY = startY; + const referenceGridX = + margin + (canvasWidth - margin * 2 - gridWidth) / 2; // 居中 + + // 绘制参考网格的网格线 + this.drawGridLines( + ctx, + referenceGridX, + referenceGridY, + gridWidth, + gridHeight, + cellSize, + gridSize, + ); + + // 绘制参考网格中的图片 + for (let i = 0; i < 9; i++) { + const row = Math.floor(i / 3); + const col = i % 3; + const imageIndex = gridData.referenceGrid[i]; + const imageX = referenceGridX + col * cellSize + cellSize / 2 - 25; // 图片居中,假设图片大小50x50 + const imageY = referenceGridY + row * cellSize + cellSize / 2 - 25; + + try { + const imagePath = `/focusPages/shared/aeests/material/${imageIndex}.png`; + const image = await getImage(this.canvas, imagePath); + // @ts-ignore - 微信小程序图片对象有 width 和 height 属性 + const imageSize = 50; + ctx.drawImage(image, imageX, imageY, imageSize, imageSize); + } catch (error) { + console.error(`加载图片失败: ${imageIndex}`, error); + } + } + + // 绘制虚线分隔 + const dividerY = referenceGridY + gridHeight + 30; + ctx.strokeStyle = '#999'; + ctx.lineWidth = 1; + ctx.setLineDash([4, 4]); // 虚线 + ctx.beginPath(); + ctx.moveTo(margin, dividerY); + ctx.lineTo(canvasWidth - margin, dividerY); + ctx.stroke(); + ctx.setLineDash([]); // 重置为实线 + + // 绘制任务区域(三行三列) + const taskStartY = dividerY + 30; + const taskImageSize = 40; // 任务图片大小 + const taskGridSize = 36; // 任务网格每个格子的大小 + const taskGridWidth = taskGridSize * gridSize; + const taskGridHeight = taskGridSize * gridSize; + const taskSpacing = 4; // 图片和网格之间的间距 + const taskGroupWidth = taskImageSize + taskSpacing + taskGridWidth; + const taskGroupSpacing = 24; // 任务组之间的间距 + const taskRowSpacing = 40; // 行之间的间距 + + // 计算每行的起始X位置(居中) + const totalTaskRowWidth = 3 * taskGroupWidth + 2 * taskGroupSpacing; + const taskRowStartX = + margin + (canvasWidth - margin * 2 - totalTaskRowWidth) / 2; + + for (let rowIndex = 0; rowIndex < 3; rowIndex++) { + const currentY = + taskStartY + rowIndex * (taskGridHeight + taskRowSpacing); + + for (let colIndex = 0; colIndex < 3; colIndex++) { + const taskIndex = rowIndex * 3 + colIndex; + const task = gridData.tasks[taskIndex]; + if (!task) continue; + + const currentX = + taskRowStartX + + colIndex * (taskGroupWidth + taskGroupSpacing); + + // 绘制任务图片(左侧) + try { + const imagePath = `/focusPages/shared/aeests/material/${task.imageIndex}.png`; + const image = await getImage(this.canvas, imagePath); + const imageX = currentX; + const imageY = + currentY + (taskGridHeight - taskImageSize) / 2; // 垂直居中 + ctx.drawImage( + image, + imageX, + imageY, + taskImageSize, + taskImageSize, + ); + } catch (error) { + console.error( + `加载任务图片失败: ${task.imageIndex}`, + error, + ); + } + + // 绘制任务网格(右侧) + const taskGridX = currentX + taskImageSize + taskSpacing; + const taskGridY = currentY; + + // 绘制网格线 + this.drawGridLines( + ctx, + taskGridX, + taskGridY, + taskGridWidth, + taskGridHeight, + taskGridSize, + gridSize, + ); + + // 第一行需要标记位置(涂深灰色) + if (rowIndex === 0) { + const { x, y } = task.position; + const cellX = taskGridX + x * taskGridSize; + const cellY = taskGridY + y * taskGridSize; + + // 填充深灰色 + ctx.fillStyle = '#666666'; + ctx.fillRect( + cellX + 1, + cellY + 1, + taskGridSize - 2, + taskGridSize - 2, + ); + } + } + } + } + + /** + * 绘制网格线 + */ + private drawGridLines( + ctx: RenderingContext, + gridStartX: number, + gridStartY: number, + gridWidth: number, + gridHeight: number, + cellSize: number, + gridSize: number, + ): void { + ctx.strokeStyle = '#000'; + ctx.lineWidth = 1; + ctx.setLineDash([]); // 实线 + + // 绘制垂直线 + for (let i = 0; i <= gridSize; i++) { + const x = gridStartX + i * cellSize; + ctx.beginPath(); + ctx.moveTo(x, gridStartY); + ctx.lineTo(x, gridStartY + gridHeight); + ctx.stroke(); + } + + // 绘制水平线 + for (let i = 0; i <= gridSize; i++) { + const y = gridStartY + i * cellSize; + ctx.beginPath(); + ctx.moveTo(gridStartX, y); + ctx.lineTo(gridStartX + gridWidth, y); + ctx.stroke(); + } + } +} + +export default PositionColoringDraw; diff --git a/project.private.config.json b/project.private.config.json index b863764..aab1907 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -23,6 +23,13 @@ "condition": { "miniprogram": { "list": [ + { + "name": "focusPages/positionColoring/positionColoring", + "pathName": "focusPages/positionColoring/positionColoring", + "query": "id=position-coloring", + "scene": null, + "launchMode": "default" + }, { "name": "pages/focusIndex/focusIndex", "pathName": "pages/focusIndex/focusIndex",