diff --git a/miniprogram/app.json b/miniprogram/app.json
index a7888d3..5d35ad5 100644
--- a/miniprogram/app.json
+++ b/miniprogram/app.json
@@ -14,7 +14,8 @@
"numberFind/numberFind",
"countMatch/countMatch",
"addition/addition",
- "missingNumber/missingNumber"
+ "missingNumber/missingNumber",
+ "compare/compare"
],
"independent": false
}
diff --git a/miniprogram/constants/mathFunctions.ts b/miniprogram/constants/mathFunctions.ts
index bb7e5f0..11ecbdb 100644
--- a/miniprogram/constants/mathFunctions.ts
+++ b/miniprogram/constants/mathFunctions.ts
@@ -72,8 +72,9 @@ export const MATH_FUNCTION_TYPES: MathFunctionType[] = [
},
{
id: 'compare',
+ page: 'compare',
title: '数一数,比大小',
- desc: '数一数,比较数量,在⭕️中填入>、<、=',
+ desc: '比较数量大小,在 ⭕️ 中填入>、<、=',
icon: '⚖️',
},
{
diff --git a/miniprogram/mathPages/compare/compare.json b/miniprogram/mathPages/compare/compare.json
new file mode 100644
index 0000000..90335ca
--- /dev/null
+++ b/miniprogram/mathPages/compare/compare.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/mathPages/compare/compare.less b/miniprogram/mathPages/compare/compare.less
new file mode 100644
index 0000000..12602d2
--- /dev/null
+++ b/miniprogram/mathPages/compare/compare.less
@@ -0,0 +1 @@
+@import '../common/mathPage.less';
\ No newline at end of file
diff --git a/miniprogram/mathPages/compare/compare.ts b/miniprogram/mathPages/compare/compare.ts
new file mode 100644
index 0000000..293327f
--- /dev/null
+++ b/miniprogram/mathPages/compare/compare.ts
@@ -0,0 +1,132 @@
+import CompareDraw from '../service/compareDraw';
+import {
+ getMathPageCommonMethods,
+ CanvasDataState,
+} from '../common/mathPageMixin';
+
+// 获取公共方法
+const commonMethods = getMathPageCommonMethods({
+ pagePath: 'compare/compare',
+});
+
+Page({
+ canvas: null as Canvas | null,
+ ctx: null as RenderingContext | null,
+ boxHeight: 0,
+ boxWidth: 0,
+ drawService: null as CompareDraw | null,
+ compareData: null as {
+ problems: Array<{
+ leftCount: number;
+ rightCount: number;
+ leftImageIndex: number;
+ rightImageIndex: number;
+ imageType: 'fruits' | 'twelve-animals';
+ }>;
+ } | null,
+
+ data: {
+ pageTitle: '数一数,比大小',
+ subTitle: '数一数,比较数量,在⭕️中填入>、<、=',
+ functionId: '',
+ hasContent: false,
+ showShareDialog: false,
+ } as CanvasDataState,
+
+ onLoad(options: { id?: string }) {
+ const functionId = options.id || 'compare';
+ this.initPageInfo(functionId, '数一数,比大小');
+ },
+
+ onReady() {
+ this.initCanvas({
+ createDrawService: (canvas, ctx, options) => {
+ return new CompareDraw(canvas, ctx, options);
+ },
+ drawServiceOptions: {
+ subTitle: this.data.subTitle,
+ },
+ onCanvasReady: () => {
+ // Canvas 初始化完成后,生成初始数据
+ this.onRandom();
+ },
+ });
+ },
+
+ /**
+ * 绘制Canvas内容
+ */
+ async drawCanvas() {
+ if (!this.ctx || !this.drawService || !this.compareData) {
+ return;
+ }
+
+ try {
+ await this.drawService.draw(this.compareData);
+ this.setData({ hasContent: true });
+ } catch (error) {
+ console.error('绘制失败:', error);
+ this.setData({ hasContent: false });
+ }
+ },
+
+ /**
+ * 随机生成
+ */
+ onRandom() {
+ this.generateCompareData();
+ this.drawCanvas();
+ },
+
+ /**
+ * 生成比较数据
+ */
+ generateCompareData() {
+ const problems: Array<{
+ leftCount: number;
+ rightCount: number;
+ leftImageIndex: number;
+ rightImageIndex: number;
+ imageType: 'fruits' | 'twelve-animals';
+ }> = [];
+
+ // 生成6道题目
+ for (let i = 0; i < 12; i++) {
+ // 随机选择图片类型
+ const imageType: 'fruits' | 'twelve-animals' =
+ Math.random() < 0.5 ? 'fruits' : 'twelve-animals';
+
+ // 根据图片类型确定最大索引
+ const maxImageIndex = imageType === 'fruits' ? 22 : 12;
+
+ // 生成左右两边的数量(1-10)
+ const leftCount = Math.floor(Math.random() * 10) + 1;
+ const rightCount = Math.floor(Math.random() * 10) + 1;
+
+ // 随机选择图片索引
+ const leftImageIndex =
+ Math.floor(Math.random() * maxImageIndex) + 1;
+ const rightImageIndex =
+ Math.floor(Math.random() * maxImageIndex) + 1;
+
+ problems.push({
+ leftCount,
+ rightCount,
+ leftImageIndex,
+ rightImageIndex,
+ imageType,
+ });
+ }
+
+ this.compareData = { problems };
+ },
+
+ // ========== 使用公共方法 ==========
+ initCanvas: commonMethods.initCanvas,
+ exportToPrint: commonMethods.exportToPrint,
+ onShareAppMessage: commonMethods.onShareAppMessage,
+ onShareTimeline: commonMethods.onShareTimeline,
+ onCloseShareDialog: commonMethods.onCloseShareDialog,
+ onShareSuccess: commonMethods.onShareSuccess,
+ initPageInfo: commonMethods.initPageInfo,
+});
diff --git a/miniprogram/mathPages/compare/compare.wxml b/miniprogram/mathPages/compare/compare.wxml
new file mode 100644
index 0000000..5d3cbdb
--- /dev/null
+++ b/miniprogram/mathPages/compare/compare.wxml
@@ -0,0 +1,37 @@
+
+
+ 预览打印效果
+
+
+
+
+
+
+
+
+
+ 随机生成
+
+
+
+
+
+
+
diff --git a/miniprogram/mathPages/service/compareContentDraw.ts b/miniprogram/mathPages/service/compareContentDraw.ts
new file mode 100644
index 0000000..d744b25
--- /dev/null
+++ b/miniprogram/mathPages/service/compareContentDraw.ts
@@ -0,0 +1,330 @@
+import { getImage } from '../../utils/index';
+
+interface DrawCompareContentParams {
+ canvas: WechatMiniprogram.Canvas;
+ ctx: RenderingContext;
+ compareData: {
+ problems: Array<{
+ leftCount: number;
+ rightCount: number;
+ leftImageIndex: number;
+ rightImageIndex: number;
+ imageType: 'fruits' | 'twelve-animals';
+ }>;
+ };
+ canvasWidth: number;
+ startY: number;
+}
+
+/**
+ * 绘制圆角矩形(虚线边框)
+ */
+function drawRoundedRect(
+ ctx: RenderingContext,
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ radius: number,
+ isDashed: boolean = true,
+) {
+ ctx.beginPath();
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(x + width - radius, y);
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
+ ctx.lineTo(x + radius, y + height);
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
+ ctx.lineTo(x, y + radius);
+ ctx.quadraticCurveTo(x, y, x + radius, y);
+ ctx.closePath();
+
+ if (isDashed) {
+ ctx.setLineDash([6 / 3, 6 / 3]); // 约2px的虚线
+ } else {
+ ctx.setLineDash([]);
+ }
+ ctx.stroke();
+ ctx.setLineDash([]); // 重置为实线
+}
+
+/**
+ * 绘制数一数比大小内容区域
+ * 两列布局,每列左右两个虚线框,中间圆圈
+ */
+export async function drawCompareContent({
+ canvas,
+ ctx,
+ compareData,
+ canvasWidth,
+ startY,
+}: DrawCompareContentParams): Promise {
+ const { problems } = compareData;
+
+ // 布局参数
+ const leftMargin = 30;
+ const rightMargin = 30;
+ const topMargin = 20;
+ const rowSpacing = 114; // 行之间的间距
+ const columnSpacing = 40; // 两列之间的间距(可调节)
+
+ // 计算每列的宽度(减去边距和列间距)
+ const availableWidth =
+ canvasWidth - leftMargin - rightMargin - columnSpacing;
+ const columnWidth = availableWidth / 2;
+
+ // 框的尺寸
+ const boxWidth = 100;
+ const boxHeight = 98;
+ const borderRadius = 12;
+ const circleRadius = 18; // 圆圈半径
+
+ // 框与圆圈之间的间距
+ const boxToCircleSpacing = 12;
+
+ // 计算左侧列和右侧列的起始X位置
+ const leftColumnStartX = leftMargin;
+ const rightColumnStartX = leftMargin + columnWidth + columnSpacing;
+
+ // 计算每列内部的布局(左框 | 圆圈 | 右框,居中)
+ const totalWidthPerColumn =
+ boxWidth +
+ boxToCircleSpacing +
+ circleRadius * 2 +
+ boxToCircleSpacing +
+ boxWidth;
+ const columnPadding = (columnWidth - totalWidthPerColumn) / 2;
+
+ let currentY = startY + topMargin;
+
+ // 固定绘制6行
+ const totalRows = 6;
+ for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) {
+ const rowY = currentY + rowIndex * rowSpacing;
+
+ // 绘制左侧列
+ const leftProblemIndex = rowIndex * 2;
+ if (leftProblemIndex < problems.length) {
+ await drawCompareProblem(
+ ctx,
+ canvas,
+ problems[leftProblemIndex],
+ leftColumnStartX + columnPadding,
+ rowY,
+ boxWidth,
+ boxHeight,
+ borderRadius,
+ circleRadius,
+ boxToCircleSpacing,
+ );
+ }
+
+ // 绘制右侧列
+ const rightProblemIndex = rowIndex * 2 + 1;
+ if (rightProblemIndex < problems.length) {
+ await drawCompareProblem(
+ ctx,
+ canvas,
+ problems[rightProblemIndex],
+ rightColumnStartX + columnPadding,
+ rowY,
+ boxWidth,
+ boxHeight,
+ borderRadius,
+ circleRadius,
+ boxToCircleSpacing,
+ );
+ }
+ }
+}
+
+/**
+ * 绘制一个比较问题(左框 | 圆圈 | 右框)
+ */
+async function drawCompareProblem(
+ ctx: RenderingContext,
+ canvas: WechatMiniprogram.Canvas,
+ problem: {
+ leftCount: number;
+ rightCount: number;
+ leftImageIndex: number;
+ rightImageIndex: number;
+ imageType: 'fruits' | 'twelve-animals';
+ },
+ startX: number,
+ startY: number,
+ boxWidth: number,
+ boxHeight: number,
+ borderRadius: number,
+ circleRadius: number,
+ boxToCircleSpacing: number,
+) {
+ const {
+ leftCount,
+ rightCount,
+ leftImageIndex,
+ rightImageIndex,
+ imageType,
+ } = problem;
+
+ // 图片配置
+ const imageConfig = {
+ 'twelve-animals': {
+ folder: 'twelve-animals',
+ maxIndex: 12,
+ },
+ fruits: {
+ folder: 'fruits',
+ maxIndex: 22,
+ },
+ };
+
+ const config = imageConfig[imageType] || imageConfig['twelve-animals'];
+
+ // 计算左框位置
+ const leftBoxX = startX;
+ const leftBoxY = startY;
+
+ // 计算圆圈位置
+ const circleX = leftBoxX + boxWidth + boxToCircleSpacing + circleRadius;
+ const circleY = startY + boxHeight / 2;
+
+ // 计算右框位置
+ const rightBoxX = circleX + circleRadius + boxToCircleSpacing;
+ const rightBoxY = startY;
+
+ // 绘制左框
+ ctx.strokeStyle = '#999';
+ ctx.lineWidth = 1;
+ drawRoundedRect(
+ ctx,
+ leftBoxX,
+ leftBoxY,
+ boxWidth,
+ boxHeight,
+ borderRadius,
+ true,
+ );
+
+ // 绘制左框内的图片
+ await drawImagesInBox(
+ ctx,
+ canvas,
+ leftCount,
+ leftImageIndex,
+ config.folder,
+ leftBoxX,
+ leftBoxY,
+ boxWidth,
+ boxHeight,
+ );
+
+ // 绘制实心圆圈
+ ctx.fillStyle = '#fff';
+ ctx.strokeStyle = '#333';
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+ ctx.arc(circleX, circleY, circleRadius, 0, Math.PI * 2);
+ ctx.fill(); // 先填充白色
+ ctx.stroke(); // 再绘制边框
+
+ // 绘制右框
+ ctx.strokeStyle = '#999';
+ ctx.lineWidth = 1;
+ drawRoundedRect(
+ ctx,
+ rightBoxX,
+ rightBoxY,
+ boxWidth,
+ boxHeight,
+ borderRadius,
+ true,
+ );
+
+ // 绘制右框内的图片
+ await drawImagesInBox(
+ ctx,
+ canvas,
+ rightCount,
+ rightImageIndex,
+ config.folder,
+ rightBoxX,
+ rightBoxY,
+ boxWidth,
+ boxHeight,
+ );
+}
+
+/**
+ * 在框内绘制多张图片
+ */
+async function drawImagesInBox(
+ ctx: RenderingContext,
+ canvas: WechatMiniprogram.Canvas,
+ count: number,
+ imageIndex: number,
+ folder: string,
+ boxX: number,
+ boxY: number,
+ boxWidth: number,
+ boxHeight: number,
+) {
+ const padding = 8; // 框内边距
+ const availableWidth = boxWidth - padding * 2;
+ const availableHeight = boxHeight - padding * 2;
+
+ // 根据数量确定每行的图片数和图片大小
+ let imagesPerRow: number;
+ let imageSize: number;
+
+ if (count <= 4) {
+ imagesPerRow = count <= 2 ? count : 2;
+ imageSize =
+ Math.min(availableWidth / imagesPerRow, availableHeight / 2) - 4;
+ } else if (count <= 6) {
+ imagesPerRow = 3;
+ imageSize = Math.min(availableWidth / 3, availableHeight / 2) - 4;
+ } else {
+ imagesPerRow = 3;
+ imageSize = Math.min(availableWidth / 3, availableHeight / 3) - 4;
+ }
+
+ const rows = Math.ceil(count / imagesPerRow);
+ const imageSpacing =
+ (availableWidth - imageSize * imagesPerRow) / (imagesPerRow + 1);
+ const rowSpacing =
+ rows > 1 ? (availableHeight - imageSize * rows) / (rows + 1) : 0;
+
+ // 加载图片
+ let boxImage: any = null;
+ try {
+ const imagePath = `/mathPages/assets/${folder}/${imageIndex}.png`;
+ boxImage = await getImage(canvas, imagePath);
+ } catch (error) {
+ console.error(`加载${folder}/${imageIndex}图片失败:`, error);
+ return;
+ }
+
+ // 绘制图片
+ for (let i = 0; i < count; i++) {
+ const row = Math.floor(i / imagesPerRow);
+ const col = i % imagesPerRow;
+
+ const imageX =
+ boxX + padding + imageSpacing + col * (imageSize + imageSpacing);
+ const imageY =
+ boxY +
+ padding +
+ (rows > 1 ? rowSpacing : (availableHeight - imageSize) / 2) +
+ row * (imageSize + (rows > 1 ? rowSpacing : 0));
+
+ if (boxImage) {
+ // 计算图片高度(等比例缩放)
+ // @ts-ignore - 微信小程序图片对象有 width 和 height 属性
+ const scaledHeight = (boxImage.height / boxImage.width) * imageSize;
+
+ ctx.drawImage(boxImage, imageX, imageY, imageSize, scaledHeight);
+ }
+ }
+}
diff --git a/miniprogram/mathPages/service/compareDraw.ts b/miniprogram/mathPages/service/compareDraw.ts
new file mode 100644
index 0000000..db2e5ef
--- /dev/null
+++ b/miniprogram/mathPages/service/compareDraw.ts
@@ -0,0 +1,65 @@
+import { BaseMathDrawService } from './baseMathDraw';
+import { drawCompareContent } from './compareContentDraw';
+
+/**
+ * 数一数比大小绘制服务
+ * 组合使用基础绘制服务和内容区域绘制服务
+ */
+class CompareDraw extends BaseMathDrawService {
+ compareData: {
+ problems: Array<{
+ leftCount: number;
+ rightCount: number;
+ leftImageIndex: number;
+ rightImageIndex: number;
+ imageType: 'fruits' | 'twelve-animals';
+ }>;
+ } | null;
+
+ constructor(
+ canvas: Canvas,
+ ctx: RenderingContext,
+ options?: Record,
+ ) {
+ super(canvas, ctx, options);
+ this.compareData = null;
+ }
+
+ async draw(compareData: {
+ problems: Array<{
+ leftCount: number;
+ rightCount: number;
+ leftImageIndex: number;
+ rightImageIndex: number;
+ imageType: 'fruits' | 'twelve-animals';
+ }>;
+ }) {
+ if (!compareData || !compareData.problems) {
+ return;
+ }
+
+ this.setPrintConfig();
+ this.compareData = compareData;
+ this.clear();
+ this.setPaper();
+
+ // 绘制Header
+ if (this.headerType !== 'minimal') {
+ await this.drawHeader();
+ } else {
+ this.drawMiniHeader();
+ }
+
+ // 绘制内容区域(数一数比大小)
+ this.drawDivider();
+ await drawCompareContent({
+ canvas: this.canvas,
+ ctx: this.ctx,
+ compareData: this.compareData,
+ canvasWidth: this.canvasWidth,
+ startY: this.currentY,
+ });
+ }
+}
+
+export default CompareDraw;
diff --git a/project.private.config.json b/project.private.config.json
index 5ae1791..9734f26 100644
--- a/project.private.config.json
+++ b/project.private.config.json
@@ -23,12 +23,19 @@
"condition": {
"miniprogram": {
"list": [
+ {
+ "name": "mathPages/compare/compare",
+ "pathName": "mathPages/compare/compare",
+ "query": "id=compare",
+ "scene": null,
+ "launchMode": "default"
+ },
{
"name": "mathPages/missingNumber/missingNumber",
"pathName": "mathPages/missingNumber/missingNumber",
"query": "id=missing-number",
- "scene": null,
- "launchMode": "default"
+ "launchMode": "default",
+ "scene": null
},
{
"name": "mathPages/addition/addition",